??xml version="1.0" encoding="utf-8" standalone="yes"?>
Configuring the conversation-timeout period can be accomplished through the following in your components.xml:
<core:manager conversation-timeout="900000" />
At first glance most developers relate conversation-timeout to session timeout, where any conversation will simply timeout after the configured conversation timeout period. As you will quickly notice during testing, this is not the case. To execute a quick experiment, try the following configuration in the components.xml of the seam-booking example:
<core:manager conversation-timeout="60000" />
Because the conversation-timeout is set in milliseconds, the above configuration sets the conversation-timeout to 1 minute. Now in the web.xml set the session timeout to 5 minutes:
<session-config>
<session-timeout>5</session-timeout>
</session-config>
Now in the destroy method of the HotelBookingAction add the following line:
...
@Destroy @Remove
public void destroy() {
log.info("Destroying HotelBookingAction...");
}
...
This will log our message when the conversation ends and the HotelBookingAction is destroyed. Now deploy the seam-booking example to your local JBoss instance and start up a conversation. This can be accomplished by logging in, navigating to the hotels listing, and selecting a hotel for booking. At this point, wait for the 1 minute period… nothing happens. Now wait for another 4 minutes, the message is displayed. The conversation timed out along with the session.
Foreground vs. Background Conversations
So why didn’t our conversation timeout as configured? This is because the conversation-timeout only affects background conversations. The foreground conversation will only timeout when the session times out. Foreground, background, what? The foreground conversation is the conversation that the user last interacted with. A background conversation is any other conversation in the user’s session. Thus, in our previous scenario the foreground conversation timed out with the session as expected.
Now lets try another approach. Perform the same steps as before to proceed to the booking screen. Now open a new window and perform the same steps. We now have a foreground conversation and a background conversation in progress. Again, wait 1 minute. Nothing happened. If you wait an additional 4 minutes, both conversations will timeout. So what is going on here, I thought we had a background conversation? We did, Seam simply checks the conversation timeout on each request. Thus, if I interact with the foreground conversation after 1 minute, the background conversation times out. Try it, perform the same steps, wait 1 minute and then click on the window of the foreground conversation and you will see the log message.
This is very desirable behavior. Essentially when a user leaves his or her desk for a period of time and comes back, if the session is still active it would be desirable to maintain the state the user was previously in. This is the foreground conversation state. All other background conversation state is left to timeout after the configured conversation-timeout period which reduces overall memory consumption. This enables a developer to think less about memory usage and cleaning up state and more about developing business logic. That’s why we’re here right?
Letting the user choose
So you may be asking at this point why the conversation-timeout doesn’t use polling. As we said, you must interact with the foreground conversation to cause the background conversations to timeout after the conversation-timeout period. Imagine that the user had many windows open and leaves his or her desk. Based on which window the user clicks on when they return, that becomes the foreground conversation timing out any other background conversations. This gives the user a chance to resume whichever conversation he or she chooses, not the one the developer chooses.
假如文本中包含了大量?"<" ?"&" 字符 - 像~程代码中经常出现的情况一?- 那么q个 XML 元素可以被定义Z?CDATA 部分?/p>
CDATA 区段开始于 "<![CDATA["Q结束于 "]]>"Q?/p>
<script type="text/javascript">
<![CDATA[
function compare(a,b)
{
if (a < b)
{alert("a于b");}
else if (a>b)
{alert("a大于b");}
else
{alert("a{于b");}
}
]]>
</script>
在上面的例子中,?CDATA 区段中的所有东襉K会被解析器忽略?/p>
关于 CDATA 区段的注?
CDATA 区段不能包含字符?"]]>"Q所以,CDATA 区段的嵌套是不被允许的?/p>
同时也需要确保在 "]]>" 字符串中没有I格或折行?/p>
Z么要使用CDATA:
XHTML的第二个改变是用CDATADcXML中的CDATAD는于声明不应被解析为标{文本QXHTML也是如此Q,q样可以用特D字W,? 于Q?lt;Q、大于(>Q、和P&Q和双引P"Q,而不必用它们的字符实体。考虑下面的代码:
<script type="text/javascript">
function compare(a,b)
{
if (a < b)
{alert("a于b");}
else if (a>b)
{alert("a大于b");}
else
{alert("a{于b");}
}
</script>
q个函数相当单,它比较数字a和bQ然后显C消息说明它们的关系。但是,在XHTML中,q段代码是无效的Q因为它使用了三个特D符P卛_于? 大于和双引号。要修正q个问题Q必d别用q三个字W的XML实体<?amp;gt;?amp;quot;替换它们Q?/p>
<script type="text/javascript">
function compare(a,b)
{
if (a <b)
{alert("a于b");}
else if (a>b)
{alert("a大于b");}
else
{alert("a{于b");}
}
</script>
q段代码存在两个问题。首先,开发者不习惯用XML实体~写代码。这使代码很难读懂。其ơ,在JavaScript中,q种代码实际上将视ؓ有语? 错,因ؓ解释E序不知道XML实体的意思。用CDATAD即可以以常规Ş式(xȝ语法Q编写JavaScript代码。正式加入CDATAD늚Ҏ? 下:
<script type="text/javascript">
<![CDATA[
function compare(a,b)
{
if (a < b)
{alert("a于b");}
else if (a>b)
{alert("a大于b");}
else
{alert("a{于b");}
}
]]>
</script>
虽然q是正式方式Q但q要CQ大多数览器都不完全支持XHTMLQ这带来主要问题,卌在JavaScript中是个语法错误,因ؓ大多数浏览器q不认识CDATADc?/p>
<script type="text/javascript">
//<![CDATA[
function compare(a,b)
{
if (a < b)
{alert("a于b");}
else if (a>b)
{alert("a大于b");}
else
{alert("a{于b");}
}
//]]>
</script>
当前使用的解x案模仿了“Ҏ览器隐?#8221;代码的方法。用单行的JavaScript注释"http://"Q可在不影响代码语法的情况下嵌入CDATAD:
现在Q这D代码在不支持XHTML的浏览器中也可运行?/p>
但是Qؓ避免CDATA的问题,最好还是用外部文g引入JavaScript代码?/p>
RichFaces 3.1.3 is bundled with the same ones as Prototype 1.6.0, Scriptaculous 1.8.0
...
<context-param>
<param-name>org.richfaces.ExcludeScripts</param-name>
<param-value>Prototype,Scriptaculous</param-value>
</context-param>
...
当一个操作成功时QHome对象可以自动地显CFaces信息Q我们可以再一ơ通过配置来定制信息?
<factory name="person"
value="#{personHome.instance}"/>
<framework:entity-home name="personHome"
entity-class="eg.Person"
new-instance="#{newPerson}">
<framework:created-message>New person #{person.firstName} #{person.lastName} created</framework:created-message>
<framework:deleted-message>Person #{person.firstName} #{person.lastName} deleted</framework:deleted-message>
<framework:updated-message>Person #{person.firstName} #{person.lastName} updated</framework:updated-message>
</framework:entity-home>
<component name="newPerson"
class="eg.Person">
<property name="nationality">#{country}</property>
</component>
或者扩展:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@In Country country;
@Factory("person")
public Person initPerson() { return getInstance(); }
protected Person createInstance() {
return new Person(country);
}
protected String getCreatedMessage() { return "New person #{person.firstName} #{person.lastName} created"; }
protected String getUpdatedMessage() { return "Person #{person.firstName} #{person.lastName} updated"; }
protected String getDeletedMessage() { return "Person #{person.firstName} #{person.lastName} deleted"; }
}
但是指定信息最好的Ҏ是把信息|于Seam所知的resource bundle中(在默认情况下Q这个bundle叫做 messages Q?
Person_created=New person #{person.firstName} #{person.lastName} created
Person_deleted=Person #{person.firstName} #{person.lastName} deleted
Person_updated=Person #{person.firstName} #{person.lastName} updated
q样方便q行国际化,从表现层的角度考虑也保持了代码和配|的整洁?
最后一步是使用 <s:validateAll> ?<s:decorate> 向页面中d验证功能Q我会把q个留给你们自己d现?
<f:view contentType="text/html" />
问题解决?/pre>
]]>
一?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">修改数据库的q接方式
1、采用数据源的Ş?br />
?nbsp;tomcat 中配|数据源方式如下:
在生成的目同目录中找此文Ӟ context.xml
Servers/Tomcat 。。?/font>/context.xml 作如下配|:
在标{?nbsp;<Context> 中间加入:
<Resource
name="jdbc/demo" auth="Container" type="javax.sql.DataSource"
maxActive="30" maxIdle="5" maxWait="10000" username="root"
password="admin" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8">
</Resource>
2?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">使用非数据源的Ş?br />
在文Ӟpersistence.xml 中添?/font>hibernate 的连接数据库的方式,如下Q?br />
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="admin"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8"/>
二?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">修改 JPA 配置文g persistence.xml
1?/span>如果数据的连接方式采用数据源的Ş式,则只需修改里面数据源的名称Q如Q?br /> <jta-data-source>java:comp/env/jdbc/demo</jta-data-source>
2、如果采?font face="Times New Roman">hibernate
的连接方式,则把此行删了?/font> 3?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">删除里面的此行配|:
<property name="hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
4、修改事务的{略方式?RESOURCE_LOCAL Q?nbsp;
<persistence-unit name="demo" transaction-type="RESOURCE_LOCAL">
三?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">配置 Seam 的事务管理器
?nbsp;component.xml 文g加入Q?br />
<transaction:entity-transaction entity-manager="#{entityManager}"/>
默认生成的此文g是不含有 transaction 的命名空_需要手动在此文件加入它的命名空_
xmlns:transaction=http://jboss.com/products/seam/transaction
http://jboss.com/products/seam/transaction
http://jboss.com/products/seam/transaction-2.1.xsd
四?span style="font-weight: normal; font-size: 10.5pt; font-family: '宋体';">为此目加入必要的依赖包 。。。。。?/span>
adviseNodeOpened | false | false |
javax.el.MethodExpression
(signature must match java.lang.Boolean adviseNodeOpened(org.richfaces.component.UITree) )
|
MethodBinding pointing at a method accepting an org.richfaces.component.UITree with return of java.lang.Boolean type. If returned value is: java.lang.Boolean. TRUE, a particular treeNode is expanded; java.lang.Boolean.FALSE, a particular treeNode is collapsed; null, a particular treeNode saves the current state |
adviseNodeSelected | false | false |
javax.el.MethodExpression
(signature must match java.lang.Boolean adviseNodeSelected(org.richfaces.component.UITree) )
|
MethodBinding pointing at a method accepting an org.richfaces.component.UITree with return of java.lang.Boolean type. If returned value is: java.lang.Boolean. TRUE, a particular treeNode is selected; java.lang.Boolean.FALSE, a particular treeNode is unselected; null, a particular treeNode saves the current state |
Conversational components don't allow real concurrent access therefore Seam queues each request to process them serially. This allows each request to be executed in a deterministic fashion. However, a simple queue isn't that great ?firstly, if a method is, for some reason, taking a very long time to complete, running it over and over again whenever the client generates a request is bad idea (potential for Denial of Service attacks), and, secondly, AJAX is often to used to provide a quick status update to the user, so continuing to run the action after a long time isn't useful.
Therefore, when you are working inside a long running conversation, Seam queues the action event for a period of time (the concurrent request timeout); if it can't process the event in time, it creates a temporary conversation and prints out a message to the user to let them know what's going on. It's therefore very important not to flood the server with AJAX events!
We can set a sensible default for the concurrent request timeout (in ms) in components.xml:
<core:manager concurrent-request-timeout="500" />
We can also fine tune the concurrent request timeout on a page-by-page basis:
<page view-id="/book.xhtml"
conversation-required="true"
login-required="true"
concurrent-request-timeout="2000" />
So far we've discussed AJAX requests which appear serial to the user - the client tells the server that an event has occur, and then rerenders part of the page based on the result. This approach is great when the AJAX request is lightweight (the methods called are simple e.g. calculating the sum of a column of numbers). But what if we need to do a complex computation thats going to take a minute?
For heavy computation we should use a poll based approach ?the client sends an AJAX request to the server, which causes action to be executed asynchronously on the server (the response to the client is immediate) and the client then polls the server for updates. This is good approach when you have a long-running action for which it is important that every action executes (you don't want some to timeout).
}
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" logLevel="trace">
<http-error error-code="503" />
</exception>
Alternatively you could redirect to an error page:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" logLevel="trace">
<end-conversation/>
<redirect view-id="/error.xhtml">
<message>The server is too busy to process your request, please try again later</message>
</redirect>
</exception>
<script type="text/javascript">
A4J.AJAX.onError = function(req,status,message) {
alert("message");
};
</script>
Transaction timeout (unit is always seconds) can be configured in various ways:
This argument is the same no matter if you are using JBossTM(<=4.0.5) or JBossJTA-Arjuna(>=4.2).
Globally: You can change this behavior globally by modifying the TransactionManagerService in /conf/jboss-service.xml (or /deploy/jta-service.xml for 4.0.3)
Version <= 4.0.5
This part is the same for either JBossTM or JBossJTA and is the same for ejb2 and ejb3
Per-method basis: Modifying the <transaction-timeout> element inside the <method> element of a session or entity bean. This is located in the META-INF/jboss.xml deployment descriptor of a session bean. When the transaction timeout is specified at the method level, it overrides the default timeout. Further information about this element can be found in jboss-x.x.x/docs/dtd/jboss_4_0.dtd. Example taken from the testsuite:
Using BMT: Calling javax.transaction.UserTransaction.setTransactionTimeout(int seconds). Please, be aware that this only applies to transactions started after this invocation on the same thread. Example:
@TransactionTimeout(1500)
在component.xml里添加:
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}"/>
</event>
<event type="org.jboss.seam.security.postAuthenticate">
<action execute="#{redirect.returnToCapturedView}"/>
</event>
In the first of this two-part article, Dan Allen discusses some common performance problems you may encounter when using JSF components, Seam components, and the EL. You'll learn about the set of best practices for eliminating them that led to an improvement of two orders of magnitude in the performance of his application.
JavaServer Faces (JSF) has a reputation for having poor performance. Some claim that this "runtime tax" is simply the cost of using a component-based abstraction layer. After focused research, I have determined that by following a handful of best practices, you can get your JSF data tables to perform almost as well as hand-crafted HTML while still being able to retain the benefits of developing with an event-driven programming model. I begin by identifying some performance problems that occur when using JSF UI components, Seam components, and the EL carelessly, and then present, through a series of four lessons, ways to eliminate these problems one-by-one until you have observed a remarkable, two orders of magnitude improvement.
All the test results reported in this article were gathered on a Lenovo R60 with 2.5GB RAM, Dual Core T2300 @ 1.66Ghz processor running Ubuntu Linux 7.10. The application was built using Seam 2.0.3.CR1, Sun JSF 1.2_04-b16-p02, and RichFaces 3.2.2.GA. It was deployed to JBoss 4.2.2.GA running on Sun JVM 1.6.0_03. The timing results are shown in six progressive phases. Each result shows the total request time and the time to render a data table with 50 records. All metrics were captured using the FireBug extension for Firefox.
Developing a data management application is just a matter of getting data up on the screen in tabular format, correct? Oh, right, and being able to filter the data. Ah, and also allowing the data to be changed. Unfortunately, once those challenges are behind us, we tend to wash our hands of the application and move on. But the principle goal of most web applications is to enable users to perform their work more efficiently than they did before we introduced our "solution." In fact, none of those fancy features you add have any value at all if you can't improve the user's productivity. That's why, before you step away, you have to make sure that you have addressed the issue of performance.
My colleagues and I recently completed the first stage of an open source data management application based on JSF, Seam, and RichFaces in which we addressed this very concern. The application, named EDAS2, was developed for a group of scientists for managing water quality data (stored in the WQX database schema). Now, you have to understand that these scientists, they like their data. Hordes of data. And they like to view it all at once. So much, in fact, that it tends to cause the browser to crash. Naturally, we needed to condition the scientists to some degree that browsers have limits. But regardless, we were going to be dealing with large data sets. Our goal was to make sure that working with those data sets was not painful.
This article documents the bottlenecks that we discovered and a set of best practices for eliminating them. But we went beyond merely removing obstacles in performance. We tuned the application to the point where paginating, sorting, and filtering the data is actually faster than any desktop application our scientists had ever used. Find that hard to believe? Read on.
The intent of the EDAS2 application is to house and analyze water-quality measurement results. The results are taken from a location, known as a monitoring location, during a given visit, known as an activity. There are various types of results, depending on what is being measured. In this article, we will be focusing on the benthic measurement result, which in layman's terms is a sampling of mud with bugs in it. That data is recorded on site and later entered into the database and analyzed using the EDAS2 interface.
There isn't anything revolutionary about the interface of the EDAS2 application. Rather, the emphasis is on efficiency. We want to provide the experience of the MS Access database-which our scientists are currently using to manage this data-in a web application.
The application has two types of views. The first is a list view, which displays a paginated table of records for the currently selected parent entity, such as monitoring location, activity, or result. You will learn shortly that what makes this interface efficient is that it offers in-place editing of each row (it also has a floating popup dialog for detailed editing of the row).
The key feature of this application is that the data rendered in each table can be modified in place. To implement this functionality, we decided against using an off-the-shelf grid editor from a JSF component library. Instead, we took the RichFaces step-wise approach by building a composite, Ajax-enabled component using the partial page rendering technology that the Ajax4jsf core provides.
Ajax4jsf provides a set of tag libraries that can tie a JSF generated event to the rerendering of one or more regions of the user-interface. Those regions are identified by their JSF client IDs. When the JSF event is sent to the server, instead of the JSF servlet returning an entire page response, it returns only fragments of the HTML. Ajax4jsf then cuts out the old branches from the live view and stitches in the replacements returned from the server. The result is that the user observes the page updating without any noticeable refresh, in real-time, so to speak. And Ajax4jsf's declarative approach let's us fine-tune this behavior.
Figure 1 provides a view of the editable data table with one of the rows in edit mode.
Figure 1. The data table demonstrating its single-line editing capabilities.
When the page is first rendered, all rows have an edit and delete button. Clicking on an edit button puts the corresponding row in edit mode, at which point the outputs in the selected row become inputs. From edit mode, the user can make changes to the visible data and either save or cancel the update, which returns the table to read-only mode.
The strategy we use to deliver this row-level editing functionality is to have two UI components in each column of a standard JSF data table: one output component (e.g., <h:outputText>) and one input component (e.g., <h:inputText>). We then use the JSF rendered attribute on each component to control which one is displayed.
To support our editable data table, we put together a small hierarchy of classes that are instantiated as Seam components to manage the query (filtering, pagination) and the editing process (select row for editing, update, delete, cancel). We chose to put the components in Seam's conversation scope. The conversation scope is a slice of the HTTP session that is managed by Seam and associated with a sequence of pages through the use of a special request token (known as the conversation id). The conversation provides the developer the convenience of using the HTTP session without the memory leakage concerns, since the conversation has a much shorter lifetime and is kept isolated from other parallel conversations in the same session.
We chose to leverage the conversation for three reasons:
Using the conversation has the added bonus of making previously viewed result pages load faster since the records are already sitting in the persistence context (i.e., the first-level cache).
Given all the benefits the conversations provide, you may be
wondering where the performance problem is. Let's take at look at where
things began to go wrong and what we did about it.
Development was going smoothly until my colleague noticed something
peculiar about the performance. Five or ten records on the page took a
reasonable amount of time to render, but when that number went up to 50
or 100 records, the performance of the page plummeted. It turns out
that the degradation was linear, but the slope was very steep. The page
with 100 records was taking over 15 seconds to render. Obviously, that
just wasn't going to fly. And so our optimization work began. Could we
find the bottleneck and how low could we go?
As we optimized, we first looked at the most basic page and
established a performance baseline. Then we added additional components
to the page and tried to identify which ones were contributing to the
major slowdown. As it turns out, in a Seam application, the first area
to place your focus is on page actions, which are methods that execute
before the page begins to render.
When a page is taking 15 seconds to render, there is likely a single
culprit that is chewing up a bulk of that time. To establish a
baseline, and to make sure I was focusing on the right problem, I first
stripped everything from the page and requested it. The response took a
couple of seconds to come back. This had me puzzled for a moment. I
soon realized that that a Seam page action was registered with the page
(i.e., view ID). A page action is a method-binding expression that is
assigned to a JSF view ID (or a group of view IDs if a wildcard is
used) in a Seam page descriptor and is evaluated by Seam just before
the view ID is rendered by the JSF view handler. Here's the expression
that was registered with the view ID of the results page.
The page action is there to eagerly fetch the results that are to be
displayed on the page. However, the query in that method was executing
in about a tenth of a second. So that wasn't the problem. After
studying the code a bit longer, I recognized that the problem was not
in the page action method, but rather the @Create method of the
component being invoked. The @Create method is a synonym to the
standard @PostConstruct method in Java EE and marks a method to be
evaluated immediately after a component is instantiated.
Inside the @Create method was a handful of queries that retrieved
more than 10,000 records of reference data. This data is used by select
menus in various forms on the page, but those forms are all being
conditionally rendered. So basically, we were charging the user a toll
to enter the page with a chance that that reference data would never be
referenced. That brings us to lesson #1.
Lesson #1: Don't make the user pay an upfront fee to view a page. Defer logic where possible.
Since the forms are rendered conditionally, and some via Ajax, the
reference data can be retrieved at the same time the forms are
activated. If you must display a form unconditionally, think about the
most efficient way to prepare the data (perhaps using a cache). It's
also preferable to use Ajax-based autocomplete rather than select menus
with a large list of options, since making this switch can drastically
reduce the speed of the initial rendering of the form. The user will
likely be more patient when working on the field with autocomplete, and
you can even keep the number of options delivered to a minimum as the
user types.
With the toll skimmed off the top, we could get back to the
performance of the elements on the page. Bringing back the page
piece-by-piece, I determined that the next big time hog was in fact the
data table. Again, I stripped out elements in the data table until I
pinned down what was causing the problem. As it turns out, it was the
expressions in the rendered attributes that I was using to hide or show
various components in the table.
In each row there are 6 "editable" columns, each containing an
output and an input component and 4 icons for controlling editing
(edit, delete, approve, cancel). In total, there are 16 uses of the
rendered attribute appearing in each row. (Initially I had a couple
columns with multiple input components, which I realized I needed to
group within a panel group [i.e., <h:panelGroup>] so that the
rendered attribute was only applied once).
As you know, logic that occurs in a single row is multiplied by the
number of rows in the table. In a table with 100 rows, there are 1600
uses of the rendered attribute! But wait, there's even more! The
rendered attribute is a nasty beast in JSF because it's often evaluated
multiple times as the UI component tree is assembled. In fact, during
the render response phase, it's resolved 3 or 4 times per component
in a data table. What that means is that for our data table, the
conditional rendering logic we are using is executed 5200 times for 100
rows! Whatever that logic is better be darn efficient or else it will
have a huge impact on performance.
As it turns out, we were not being very efficient. Let's take a look
at one of the columns of the data table in the
/BenthicMeasurementList.xhtml view template:
As you can see here, I am calling the editing() method on a Seam
component named benthicMsmntEditor to test whether the current row is
in edit mode. We can pass the iteration variable, _item, to the method
because Seam integrates with the JBoss EL, which introduces
parameterized method calls to the Unified EL. The editing() method
performs an identity check between the row data and the selected row.
Here we are only allowing one row to be in edit mode at a time, but
this logic could easily be enhanced to support editing multiple rows
simultaneously.
So where's the bottleneck? Initially, you may be inclined to point
the finger at the EL or Java reflection. I did some testing and
determined that the EL is surprisingly fast and Java reflection is
equally optimized. And if you are inclined to believe that the slowness
is caused by the parameterized method call, I'll inform you that
comparing the current item using an EL operator to the item in edit
mode retrieved using the JavaBean style accessor yields the same timing
results:
The culprit is that the editing() method resides on a Seam component
and each method call to a Seam component passes through a stack of
interceptors, unless otherwise skipped by the presence of the
@BypassInterceptors annotation at the component or method level.
When you call an intercepted method once, you would never notice the
impact of the interceptors. However, when you call the method 5200
times, the time spent in the interceptors adds up. How much of a
difference does it make and what other options do we have?
To determine the impact, I timed both the rendering of the entire
page and the rendering of the data table region, as described in the
introduction. The data table region consists of the data table and the
pagination controls and summary information for the table. A test of 4
requests on 50 rows (2600 calls) produced these timing results:
Not many people are going to stick around for a page that takes 6
seconds to render (in the best case scenario), and that doubles for 100
rows. The trick is to outject the selected row so that the comparison
can be done without having to invoke a Seam component. Outjection is a
mechanism in Seam that takes the value of a JavaBean property on a
component and assigns it directly to the name of a variable in the
specified scope (such as the conversion scope). You outject a property
by annotating it with the @Out annotation, as shown here:
(For readers with Seam experience, there is a reason why you cannot
simply add @BypassInterceptors to the editing() method, which I will
provide in a moment.)
Now we can check if the row is in edit mode by comparing the
iteration variable in the data table to the outjected property using
the following EL expression in the view:
Here's how things improve after making this change:
Less than one second is certainly a nice place to be. We can do
better, but let's first focus on the 5 second discrepancy because it is
a cause of concern regarding Seam's performance.
The truth is, interceptors come with a cost. Again, this cost only
adds up when you are pounding the component, like the rendered
attribute does. Unfortunately, that is more of a limitation (and a fact
of life) in the way that the data table component in JSF was designed.
On the other hand, that is why Seam provides the @BypassInterceptors
annotation. This annotation is intended to be used on methods that read
the state of a component, as opposed to a method with behavior. After
all, Seam is a stateful framework and espouses using objects as they
were intended, to have behavior and state.
So why not just add @BypassInterceptors to the editing() method to
reduce the overhead of invoking it? Theoretically that would work. The
only problem is that Seam relies on interceptors to restore the state
of conversation-scoped components, at least in Seam 2.0. In Seam 2.1,
this behavior is disabled by default, so you could just add
@BypassInterceptors to the method. However, if you plan to use stateful
session beans (SFSBs) in your application or run the application in a
cluster, you will need to enable the behavior I am about to describe,
so it's important to understand why interceptors on conversation-scoped
components are important.
Seam 2.0 uses an interceptor that aids with maintaining the object
identity of persistent objects (managed entities) across passivation of
a SFSB, or when components jump nodes in a cluster. This interceptor
has good intentions, but can have bad consequences. At the end of a
method call on a conversation-scoped component, the interceptor
transfers the values of all fields holding references to entity
instances directly into the conversation context, and then nullifies
the values of those fields. It reverses the process at the beginning of
the next call. The consequence is that without interceptors, your
conversation-scoped object is missing state. Eeek! What's worse is that
the performance of the interceptor is naturally challenged if your
component happens to be storing large data sets, because it takes time
for the interceptor to perform its work. It's an unfortunate sacrifice
in order to achieve transparent replication.
Rather than worrying about whether to use this interceptor, or
locking the design of your application into its absence, it's best just
to avoid the need to disable interceptors. Besides, there are still
other interceptors that may need to be enabled on a component's methods
(its all or nothing when you disable them) and working with outjected
data is the fastest approach anyway. You can feel comfortable calling
methods on Seam components in other areas of your page, but you should
avoid doing so inside of a data table, which brings us to our second
lesson.
Lesson #2: Don't call intercepted methods inside a data table (or in excess)
The question is, have we done all that we can do to optimize? Not
even close. There is another important lesson to learn, and this one
has to do with the EL, or more specifically, the EL resolver mechanism.
In JSF, the view is bound to server-side components using a syntax
known as the Unified Expression Language (EL). The root of an EL string
(e.g., #{itemInEditMode}) is presumed to be a variable in one of the
available web application scopes, or the name of a component that must
be created (such as a JSF managed bean or Seam component). The name
resolution is handled by the EL resolver chain, which is a collection
of objects that know how to locate or create objects that map to a
name. All resolvers in the chain are consulted until the end of the
chain is reached or a value is found. This lookup happens a tremendous
number of times while rendering a JSF view, especially while rendering
a data table. Thus, it's a potential source of performance problems.
As the EL resolver chain seeks out a variable, it becomes increasingly
more aggressive. The standard EL resolver looks in the familiar places:
the request, session, and application scope. It then turns the task
over to the Seam resolver, which is where things start to slow down.
Seam has lots of different places to look to resolve a variable: a
component, a factory, a Seam namespace, a Seam context, and the list
goes on. Thus, not finding a variable comes at a high cost.
So the solution is simply to avoid referencing a missing variable,
right? Well, what happens when a null value for a variable is
meaningful in your application, as is the case of our editable data
grid. A null value for the itemInEditMode variable means the row is not
in edit mode. Unfortunately, the EL resolver chain doesn't know that a
null value means something, and will keep working through its crib
sheet until it has tried all possible combinations. Thus, we need to
find some way to tell Seam exactly where to look rather than allowing
Seam to send out its search party, so to speak.
Again, taking advantage of the flexibility afforded to us by the JBoss
EL, we can reach directly into the conversation context to look for the
row in edit mode:
Here's the reward we get for telling Seam exactly where to look:
We roughly doubled our performance and now have 100 rows coming in
under a second, with an order of magnitude improvement over the first
run. But there is still a slight bottleneck. The variable _item is
stored in request scope by the data table and is resolved quickly, but
the variable conversationContext is a pseudo-variable that Seam
interprets after looking in all the usual places for a real variable
named conversationContext. Not only that, conversationContext is an
imported context variable, the qualified name being
org.jboss.seam.context.conversationContext. It turns out that
referencing a context variable in an imported namespace has a
measurable cost associated with it. A better choice would be to pull
the result of this lookup somewhere closer so that Seam doesn't have to
keep searching for it. We can set that up using an alias (an
event-scoped factory) in the Seam component descriptor named
conversationScope (to match requestScope, sessionScope, and
applicationScope provided by the standard EL resolver):
We now reference this name in our rendered logic:
Here's how the timing results improve:
Those are the kinds of numbers we want to see! Just as I mentioned
at the start of this article, there was likely a single culprit that
was squandering a majority of the rendering time. It turns out to have
been the logic in the rendered attribute of components within a data
table. But really any logic inside of a data table has to be optimized
because it's going to be compounded by the number of rows being
rendered. For instance, you might be conditionally rendering columns
based on the user's preferences. That brings us to the third lesson.
Lesson #3: Be extremely frugal with the logic you use within a data table
Incidentally, I thought about using an action listener to toggle the
rendered state on components in a row when the user clicks on the edit
button, since that's the "object-oriented" way of doing things.
Unfortunately, the design of the data table is extremely naive and does
not support this usage pattern. A data table doesn't have any concept
of rows, only columns. The rows are introduced dynamically based on the
data fed to the table (they are not represented in the state of the
component tree). Thus, if you change the rendered attribute on a
component in one of the rows, you end up affecting every row in the
table. The dynamic nature of the data table leads to many other
problems in JSF, including the "ghost click" which I discuss in my
book, Seam in Action
If you are committed to squeezing as much performance as possible out
of your page, then there is one more way you can optimize the speed of
the rendered logic: don't use it. I'm not suggesting that we throw out
the editable grid functionality. If you think about it, that logic only
needs to be performed once the user has selected a row. Before that
time, you know that you only need to display the table in read-only
mode (and you know which controls to provide in that case). Thus, the
best thing to do is split the table into two, one that has the rendered
logic in the columns and one that does not, then toggle the rendering
of the entire table. That way, the person just browsing the data does
not have to pay the tax of checking for the row selected for editing.
While this does increase the amount of code to maintain, it introduces
the possibility of having different columns displayed when the user is
editing than when they are just viewing (or even having the table look
different in some way). You can move common code into templates to
prevent duplication. Of course, the performance is now going to
increase noticeably.
You are probably feeling pretty happy with the progress so far.
Where to next? In each of the performance results, I have provided two
columns of data for a reason: to emphasize that we are paying yet
another tax to render the remainder of the page. That is the focus of
the next round of optimizations. Obviously, the size of this tax is
going to depend on what else you have on your screen and won't
necessarily amount to the ~270ms appearing in these test results.
Regardless, the amount of this tax now exceeds the cost of the product
and we need to do something about it. That's where Ajax comes in.
The bottleneck in any decently performing web application is the
process of the browser requesting and ultimately rendering a new page.
What makes the process worse is that it happens synchronously, forcing
the user to wait until it finishes. It's extremely disruptive and it
makes the application feel slow. A far better approach is to have the
browser only replace the portions of the page that need to be changed
and to insert those changes into the page when they arrive (i.e.,
partial page rendering), which doesn't interrupt what the user is
currently doing (or at least keeps the disruption localized).
The exchange just described is achieved using Ajax. Fortunately, the
RichFaces component library for JSF makes adding Ajax interactions to a
page extremely straightforward. In the next part of this article,
you'll learn to use RichFaces' partial-page rendering to only update
the data table when the user change its state, such as to select a row
for editing or paginating the table, thus eliminating the tax that
comes with rerendering the entire page. Once this change is made, the
two orders of magnitude performance boost will be realized. Speed up your Data-Driven JSF/Seam Application by Two Orders of Magnitude – Part 2
In the second
installment of this two-part article, Dan Allen continues his
discussion of some common performance problems you may encounter when
using JSF components, Seam components, and the EL. You'll learn about
the set of best practices for eliminating them that led to an
improvement of two orders of magnitude in the performance of his
application.
In the first part of this article, I began briefing you on
optimizations I made to maximize the responsiveness of a JSF
application that I developed out in the field. I cited performance
problems caused by casually accessing components from a JSF view, then
presented a set of best practices to eliminate this unnecessary
overhead. Despite the progress made by the end of the first part, you
had not yet witnessed the two orders of magnitude in performance
improvement that was promised. In this part, the additional gains will be achieved by
leveraging partial page rendering-provided by the RichFaces JSF
component library and by slimming the response. Partial page rendering
cuts out the overhead of rerendering the entire page after each user
interaction, which turns out to be the real bottleneck in most
traditional web applications, and instead redraws only the areas of the
page that have changed. Naturally, you want the replacement HTML source
to be as condensed as possible. These optimizations allow the
responsiveness of a web application to measure up to its desktop
counterpart. When the user performs an
operation on the screen, such as selecting a row for editing or
paginating a result set, we only want to rerender the area of the page
that is affected. Using the data-driven application presented in the
first part, that means redrawing the data table and its pagination
controls. (Note that it's possible with Ajax4jsf to rerender a single
row, when applicable, but I have found it to be more trouble than it's
worth).
Putting numbers aside, using Ajax is going to make the application feel
far more responsive because the browser does not have to bootstrap a
whole new page and all the assets that come along with it. Research
has shown that creating HTTP connections is more costly than rendering
large pages. Partial page rendering accounts for these findings by
treating the static areas of the page and its associated assets as
completed work, and focusing solely on retrieving updates. This section
will support my recommendation that you should always consider using
Ajax in your application, as it truly does eliminate a lot of overhead.
Turning regular JSF postbacks into Ajax requests is pretty simple
with Ajax4jsf. However, if used inappropriately, you won't get all the
performance gains you are looking for. We'll get to that in a second.
First, begin by changing your <h:commandLink> and
<h:commandButton> components to the ones from Ajax4jsf:
<a:commandLink> and <a:commandButton>. Next, select what
you want to rerender. You reference the areas of the page to update
using a comma-separated list of the regions' component IDs in the
reRender attribute of the command component. If you are paginating, you
need to rerender the whole table and the pagination controls
(dataTableContainer). If you are transitioning into edit mode, there's
no need to requery or update the pagination, so you only have to
rerender the table itself. Here's the code for the pagination controls:
The code for the edit control buttons in each row is similar. Here's how the edit button is defined:
What's important about using an Ajax request is to keep it simple.
You don't want to perform a lot of processing on the server because
then the users aren't going to get the "instant" feedback they are
expecting. You can drastically reduce the portion of the component tree
that is processed by JSF by using either the <;a:region> tag or
the ajaxSingle attribute on an Ajax4jsf component. Let's focus on
ajaxSingle.
By default, JSF processes the whole component tree on a postback
because it doesn't know where events may originate or which input
fields within the submitted form contain new data. What the ajaxSingle
attribute does is tell JSF to advance directly to the component that
was activated, process events from that component, and re-encode it as
if it were the only component on the page. The result is a drastic
speed increase in the processing, independent of the size of the
component tree. In fact, the only time you would forgo using ajaxSingle
is when you need to capture input data from a form (a classic form
submit).
When deciding whether or not to use this attribute, ask yourself if you
are capturing form data or whether JSF is simply using the form submit
to perform server-side work (a contrived form submit). In JSF, it's
most often the latter.
Note: Interestingly enough, ajaxSingle is also a drop-in
replacement for immediate, which is excellent since immediate is so
poorly understood. When ajaxSingle is placed on a command component
(such as a button), the form data is not processed, and hence no
validation/conversion failures can occur, thus eliminating the need for
immediate. Having shifted all interactions to the Ajax bridge, it's
now time to look at the performance gains. Of course, when using Ajax,
the tax of rendering the portions of the page outside of the data table
is gone. The performance on the server is also no longer a primary
concern. Now what matters is the size of the response. Unfortunately,
giving timing results here would be arbitrary because it's highly
dependent on the speed of the network (and I'm testing against my own
box). We have to focus on what we can control.
So what affects response size? The answer is, every character in the
view. Every character that you type that is encoded into the response,
as well as the markup that the JSF components generate, affects the
size of the response. That includes:
As you can see, there's lots of room for improvement in this category.
I want to focus on component IDs first, since they are the biggest
culprit.
After graduating from Cornell University with a degree in Materials
Science and Engineering in 2000, Dan became captivated by the world
of free and open source software, which gave him his debut in
software development. He soon discovered the combination of Linux and
the Java EE platform to be the ideal blend on which to build his
professional career, with his interests equally divided between the
two platforms.
Dan is a member of the Seam project, a dedicated open source
advocate, and a Java blogger. He lives with his extremely supportive
wife in Laurel, MD. You can keep up with Dan's development
experiences by subscribing to his blog at
MojavelinuxThe performance roadblock
Page actions and component initialization
<action execute="#{benthicMsmntEditor.prepareResults}" />
The cost of conditional rendering
<h:column>
<f:facet name="header">Taxonomic Unit</f:facet>
<h:outputText
value="#{_item.taxon.name}"
rendered="#{!benthicMsmntEditor.editing(_item)}"/>
<h:selectOneMenu id="taxonomicUnit"
rendered="#{benthicMsmntEditor.editing(_item)}"
defaultLabel="No value set" value="#{_item.taxon}">
<s:selectItems value="#{taxonValues}" var="_taxon"
label="#{_taxon.name}" noSelectionLabel="No value set"/>
<s:convertEntity/>
</h:selectOneMenu>
</h:column>
public boolean editing(T itemInRow) {
return itemInEditMode == itemInRow;
}
rendered="#{_item == benthicMsmntEditor.itemInEditMode}"
Request
Elapsed time of request (ms)
Time to render table (ms)
1
6330
6090
2
6340
6096
3
6400
5883
4
6100
5850
avg
6292.4
5979.8
@Out(required = false) private T itemInEditMode;
"#{item == itemInEditMode}"
Request
Elapsed time of request (ms)
Time to render table (ms)
1
904
663
2
807
608
3
813
569
4
823
592
avg
836.8
608
Seam's managed entity interceptor
Resolving variables efficiently
rendered="#{_item == conversationContext.get('itemInEditMode')}"
Request
Elapsed time of request (ms)
Time to render table (ms)
1
491
207
2
495
224
3
493
201
4
444
211
avg
480.8
210.8
<factory name="conversationScope" value="#{conversationContext}"/>
rendered="#{_item == conversationScope.get('itemInEditMode')}"
Request
Elapsed time of request (ms)
Time to render table (ms)
1
399
161
2
373
150
3
458
207
4
560
163
avg
447.5
170.25
Request
Elapsed time of request (ms)
Time to render table (ms)
1
537
174
2
355
127
3
372
127
4
374
127
avg
409.5
138.8
Cutting costs with Ajax
by Dan Allen
27 Mar 2009 01:30 EDT
Tapping into Ajax
Accessible Ajax with Ajax4jsf
<a:commandLink id="previous" action="#{benthicMsmntEditor.previous}"
rendered="#{benthicMsmntEditor.previousAvailable}"
reRender="dataTableContainer"
ajaxSingle="true">
<h:graphicImage value="/img/previous.png" alt="Previous" title="Previous"/>
</a:commandLink>
<a:commandLink id="next" action="#{benthicMsmntEditor.next}"
rendered="#{benthicMsmntEditor.nextAvailable}"
reRender="dataTableContainer"
ajaxSingle="true">
<h:graphicImage value="/img/next.png" alt="Next" title="Next"/>
</a:commandLink>
<a:commandLink id="edit" action="#{benthicMsmntEditor.editItem}"
rendered="#{_item != conversationScope.get('itemInEditMode')}"
reRender="dataTable" ajaxSingle="true">
<h:graphicImage value="/img/edit.png" alt="Edit" title="Edit"/>
</a:commandLink>
Keeping the Ajax call brief
Trimming down the response
Resources
Dan Allen is a Senior Software Engineer at Red Hat and author of Seam
in Action. He has over eight years of development experience using
technologies that include Java frameworks (Seam, JSF, EJB3, Hibernate,
Spring, Struts), testing frameworks (JUnit, TestNG), JavaScript and
DOM scripting, CSS and page layouts, Maven 2, Ant, Groovy, and many
others.
Recently, I mentioned how I had just started working with the JBoss Seam application framework.
The more I have worked with it, the more impressed I have become at how
it makes things that are typically difficult using standard Java EE
much simpler and more streamlined. One of the areas in which Seam
really shines is security. While Java EE defines JAAS
for use in securing applications, it is left up to the developer to
ingrain this security down to each facet of the application. With Seam,
it is easy to define security constraints at all levels of an
application, simply through using annotations. In addition, the
complexity of authenticating users with JAAS is reduced through Seam's authenticator mechanism. This article will give an introduction to Seam authentication, and show how to write your own custom authenticator.
Seam's
authenticator construct hides the complexity of managing a JAAS
configuration, and allows you to implement authentication how you see
fit. Perhaps your organization relies on a simple username/password
combination for authenticating user accounts in LDAP. Maybe you use a
SecureID token, and the accounts are stored in a SQL database. By
writing your own authenticator class, or making use of publicly
available ones, you can control the way authentication is done in your
organization.
To get started with your own authenticator, you must first declare it in the components.xml
file. This file manages much of the configuration for Seam. To add your
authenticator, you simply define the class and method that will be used
for authentication. For example:
Posted by
WhoAmI?
at
11:59 AM
components.xml
:
<transaction:ejb-transaction />
javax.persistence.EntityTransaction
interface. EntityTransaction
begins the transaction at the beginning of the apply request values phase.
org.hibernate.Transaction
interface. HibernateTransaction
begins the transaction at the beginning of the apply request values phase.
org.springframework.transaction.PlatformTransactionManager
interface. The Spring PlatformTransactionManagement
manager may begin the transaction at the beginning of the apply request values phase if the userConversationContext
attribute is set.
#{em}
is the name of the persistence:managed-persistence-context
component. If your managed persistence context is named entityManager
, you can opt to leave out the entity-manager
attribute. (see Section 10.3, “Seam-managed persistence contexts”Seam-managed persistence contexts)
<transaction:entity-transaction entity-manager="#{em}"/>
#{hibernateSession}
is the name of the project's persistence:managed-hibernate-session
component. If your managed hibernate session is named session
, you can opt to leave out the session
attribute. (see Section 10.3, “Seam-managed persistence contexts”Seam-managed persistence contexts)
<transaction:hibernate-transaction session="#{hibernateSession}"/>
<transaction:no-transaction />
?/p>
?/p>
?/p>
?/p>
EntityManager 提供了这些功能。首先,如何把EntityManager 引入我的代码Q很单:
好了Q我们看看一个Entity q程如何产生Q?/p>
q很单。现在,qentity 的状态是NEW/TRANSIENT 。这意味着一个entity已经存在于你的应用程序中Q但q不hidQ也不存在于你的数据库中?br />
׃我们要它持久化Q即它应被写入数据库Q,我们应把它的状态{换ؓMANAGED ?/span>
现在Q此entity由EntityManager理了。EntityManager控制entity写入数据库。这动作无须立刻发生Q可能把你的entity攑֜cacheQ稍后写入数据库。你可以攑ֿQ写动作肯定会发生?br />
Ok, what about reading an existing entity from the database? Therefore we use:
好,如何从数据库中读出已存在的entity呢?q样Q?/p>
每个entity 有一个idQ我已经说过Q多数情况下是Long数据cdQ,通过id你可存取entity。这是这里的W二个参数。第一个参C表你要存取的Entity class的进E。find操作之后Qentity的状态也?span style="font-weight: bold;">MANAGED ?/span>
一旦entity数据有所改变Q将反映到数据库中。不能确认EntityManager何时会向数据库写入更新的数据。但是这一事g肯定会发生,一般是马上Q但不会晚于EntityManager消失;)。你可以控制触发更新数据库:
em.flush();
q将强制EntityManager 立刻更新写入数据库。请CQ这条语句将影响所有状态ؓMANAGED?/span>entitieQ而不是当前一个。不q,一般情况下不必q么做?br />
如果你想反其道而行之,也就是从数据库蝲入一?span>entity Q因为别人可能已l修改其数据Q,q么写:
em.refresh(entity);
怎样删除一个entity呢?很简单:
em.remove(entity);
现在Qentity的状态成?span style="font-weight: bold;">REMOVEDQ表C其已经被列入删除计划。你可以?/span>flush()语句Q让删除动作马上发生Q但没必要这么干?br />
现在来点复杂的。当注入EntityManager 的时候,有一?span style="font-weight: bold;">事务范围持久上下文(
Posted by Roger Keays, 7 June 2007, 12:28 PM
This is probably one of the most frequently asked questions on the Facelets mailing list. Why doesn't my c:forEach tag work correctly? Unfortunately, there are may ways to misuse the jstl tags available in Facelets, so the answer isn't always simple. Here is an explanation of the differences between c:forEach and ui:repeat, along with some examples which will hopefully save you some headaches.
The most important thing to understand about the jstl tags in Facelets is that they do not represent components and never become a part of the component tree once the view has been built. Rather, they are tags which are actually responsible for building the tree in the first place. Once they have done their job they expire, are no more, cease to be, etc etc.
Here is a table of the semantics of several common tags. I just discovered, reading the Facelets code, that validators and converters are classified separately. I had always thought they were just tag handlers, but I imagine they behave in much the same way.
TagHandlers |
Components |
Other |
c:forEach |
ui:repeat |
f:validator f:converter |
One of the problems here is that there is no naming convention to indicate which tags correspond to which constructs. You've just got to know, or find out.
Now that you understand that tag handlers are only effective when the tree is built, the next logical question should be well, when is tree built?
The short answer is that a new view is built for every request which is not a postback. During a postback, the view is reconstructed from saved state. Quite confusing, and not very obvious I know, but there you have it.
The most common pitfalls are either with the JSF lifecycle, EL evaluation or combining tag handlers with components.
My c:if always evaluates to false
<h:dataTable values="${numbers}" var="number">
<h:column>
<c:if test="${number > 5}">
<h:outputText value="${number}"/>
</c:if>
</h:column>
</h:datatable>
Yes, the c:if is always evaluating to false! But it is only ever evaluated once - when the tree is built. The h:outputText component never makes it into the tree. Solution: replace the c:if with:
<ui:fragment rendered="${number > 5}"> ... </ui:fragment>
You could also use the rendered attribute on the h:outputText component in this example.
My ui:include fails inside ui:repeat
<ui:repeat value="#{bean.items}" var="item">
<ui:include src="#{item.src}"/>
</ui:repeat>
The EL for the ui:include is evaluated when the view is built and is invalid since it relies on a variable only made available by the ui:repeat during rendering. Use c:forEach in this case.
My recursive tag never stops
myTag.xhtml:
<ul>
<ui:repeat value="${item.children} var="child">
<li><eg:myTag item="${child}"/></li>
</ui:repeat>
</ul>
The stop condition in this recursion is supposed to be when you run out of children. The problem is that the custom eg:myTag is just a tag handler, like a special version of ui:include. When the view is built, the ui:repeat has no influence on the building process and can't stop the recursion. Use c:forEach here instead of ui:repeat. Or better still, convert your tag file to a real UIComponent.
You might also recognise that the ${child} EL expression is meaningless during build time in this example, unless you use c:foreach.
My list doesn't change size after deleting or adding an item
<h:form>
<c:forEach items="${list}" var="item">
<h:outputText value="${item.name}"/><br/>
</c:forEach>
<h:commandButton value="Create new item" action="..."/>
<h:commandButton value="Delete an item" action="..."/>
</h:form>
When your view was built you only had, say, 5 items. If you post back to this view and add or delete an item, your view still has 5 h:outputText components in it since it was restored from saved state. In this simple case, you should use ui:repeat and your tree will always contain one h:ouputText component which is iterated over with differing values of ${item}.
If you rely on using c:forEach to dynamically include different form components you could run into difficulty. Always try to think about what the resulting tree looks like and remember it doesn't change on a postback.
Probably the best relief to this problem would be to come up with a better syntax or naming convention to distinguish between tag handlers and components. I imagine you could also improve compilation performance if you did this.
Secondly, we need better terminology. I've used the terms tag handler and component in this blog which isn't too bad. The Facelets' FAQ [1] uses the terms build-time tags and render-time tags which is a bit misleading because render-time tags (components) are involved in all phases of the JSF lifecycle, not just the Render View phase.
Whatever happens, tag handlers are very useful (would you use facelets without ui:decorate?) so let's not get rid of them.
[1] http://wiki.java.net/.../FaceletsFAQ#Why_doesn_t_my_c_if_ui_repeat_ui
See http://docs.jboss.org/seam/latest/reference/en/html/controls.html
<h:selectOneMenu value="#{person.continent}" required="true"> (1)
<s:selectItems value="#{continents.resultList}" var="continent" (2)
label="#{continent.name}" noSelectionLabel="Please Select..."/>
<s:convertEntity /> (3)
</h:selectOneMenu>
This works just like selecting an entity, but <s:convertEnum/> is used instead.
XHTML:
<h:selectOneMenu id="marketStatus" value="#{person.status}" (1)
required="true">
<s:selectItems value="#{enumLists.statusArray}" var="status" (2)
label="#{status}"
noSelectionLabel="Select a status..."/>
<s:convertEnum/>
</h:selectOneMenu>
EnumLists.java:
@Name("enumLists")
@Scope(ScopeType.STATELESS)
public class EnumLists
{
public Status[] getStatusArray()
{
return Status.values();
}
}
Here we use a selectManyCheckbox.
<h:selectManyCheckbox id="roles"
layout="pageDirection" value="#{person.roles}"
required="true">
<s:selectItems value="#{enumLists.roleArray}" var="role"
label="#{role}"/>
<s:convertEnum/>
</h:selectManyCheckbox>
Unfortunately, Seam's convertEnum can't handle multi selects yet. This example will yeild a strange exception:
java.lang.IllegalArgumentException: java.util.List is not an enum type
Luckily, it's very easy to create custom converter tags with Facelets. Here is the converter class that handles both ordinary enums and multi-selects:
package eg;
import javax.faces.component.*;
import javax.faces.context.*;
import javax.faces.convert.*;
import javax.faces.el.ValueBinding;
import java.util.List;
import java.util.Collection;
/**
* Converter for enum multi-selects.
* <br>User: Joshua Davis
* Date: May 16, 2007
* Time: 7:25:58 AM
*/
public class EnumListConverter implements Converter
{
@SuppressWarnings({"unchecked"})
public Object getAsObject(FacesContext context,
UIComponent comp,
String value)
throws ConverterException
{
ValueBinding binding = comp.getValueBinding("value");
Class enumType = binding.getType(context);
if (enumType.isEnum()) // Single enum?
return Enum.valueOf(enumType, value);
else // List of enums.
{
// Find the s:selectItems so we can get the enum.
List children = comp.getChildren();
for (Object child : children)
{
if (child instanceof UIComponent)
{
UIComponent c = (UIComponent) child;
ValueBinding b = c.getValueBinding("value");
Class t = b.getType(context);
// Array of enums: use the component type.
if (t.isArray() && t.getComponentType().isEnum())
{
t = t.getComponentType();
return Enum.valueOf(t,value);
}
else
{
Object v = b.getValue(context);
// Collection of enum values, get the type of the first element.
if (v instanceof Collection)
{
t = ((Collection) v).iterator().next().getClass();
return Enum.valueOf(t,value);
}
}
}
}
throw new ConverterException("Unable to find selectItems with enum values!");
}
}
public String getAsString(FacesContext context,
UIComponent component,
Object object)
throws ConverterException
{
if (object == null) {
return null;
}
return ((Enum) object).name();
}
}
I
spent quite some time trying to find a solution for the following JSF
issue: it is not possible to iterate over a java.util.Set.
- ui:repeat (facelets) doesn’t work
- a4j:repeat (richfaces) doesn’t work
- c:forEach works..only in case it does not rely on a variable defined by a parent component (rich:dataTable for instance)
All above are pretty logical phenomena, as UIData relies on ordered data, and generally a Set is not ordered.
In my case I had to use a Set defined in the Hibernate (JPA) object (PersistentSet).
An important note: you should use a set in case the view order is of no matter to you.
The solution..is pretty simple. And I’ll suggest it to be a part of facelets/richfaces for the next version, unless of course there is some valid specific
reason for it not to be.
1. Define your own UI component extending an existing repeater component. I used a4j:repeat (HtmlAjaxRepeat)
2. Override the metohd getDataModel
3. Define your component in your faces-config
4. create a custom facelets tag definition
5. Define a context-variable in web.xml pointing to the facelet tag definition.
Note: for use with JSP instead of Facelets, you should define a .tld and a Tag handler, which is not an ojbect of this post.
Now let’s see the steps in detail:
1,2. Here some code:
So, as we don’t care about the order of the elements, we just create a new ArrayList out of the Set. And we can now easily return the appropirate DataModel.
3. Add this to your faces-config. (I copied it from the a4j definition)
4. Here is the tag definition for facelets
Save this file as /WEB-INF/facelets/custom.taglib.xml
5. Add to your web.xml
6. It is now ready to use
…
xmlns:cust=”http://myproject.com/cust”
…
<cust:repeat var=”myVar” value=”${aSet}”>
…
</cust:repeat>
I think it is way neater than other workarounds, like defining a custom EL Resolver.