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.
In this article I will detail and example of executing an @Asynchronous task in Seam and hooking Richfaces progressBar to display the current status of that task. I also updated the example to show how to achieve the same polling of the results via a:poll.
This example is not necessary optimized, just written as a baseline example to work off of. Let's start with a basic Entity to work with:
@Entity public class OrderRecord implements Serializable { private Long id; private Integer version; private String name; @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Version public Integer getVersion() { return version; } private void setVersion(Integer version) { this.version = version; } @Length(max=20) public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Id: " + id + " Name: " + name; } }
Now that we have the entity, let's create the Seam JavaBean. This bean will generate a random number which will determine the number of OrderRecords to insert, then when inserting those OrderRecords, random data will be generated to populate it.
AsyncExample.java
@Name("asyncExample") @Scope(ScopeType.CONVERSATION) public class AsyncExample { @Logger private Log log; @In EntityManager entityManager; int randomNumber; int min = 0; int max; long currentValue = -1; private boolean pollEnabled = false; @Begin(join=true) public void begin() { randomNumber = new Random().nextInt(5000); log.debug("Found " + randomNumber + " of OrderRecords to insert."); max = randomNumber; } private boolean buttonRendered = true; private boolean enabled = false; private Long startTime; private Long endTime; private int totalTime; private int operationsPerSecond; @Asynchronous public String startProcess(AsyncExample asyncExample) throws InterruptedException { asyncExample.setEnabled(true); asyncExample.setButtonRendered(false); long startTime = new Date().getTime(); asyncExample.setStartTime(startTime); asyncExample.insertOrderRecords(); return null; } @Transactional public void insertOrderRecords() { entityManager.joinTransaction(); OrderRecord orderRecord = null; for(int i = 1; i <= randomNumber; i++) { orderRecord = new OrderRecord(); orderRecord.setName("Person" + new Random().nextInt()); log.debug("Inserting orderRecord " + orderRecord); entityManager.persist(orderRecord); currentValue = i; entityManager.flush(); } endTime = new Date().getTime(); totalTime = (int)(endTime - startTime) / 1000; operationsPerSecond = max / totalTime; } public Long getCurrentValue() { if (isEnabled()) { if(currentValue < max) { return currentValue; } } if (startTime == null) { return Long.valueOf(-1); } else { return Long.valueOf(max + 1); } } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Long getStartTime() { return startTime; } public void setStartTime(Long startTime) { this.startTime = startTime; } public boolean isButtonRendered() { return buttonRendered; } public void setButtonRendered(boolean buttonRendered) { this.buttonRendered = buttonRendered; } public Log getLog() { return log; } public EntityManager getEntityManager() { return entityManager; } public int getMin() { return min; } public void setMin(int min) { this.min = min; } public int getMax() { return max; } public void setMax(int max) { this.max = max; } public int getRandomNumber() { return randomNumber; } public void setRandomNumber(int randomNumber) { this.randomNumber = randomNumber; } public void setCurrentValue(long currentValue) { this.currentValue = currentValue; } public Long getEndTime() { return endTime; } public void setEndTime(Long endTime) { this.endTime = endTime; } public int getTotalTime() { return totalTime; } public void setTotalTime(int totalTime) { this.totalTime = totalTime; } public int getOperationsPerSecond() { return operationsPerSecond; } public void setOperationsPerSecond(int operationsPerSecond) { this.operationsPerSecond = operationsPerSecond; } public boolean isPollEnabled() { return pollEnabled; } public void setPollEnabled(boolean pollingEnabled) { this.pollEnabled = pollingEnabled; } }
Now for the asyncExample.xhtml:
<!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:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" xmlns:a="http://richfaces.org/a4j" template="layout/template.xhtml"> <ui:define name="body"> <h:messages globalOnly="true" styleClass="message" /> <!-- The following rich:panel contains code to handle viewing the asynchronous results via a:poll --> <rich:panel> <f:facet name="header">Asynchronous Process and a4j:poll</f:facet> <h:form id="beginLRCForm"> <a:commandButton action="#{asyncExample.begin()}" value="Begin LRC" reRender="asyncExampleForm, beginLRCForm" rendered="#{!conversation.longRunning}" style="margin: 9px 0px 5px;" ignoreDupResponses="true" eventsQueue="async" requestDelay="200" /> </h:form> <a:region> <h:form id="pollForm"> <a:poll id="poll" interval="1000" enabled="#{asyncExample.pollEnabled}" reRender="poll,grid"/> </h:form> </a:region> <h:form id="startAsyncForm"> <h:panelGrid columns="2" width="80%" id="grid"> <h:panelGrid columns="1"> <h:outputText value="Polling Inactive" rendered="#{not asyncExample.pollEnabled}"></h:outputText> <h:outputText value="Polling Active" rendered="#{asyncExample.pollEnabled}"></h:outputText> <a:commandButton style="width:120px" id="control" value="#{asyncExample.pollEnabled?'Stop':'Start'} Polling" reRender="poll, grid"> <a:actionparam name="polling" value="#{!asyncExample.pollEnabled}" assignTo="#{asyncExample.pollEnabled}"/> </a:commandButton> <a:commandButton action="#{asyncExample.startProcess(asyncExample)}" value="Insert Order Records" reRender="progressPanel2" rendered="#{asyncExample.buttonRendered and conversation.longRunning}" style="margin: 9px 0px 5px;" ignoreDupResponses="true" eventsQueue="async" requestDelay="200" /> </h:panelGrid> <h:outputText id="serverDate" style="font-size:16px" value="#{asyncExample.currentValue} of #{asyncExample.max} processed" rendered="#{asyncExample.pollEnabled}"/> </h:panelGrid> </h:form> </rich:panel> <!-- This rich:panel contains the code to view the asynchronous results via rich:progressBar --> <rich:panel> <f:facet name="header">Asynchronous Process and the rich:progressBar</f:facet> <h:form id="asyncExampleForm"> <a:commandButton action="#{asyncExample.begin()}" value="Begin LRC" reRender="asyncExampleForm" rendered="#{!conversation.longRunning}" style="margin: 9px 0px 5px;" ignoreDupResponses="true" eventsQueue="async" requestDelay="200" /> <a:outputPanel id="progressPanel2"> <rich:progressBar value="#{asyncExample.currentValue}" interval="1000" label="#{asyncExample.currentValue} of #{asyncExample.max} processed" enabled="#{asyncExample.enabled}" minValue="#{asyncExample.min}" maxValue="#{asyncExample.max}" reRenderAfterComplete="progressPanel2" ignoreDupResponses="true" eventsQueue="async" requestDelay="200" > <f:facet name="complete"> <br /> <h:outputText value="Process Complete. There were #{asyncExample.max} records inserted over #{asyncExample.totalTime} seconds for an average of #{asyncExample.operationsPerSecond} Records / second" /> </f:facet> </rich:progressBar> </a:outputPanel> <a:commandButton action="#{asyncExample.startProcess(asyncExample)}" value="Insert Order Records" reRender="progressPanel2" rendered="#{asyncExample.buttonRendered and conversation.longRunning}" style="margin: 9px 0px 5px;" ignoreDupResponses="true" eventsQueue="async" requestDelay="200" /> </h:form> </rich:panel> </ui:define> </ui:composition>
When accessing the page, you must first start the Long Running conversation by clicking the Begin LRC button. After the LRC is started you'll see the button Insert Order Records. Click that, wait for the records to be inserted, then metrics will show up on the operation. In my example I received:
Process Complete. There were 4862 records inserted over 43 seconds for an average of 113 Records / second