You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.
| Online: | 9 Members of 4089 |
| Forum: Seam Users |
12. Aug 2008, 01:38 CET | Link |
I have a page that is running appallingly slow.
I have provided code below.
Question 1: Can anyone spot the problem?
Question 2: What tools are recommended for analysing the problem.
This is running on Prod just as slow (if not slower) than Dev.
There are a lot of set/gets as @Out with boolean doesn't work so you have to add routines (not that I even found why in the manual). Another little quirk of Seam I guess. Have code with @Out for most private values and then put/get for booleans...
Given this:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:s="http://jboss.com/products/seam/taglib" xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="/WEB-INF/pages/templateCleanWithConversations.xhtml">
<!-- content -->
<ui:define name="template_title">
<h:outputText value="#{messages.user_list_page}" />
</ui:define>
<ui:define name="template_content">
<div id="reg_form">
<h:form class="general_form">
<fieldset class="general_form_fieldset">
<legend class="general_form_legend">
<h:outputText value="#{messages.user_list_fieldset}" />
</legend>
<s:validateAll>
<!-- CONTROLS -->
<div class="left">
<span class="padded_right_left">
<h:outputLabel for="show_avatar" value="Show Avatar"/>
<h:selectBooleanCheckbox id="show_avatar" value="#{userListController.showingAvatar}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_email" value="Show Email"/>
<h:selectBooleanCheckbox id="show_email" value="#{userListController.showingEmail}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_phone" value="Show Phone"/>
<h:selectBooleanCheckbox id="show_phone" value="#{userListController.showingPhone}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_dressage" value="Show Dressage"/>
<h:selectBooleanCheckbox id="show_dressage" value="#{userListController.showingDressage}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_sj" value="Show SJ"/>
<h:selectBooleanCheckbox id="show_sj" value="#{userListController.showingSJ}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_showing" value="Show Showing"/>
<h:selectBooleanCheckbox id="show_showing" value="#{userListController.showingShowing}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_events" value="Show Events"/>
<h:selectBooleanCheckbox id="show_events" value="#{userListController.showingEvents}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
<span class="padded_right_left">
<h:outputLabel for="show_dates" value="Show Dates"/>
<h:selectBooleanCheckbox id="show_dates" value="#{userListController.showingDates}">
<a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
</h:selectBooleanCheckbox>
</span>
</div>
<div>
<br/>
<br/>
</div>
<!-- TABLE -->
<rich:dataTable id="users_list" value="#{userList}" var="eachUser"
columnClasses="center" rows="20" width="100%">
<f:facet name="header">
<h:outputText value="Users" />
</f:facet>
<rich:column rendered="#{userListController.isShowingAvatar() eq true}">
<f:facet name="header">
<h:outputText value="Avatar" />
</f:facet>
<s:graphicImage width="75px" rendered="#{eachUser.avatar ne null}" value="#{eachUser.avatar.image}" />
</rich:column>
<rich:column sortBy="#{eachUser.surname}">
<f:facet name="header">
<h:outputText value="Surname" />
</f:facet>
<h:outputText value="#{eachUser.surname}" />
</rich:column>
<rich:column sortBy="#{eachUser.firstname}">
<f:facet name="header">
<h:outputText value="Firstname" />
</f:facet>
<h:outputText value="#{eachUser.firstname}" />
</rich:column>
<rich:column rendered="#{userListController.isShowingEmail() eq true}">
<f:facet name="header">
<h:outputText value="Email" />
</f:facet>
<h:outputText value="#{eachUser.email}" />
</rich:column>
<rich:column sortBy="#{eachUser.homePhone}" rendered="#{userListController.isShowingPhone() eq true}">
<f:facet name="header">
<h:outputText value="Home Phone" />
</f:facet>
<h:outputText value="#{eachUser.homePhone}" />
</rich:column>
<rich:column sortBy="#{eachUser.mobilePhone}" rendered="#{userListController.isShowingPhone() eq true}">
<f:facet name="header">
<h:outputText value="Mobile Phone" />
</f:facet>
<h:outputText value="#{eachUser.mobilePhone}" />
</rich:column>
<rich:column sortBy="#{eachUser.phoneContactOk}" rendered="#{userListController.isShowingPhone() eq true}">
<f:facet name="header">
<h:outputText value="Phone Contact Ok" />
</f:facet>
<s:graphicImage rendered="#{eachUser.phoneContactOk}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.phoneContactOk}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.dressageJudge}" rendered="#{userListController.isShowingDressage() eq true}">
<f:facet name="header">
<h:outputText value="Dressage Judge" />
</f:facet>
<s:graphicImage rendered="#{eachUser.dressageJudge}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.dressageJudge}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.dressageWriter}" rendered="#{userListController.isShowingDressage() eq true}">
<f:facet name="header">
<h:outputText value="Dressage Writer" />
</f:facet>
<s:graphicImage rendered="#{eachUser.dressageWriter}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.dressageWriter}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.dressageHelper}" rendered="#{userListController.isShowingDressage() eq true}">
<f:facet name="header">
<h:outputText value="Dressage Helper" />
</f:facet>
<s:graphicImage rendered="#{eachUser.dressageHelper}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.dressageHelper}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.sjJudge}" rendered="#{userListController.isShowingSJ() eq true}">
<f:facet name="header">
<h:outputText value="SJ Judge" />
</f:facet>
<s:graphicImage rendered="#{eachUser.sjJudge}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.sjJudge}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.sjHelper}" rendered="#{userListController.isShowingSJ() eq true}">
<f:facet name="header">
<h:outputText value="SJ Helper" />
</f:facet>
<s:graphicImage rendered="#{eachUser.sjHelper}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.sjHelper}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.showingJudge}" rendered="#{userListController.isShowingShowing() eq true}">
<f:facet name="header">
<h:outputText value="Showing Judge" />
</f:facet>
<s:graphicImage rendered="#{eachUser.showingJudge}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.showingJudge}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.showingHelper}" rendered="#{userListController.isShowingShowing() eq true}">
<f:facet name="header">
<h:outputText value="Showing Helper" />
</f:facet>
<s:graphicImage rendered="#{eachUser.showingHelper}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.showingHelper}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.eventsHelper}" rendered="#{userListController.isShowingEvents() eq true}">
<f:facet name="header">
<h:outputText value="Events Helper" />
</f:facet>
<s:graphicImage rendered="#{eachUser.eventsHelper}" url="/images/tick.gif" />
<s:graphicImage rendered="#{not eachUser.eventsHelper}" url="/images/cross.gif" />
</rich:column>
<rich:column sortBy="#{eachUser.dates.creationDate}" rendered="#{userListController.isShowingDates() eq true}">
<f:facet name="header">
<h:outputText value="Creation Date" />
</f:facet>
<h:outputText value="#{eachUser.dates.creationDate}">
<s:convertDateTime pattern="dd/MMM/yyyy"/>
</h:outputText>
</rich:column>
</rich:dataTable>
<rich:datascroller for="users_list" maxPages="30"/>
</s:validateAll>
<div class="general_form_buttons">
<h:commandButton class="general_form_button" action="#{userListController.cancel}"
value="#{messages.general_button_cancel}" immediate="true" type="submit">
<s:conversationPropagation type="end" />
</h:commandButton>
</div>
</fieldset>
</h:form>
</div>
<a4j:status>
<f:facet name="start">
<h:graphicImage styleClass="page_foot_image" value="/images/ajax/animated_horse.gif" height="50" />
</f:facet>
</a4j:status>
</ui:define>
<!-- content -->
</ui:composition>
Running over this code:
package nz.co.selwynequestriancentre.action.user;
import java.io.Serializable;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import nz.co.selwynequestriancentre.model.entity.User;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.Conversational;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.log.Log;
import java.util.List;
/**
* @author Tony Herstell
* @version $Revision: 1.2 $ $Date: 2008-08-11 07:09:14 $
*/
@SuppressWarnings("serial")
@Stateful
@Name("userListController")
@Conversational
@Scope(value=ScopeType.CONVERSATION)
public class UserListControllerImpl implements UserListController, Serializable {
/**
* Inject and leverage the Seam Logger.
*/
@Logger
private Log log;
/**
* Inject the EJB3 Persistence context in EXTENDED mode so will remain
* "current" over multiple client/server round trips.
*/
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
@DataModel
private List<User> userList;
private boolean showingAvatar = false;
private boolean showingEmail = false;
private boolean showingPhone = true;
private boolean showingDressage = true;
private boolean showingSJ = false;
private boolean showingShowing = false;
private boolean showingEvents = false;
private boolean showingDates = false;
@Factory("userList")
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void findUsers()
{
userList = em.createQuery("from User user order by user.surname asc").getResultList();
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Begin
public String enter() {
return "userListController";
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingAvatar() {
return showingAvatar;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingAvatar(boolean showingAvatar) {
this.showingAvatar = showingAvatar;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingEmail() {
return showingEmail;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingEmail(boolean showingEmail) {
this.showingEmail = showingEmail;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingPhone() {
return showingPhone;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingPhone(boolean showingPhone) {
this.showingPhone = showingPhone;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingDressage() {
return showingDressage;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingDressage(boolean showingDressage) {
this.showingDressage = showingDressage;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingSJ() {
return showingSJ;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingSJ(boolean showingSJ) {
this.showingSJ = showingSJ;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingShowing() {
return showingShowing;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingShowing(boolean showingShowing) {
this.showingShowing = showingShowing;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingEvents() {
return showingEvents;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingEvents(boolean showingEvents) {
this.showingEvents = showingEvents;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean isShowingDates() {
return showingDates;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void setShowingDates(boolean showingDates) {
this.showingDates = showingDates;
}
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@End
public String cancel() {
log.info(">cancel");
log.info("<cancel");
return "home";
}
@Remove
@Destroy
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void destroy() {
log.info("> destory");
log.info("< destory");
}
}
9 Replies: | |||||
|---|---|---|---|---|---|
The @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) is throwing flags in my mind since Seam automatically provides a global transaction with your lifecycle, and you are telling it that you aren't supporting transactions at all. What happens if you disable those for now, and let the Seam's own transaction manager handle that for you?
|
|||||
Ok I will try this tonight. This construct was in Micheal Yauns book and solved other problems of transations running over too may part of the transaction. I will re-read the pages...
I have other pages/code combinations that do a huge amount more stuff than this to get a page on the screen; ill add :) But I being poor have only my dreams. I have spread my dreams under your feet. Tread softly because you tread on my dreams... |
|||||
Interesting, I will check that book out. I could be wrong, I'll take a look at more of your code.
|
|||||
9.3. Atomic Conversation (Web Transaction) The Seam/EJB3 transaction is tied to a Java thread. It can manage operations only within a method call stack. It flushes all updates to the database at the end of each call stack. That behavior has two problems in a web conversation: First, it can be inefficient to make multiple round-trips to the database in a conversation when all the updates are closely related. Second, when a certain operation in the conversation fails, you must manually roll back the already-committed transactions in the conversation to restore the database to its state prior to the conversation. A much better way is for the application to hold all database updates in memory and flush them all at once at the end of the conversation. If an error occurs in any step in the conversation, the conversation just fails without affecting the database. From the database point of view, the entire conversation either succeeds or fails—hence, atomic conversation. The atomic conversation behavior is also known as a web transaction. Seam makes it easy to implement atomic conversations. In the following sections, we discuss two approaches. The first is for Seam POJOs, and the second is for EJB3 session beans. SNIP SNIP
9.3.2. One Transaction per Conversation
Another alternative is to disable the transaction manager on all methods except for the @End
method. Because this approach requires method-level transaction demarcation, it can be used
only on EJB3 session bean components with an EJB3-managed EntityManager (i.e., an
EntityManager injected via @PersistenceContext).
9.3.2. One Transaction per Conversation
133
This method is not as outrageous as it might sound. The transaction manager is not flushing
anything to the database before the end of the conversation, so there is nothing to
public class HotelBookingAction
implements HotelBooking, Serializable {
// ... ...
@PersistenceContext (type=EXTENDED)
private EntityManager em;
@Begin(join=true)
@TransactionAttribute(
TransactionAttributeType.NOT_SUPPORTED)
public String find() {
// ... ...
}
@TransactionAttribute(
TransactionAttributeType.NOT_SUPPORTED)
public String bookHotel()
throws InventoryException {
// ... ...
hotel.reduceInventory ();
}
@End
@TransactionAttribute(
TransactionAttributeType.REQUIRED)
public String confirm() {
// ... ...
em.persist (booking);
}
}
Because this approach uses only EJB3 standard annotations, it works in all EJB3-compliant application servers. But I being poor have only my dreams. I have spread my dreams under your feet. Tread softly because you tread on my dreams... |
|||||
I can add something from personal experience, rich:dataTable is twice as slow as h:dataTable, so test the performance switching back to h:dataTable. I experienced this with Richfaces 3.2.1.GA, not sure if it holds true for other versions.
Also, check out http://facestrace.sourceforge.net/ which will show the amount of time spent in each phase. It will be interesting to see how much time is spend in INVOKE versus RENDER, because if all the time is in INVOKE, then you have slowdowns in your Bean, if all time is in RENDER, then it is with the EL/Components. |
|||||
Twice as fast is interesting, but this is SOOOO long its virtually unusable.. and its only drawing a list of 20 users on the screen. I will try this tonight and see wht it comes up with. Cheers Samuel. But I being poor have only my dreams. I have spread my dreams under your feet. Tread softly because you tread on my dreams... |
|||||
my list had about 10 items in it, rich:dataTable took 7 seconds, h:dataTable 3-4, this was an EL heavy table is why those numbers are so large to begin with.
|
|||||
Humm.. I can access the site from work. 9 seconds for intial list presentation. 18 seconds if I click on a table heading to sort by that heading. I will try this tonight and see what it comes up with. But I being poor have only my dreams. I have spread my dreams under your feet. Tread softly because you tread on my dreams... |
|||||
I use rich:dataTable heavily and haven't noticed any problems with it. Mileage varies I guess. I don't use the filtering or sorting. Maybe they have more of an impact? I haven't tried the FacesTrace but one thing that I've found invaluable is the Bean-Timing Interceptor that Tobias Hill posted here a while ago. I use it to ascertain whether I'm spending too much time in any particular method especially those used for rendering. Cheers, Damian. |
But I being poor have only my dreams. I have spread my dreams under your feet. Tread softly because you tread on my dreams...