seam.quartz.properties -- has to be in the jar directory if using exploded ear files.. so add it to the build.xml
org.quartz.scheduler.instanceName = schedule
org.quartz.scheduler.instanceId = 1
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer = true
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 3
org.quartz.logger.schedLogger.class = org.quartz.impl.Log4jLogger
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.dataSource = quartzDatasource
org.quartz.jobStore.nonManagedTXDataSource = noTxquartzDatasource
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.dataSource.quartzDatasource.jndiURL=java:/quartzDatasource
org.quartz.dataSource.noTxquartzDatasource.jndiURL=java:/noTxquartzDatasource
going to need some data sources... i actually split up the quartz job database from the database i uses to store the handles and other application stuff.
quartzDS.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE datasources
PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
"http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
<datasources>
<xa-datasource>
<jndi-name>quartzDatasource</jndi-name>
<track-connection-by-tx/>
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
<xa-datasource-property name="PortNumber">5432</xa-datasource-property>
<xa-datasource-property name="DatabaseName">quartz</xa-datasource-property>
<xa-datasource-property name="User">sa</xa-datasource-property>
<xa-datasource-property name="Password">sa</xa-datasource-property>
</xa-datasource>
<no-tx-datasource>
<jndi-name>noTxquartzDatasource</jndi-name>
<connection-url>jdbc:postgresql://localhost/quartz</connection-url>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>sa</user-name>
<password>sa</password>
</no-tx-datasource>
</datasources>
second datasource
handleDS.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE datasources
PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
"http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
<datasources>
<xa-datasource>
<jndi-name>quartzHandleDatasource</jndi-name>
<track-connection-by-tx/>
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
<xa-datasource-property name="PortNumber">5432</xa-datasource-property>
<xa-datasource-property name="DatabaseName">handlequartz</xa-datasource-property>
<xa-datasource-property name="User">sa</xa-datasource-property>
<xa-datasource-property name="Password">sa</xa-datasource-property>
</xa-datasource>
<no-tx-datasource>
<jndi-name>noTxquartzHandleDatasource</jndi-name>
<connection-url>jdbc:postgresql://localhost/handlequartz</connection-url>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>sa</user-name>
<password>sa</password>
</no-tx-datasource>
</datasources>
---- persistence.xml
<persistence-unit name="handleQuartzStorePU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/quartzHandleDatasource</jta-data-source>
<class>org.monarchnc.aulm.model.quartz.QuartzHandles</class>
<exclude-unlisted-classes >true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="jboss.entity.manager.factory.jndi.name" value="java:/quartzemf"/>
</properties>
</persistence-unit>
------components.xml
<persistence:managed-persistence-context name="quartzEntityManager" auto-create="true"
persistence-unit-jndi-name="java:/quartzemf"/>
<async:quartz-dispatcher/>
make sure you add quartz.jar to your deployed-jars.ear.list
next an entity to store the handles
@Entity
@Table(name = "quartz_handles")
public class QuartzHandles implements Serializable {
/**
* @author Nathan Dennis
*/
private static final long serialVersionUID = -9080580543695591916L;
private long id;
private QuartzTriggerHandle quartzTriggerHandle;
private Date timestamp;
private Date startdate =Calendar.getInstance().getTime();
private Date enddate=Calendar.getInstance().getTime();
private String cron = "0 /5 * * * ?";
private String handleName;
private String methodName;
private boolean active = true;
@Id @GeneratedValue
@Column(name = "id")
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
@Column(name = "handle_name", nullable = false, length = 250)
@Length(max = 250)
@NotNull
public String getHandleName() {
return this.handleName;
}
public void setHandleName(String handleName){
this.handleName=handleName;
}
@Column(name = "method_name", nullable = false, length = 250)
@Length(max = 250)
@NotNull
public String getMethodName() {
return this.methodName;
}
public void setMethodName(String methodName){
this.methodName=methodName;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "timestamp", length = 8)
public Date getTimestamp() {
return this.timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
@Column(name = "handle")
public QuartzTriggerHandle getQuartzTriggerHandle() {
return quartzTriggerHandle;
}
public void setQuartzTriggerHandle(QuartzTriggerHandle quartzTriggerHandle) {
this.quartzTriggerHandle = quartzTriggerHandle;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name="startdate")
public Date getStartdate() {
return startdate;
}
public void setStartdate(Date startdate) {
this.startdate = startdate;
}
@Column(name = "cron", length=250, nullable = false)
@Length(max = 250)
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name="enddate")
public Date getEnddate() {
return enddate;
}
public void setEnddate(Date enddate) {
this.enddate = enddate;
}
@Column(name="active")
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
home and list objects.. PAY ATTENTION TO THE PERSISTENCE I OVERRIDE
@Name("quartzHandlesHome")
public class QuartzHandlesHome extends EntityHome<QuartzHandles>
{
/**
* @author Nathan Dennis
* home object for cron
*/
private static final long serialVersionUID = 9193388548689584255L;
@Logger Log log;
public void setQuartzHandlesId(Long id) {
setId(id);
}
public Long getQuartzHandlesId() {
return (Long) getId();
}
@Override
protected QuartzHandles createInstance() {
QuartzHandles quartzHandles = new QuartzHandles();
return quartzHandles;
}
public void load() {
if (isIdDefined()) {
wire();
}
}
public void wire() {
getInstance();
}
public boolean isWired() {
return true;
}
public QuartzHandles getDefinedInstance() {
return isIdDefined() ? getInstance() : null;
}
@Override
protected String getPersistenceContextName() {
return "quartzEntityManager";
}
}
-------------------
List object
-------------------
import org.jboss.seam.annotations.Name;
import org.jboss.seam.framework.EntityQuery;
import java.util.Arrays;
@Name("quartzHandlesList")
public class QuartzHandlesList extends EntityQuery<QuartzHandles> {
/**
* @author Nathan Dennis
* cron manager list bean
*/
private static final long serialVersionUID = 6814491664526846768L;
private static final String EJBQL = "select quartzHandles from QuartzHandles quartzHandles";
private static final String[] RESTRICTIONS = {
"lower(quartzHandles.cron) like lower(concat(#{quartzHandlesList.quartzHandles.cron},'%'))",
"lower(quartzHandles.handleName) like lower(concat(#{quartzHandlesList.quartzHandles.handleName},'%'))",
"lower(quartzHandles.methodName) like lower(concat(#{quartzHandlesList.quartzHandles.methodName},'%'))",};
private QuartzHandles quartzHandles = new QuartzHandles();
public QuartzHandlesList() {
setEjbql(EJBQL);
setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
setMaxResults(25);
}
public QuartzHandles getQuartzHandles() {
return quartzHandles;
}
@Override
protected String getPersistenceContextName() {
return "quartzEntityManager";
}
}
here is where it started getting shaky. i had a little trouble getting the vailidator to work.. so i moved some of this mess into another bean...
just conversation issues.. and i got tired of fighting it.... dont laugh.. it was a cheap shot and it works. lol
@Stateful
@Name("quartzEditAction")
@Scope(ScopeType.CONVERSATION)
public class QuartzEditAction implements QuartzEditLocal{
/**
* @author nathan dennis
* @unit
* @date
*/
@In(required=true)
EntityHome<QuartzHandles> quartzHandlesHome;
@In AsyncGenericProcessor asyncGenericProcessor;
@In
EntityManager quartzEntityManager;
private String handleName;
private String methodName;
private String cron = "0 /5 * * * ?";
private Date startdate = Calendar.getInstance().getTime();
private Date enddate = Calendar.getInstance().getTime();
public void init(){
if(quartzHandlesHome.isManaged()){
this.handleName = quartzHandlesHome.getInstance().getHandleName();
this.methodName = quartzHandlesHome.getInstance().getMethodName();
this.cron = quartzHandlesHome.getInstance().getCron();
this.startdate = quartzHandlesHome.getInstance().getStartdate();
this.enddate = quartzHandlesHome.getInstance().getEnddate();
}
}
private void copy(){
quartzHandlesHome.getInstance().setHandleName(handleName);
quartzHandlesHome.getInstance().setMethodName(methodName);
quartzHandlesHome.getInstance().setStartdate(startdate);
quartzHandlesHome.getInstance().setEnddate(enddate);
quartzHandlesHome.getInstance().setCron(cron);
}
public String save(){
String result = "failed";
copy();
if (quartzHandlesHome.isManaged()){
result = quartzHandlesHome.update();
} else {
result = quartzHandlesHome.persist();
}
return result;
}
public String remove(){
String result = "failed";
QuartzTriggerHandle handle = quartzHandlesHome.getInstance().getQuartzTriggerHandle();
quartzHandlesHome.getInstance().setQuartzTriggerHandle(null);
//quartzHandlesHome.getInstance().setActive(false);
try
{
handle.cancel();
result = "deleted";
}
catch (Exception nsole)
{
FacesMessages.instance().add("Delete Failed");
}
result = quartzHandlesHome.remove();
return result;
}
public void methodValidator(FacesContext context,
UIComponent toValidate, Object value) throws ValidatorException {
String method = (String) value.toString();
System.out.print("context handlename: " + handleName);
if(handleName == null || handleName.isEmpty()){
throw new ValidatorException(new FacesMessage("Class (handleName) can't be null. Please set it to the EJB name for the class you wish to execute from."));
} else if (method == null || method.isEmpty()){
throw new ValidatorException(new FacesMessage("Method name can't be null. Please enter the method name from handle seleted including only the alpha characters."));
} else {
GenericLocal test = (GenericLocal) Component.getInstance("genericProcessing");
boolean sw = test.methodExist(Component.getInstance(handleName), method);
if(sw){
//do nothing
System.out.println("METHOD EXIST");
} else {
System.out.println("METHOD CANT BE FOUND");
FacesMessage message = new FacesMessage();
message.setDetail("Method "+ method +" does not exist in class: " + handleName);
message.setSummary("Method "+ method +" does not exist in class: " + handleName);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
public void classValidator(FacesContext context,
UIComponent toValidate, Object value) throws ValidatorException {
String handleName = (String) value.toString();
if(handleName == null || handleName.isEmpty()){
throw new ValidatorException(new FacesMessage("Class (handleName) can't be null. Please set it to the EJB name for the class you wish to execute from."));
} else {
//rediculous hack...
this.handleName = handleName;
GenericLocal test = (GenericLocal) Component.getInstance("genericProcessing");
boolean sw = test.classExist(Component.getInstance(handleName));
if(sw){
//do nothing
// System.out.println("METHOD EXIST");
} else {
System.out.println("CLASS CANT BE FOUND");
FacesMessage message = new FacesMessage();
message.setDetail("Class "+ handleName +" does not exist");
message.setSummary("Class "+ handleName +" does not exist");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
public String create()
{
String result = save();
QuartzHandles quartzHandles = quartzHandlesHome.getInstance();
QuartzTriggerHandle handle;
try {
handle = asyncGenericProcessor.scheduleQue(quartzHandles.getStartdate(),
quartzHandles.getCron(),
quartzHandles.getEnddate(),
quartzHandles);
quartzHandles.setQuartzTriggerHandle( handle );
// this.quartzEntityManager.merge(quartzHandles);
this.quartzHandlesHome.getInstance().setQuartzTriggerHandle(handle);
quartzHandlesHome.update();
System.out.println(quartzHandles.getQuartzTriggerHandle().toString());
} catch (IOException e) {
FacesMessages.instance().add("Create Cron Failed.");
e.printStackTrace();
result = "failed";
}
return result;
}
@Transactional
public String cancel() {
String result = "updated";
QuartzHandles quartzHandles = quartzHandlesHome.getInstance();
QuartzTriggerHandle handle = quartzHandles.getQuartzTriggerHandle();
quartzHandles.setQuartzTriggerHandle(null);
quartzHandles.setActive(false);
try
{
handle.cancel();
}
catch (Exception nsole)
{
result="failed";
FacesMessages.instance().add("Cancel Cron Failed.");
}
return result;
}
@Transactional
public String pause(){
String result = "updated";
QuartzHandles quartzHandles = quartzHandlesHome.getInstance();
try {
quartzHandles.getQuartzTriggerHandle().pause();
FacesMessages.instance().add("Cron Paused.");
} catch (Exception e) {
result = "failed";
e.printStackTrace();
FacesMessages.instance().add("Pause Cron Failed.");
}
return result;
}
@Transactional
public String resume(){
String result = "updated";
QuartzHandles quartzHandles = quartzHandlesHome.getInstance();
try {
quartzHandles.getQuartzTriggerHandle().resume();
FacesMessages.instance().add("Cron Resumed.");
} catch (Exception e) {
result = "failed";
e.printStackTrace();
FacesMessages.instance().add("Pause Cron Failed.");
}
return result;
}
public String getHandleName() {
return handleName;
}
public void setHandleName(String handleName) {
this.handleName = handleName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
public Date getStartdate() {
return startdate;
}
public void setStartdate(Date startdate) {
this.startdate = startdate;
}
public Date getEnddate() {
return enddate;
}
public void setEnddate(Date enddate) {
this.enddate = enddate;
}
@Destroy @Remove
public void destroy() {}
}
now we need the Asynchronous bean... tricky here.. i didnt see it documented anywhere...
looks like it is returning null,, but it is actually returning the handle. seam magic happens.
i would have like to have seen a little more said about this. but here it is.
@Name("asyncGenericProcessor")
@AutoCreate
public class AsyncGenericProcessor {
@Logger Log log;
// @In
// EntityManager quartzEntityManager;
@Asynchronous
@Transactional
public QuartzTriggerHandle scheduleQue(@Expiration Date when,
@IntervalCron String cron,
@FinalExpiration Date endDate,
QuartzHandles quartzHandles) throws IOException
{
//quartzHandles = quartzEntityManager.merge(quartzHandles); //this will overwrite the original handle after the initial firing of the job.. dont know why they had it in the example. doesnt work well.
GenericLocal np = (GenericLocal) Component.getInstance("genericProcessing");
np.getProperty(Component.getInstance(quartzHandles.getHandleName()), quartzHandles.getMethodName());
return null;
}
}
---------interface
@Local
public interface GenericAsyncLocal {
/**
* @author nathan dennis
* @unit
* @date
*/
public QuartzTriggerHandle scheduleQue(@Expiration Date when,
@IntervalCron String cron,
@FinalExpiration Date endDate,
long id) throws IOException;
}
i guess this could be considered mental masterbation at this point, but i though it was pretty awesome idea even if i did come up with it myself. =)
be sure to restrict access to it using drools or something if it isnt interaweb sort of app
i didnt clean up the code either... there are some unused objects in here
@Name("genericProcessing")
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@TransactionTimeout(600)
public class GenericProcessing<T> implements GenericLocal{
/**
* @author nathan dennis
* @unit
* @date
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> T getProperty(Object bean, String propertyName)
{
// System.out.print("inside generic");
if (bean == null ||
propertyName == null ||
propertyName.length() == 0)
{
return null;
}
// --- Based on the property name build the getter method name ---
String methodName=propertyName;
T property = null;
try
{
java.lang.Class c = bean.getClass();
java.lang.reflect.Method m = c.getMethod(methodName, null);
property = (T) m.invoke(bean, null);
}
catch (Exception e)
{
e.printStackTrace();
}
return property;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public boolean classExist(Object bean)
{
// System.out.print("inside generic");
if (bean == null)
{
return false;
}
// --- Based on the property name build the getter method name --
try
{
java.lang.Class c = bean.getClass();
return true;
}catch (Exception e) {
e.printStackTrace();
return false;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public boolean methodExist(Object bean, String propertyName)
{
boolean sw = false;
// System.out.print("inside generic method test");
if (bean == null ||
propertyName == null ||
propertyName.length() == 0)
{
return false;
}
// --- Based on the property name build the getter method name ---
String methodName=propertyName;
T property = null;
try
{
java.lang.Class c = bean.getClass();
//java.lang.reflect.Method m = c.getMethod(methodName, null);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++){
System.out.println(m[i].toString());
if(m[i].getName().equals(methodName)){
sw = true;
}
}
}
catch (Exception e)
{
e.printStackTrace();
// --- Handle exception --
}
return sw;
}
}
and a jsf page something like this to enter in the jobs.. X)
i am using the normal seam-gen structure for listing and reviewing..
<!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:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="../layout/template.xhtml">
<ui:define name="body">
<h:form id="quartzHandles" styleClass="edit">
<rich:panel>
<f:facet name="header">#{quartzHandlesHome.managed ? 'Edit' : 'Add'} Quartz handles</f:facet>
<s:decorate id="idField" template="../layout/edit.xhtml">
<ui:define name="label">Id</ui:define>
<h:inputText id="id"
required="true"
disabled="true"
value="#{quartzHandlesHome.instance.id}">
<a:support event="onblur" reRender="idField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>
<s:decorate id="cronField" template="../layout/edit.xhtml">
<ui:define name="label">Cron</ui:define>
<h:inputText id="cron"
required="true"
value="#{quartzEditAction.cron}">
<a:support event="onblur" reRender="cronField" bypassUpdates="true" ajaxSingle="true"/>
</h:inputText>
</s:decorate>
<s:decorate id="startdateField" template="../layout/edit.xhtml">
<ui:define name="label">Startdate</ui:define>
<rich:calendar id="startdate"
value="#{quartzEditAction.startdate}" datePattern="MM/dd/yyyy hh:mm a"/>
</s:decorate>
<s:decorate id="enddateField" template="../layout/edit.xhtml">
<ui:define name="label">Enddate</ui:define>
<rich:calendar id="enddate"
value="#{quartzEditAction.enddate}" datePattern="MM/dd/yyyy hh:mm a"/>
</s:decorate>
<s:decorate id="handleNameField" template="../layout/edit.xhtml">
<ui:define name="label">Handle name</ui:define>
<h:inputText id="handleName" validator="#{quartzEditAction.classValidator}"
required="true"
value="#{quartzEditAction.handleName}">
<a:support event="onblur" reRender="handleNameField" bypassUpdates="true" ajaxSingle="true">
</a:support>
</h:inputText>
</s:decorate>
<s:decorate id="methodNameField" template="../layout/edit.xhtml">
<ui:define name="label">Method Name</ui:define>
<h:inputText id="methodName" validator="#{quartzEditAction.methodValidator}"
required="true"
value="#{quartzEditAction.methodName}">
<a:support event="onblur" reRender="methodNameField" bypassUpdates="true" ajaxSingle="true">
</a:support>
</h:inputText>
</s:decorate>
<div style="clear:both">
<span class="required">*</span>
required fields
</div>
</rich:panel>
<div class="actionButtons">
<h:commandButton id="save"
value="Create"
action="#{quartzEditAction.create}"
disabled="#{!quartzHandlesHome.wired}"
rendered="#{!quartzHandlesHome.managed}"/>
<h:commandButton id="update"
value="Save"
action="#{quartzEditAction.save}"
rendered="#{quartzHandlesHome.managed}"/>
<h:commandButton id="pause"
value="Pause"
action="#{quartzEditAction.pause}"
rendered="#{quartzHandlesHome.managed}"/>
<h:commandButton id="resume"
value="Resume"
action="#{quartzEditAction.resume()}"
rendered="#{quartzHandlesHome.managed}"/>
<h:commandButton id="delete"
value="Delete"
action="#{quartzEditAction.remove}"
immediate="true"
rendered="#{quartzHandlesHome.managed}"/>
<s:button id="cancelEdit"
value="Go Back (Cancel)"
propagation="end"
view="/quartz/QuartzHandles.xhtml"
rendered="#{quartzHandlesHome.managed}"/>
<s:button id="cancelAdd"
value="Cancel"
propagation="end"
view="/quartz/#{empty quartzHandlesFrom ? 'QuartzHandlesList' : quartzHandlesFrom}.xhtml"
rendered="#{!quartzHandlesHome.managed}"/>
</div>
</h:form>
</ui:define>
</ui:composition>
---pages.xml
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"
no-conversation-view-id="/quartz/QuartzHandlesList.xhtml"
login-required="true">
<begin-conversation join="true" flush-mode="MANUAL"/>
<action execute="#{quartzHandlesHome.wire}"/>
<action execute="#{quartzEditAction.init()}"/>
<param name="quartzHandlesFrom"/>
<param name="quartzHandlesId" value="#{quartzHandlesHome.quartzHandlesId}"/>
<navigation from-action="#{quartzEditAction.create}">
<rule if-outcome="persisted">
<end-conversation/>
<redirect view-id="/quartz/QuartzHandles.xhtml"/>
</rule>
</navigation>
<navigation from-action="#{quartzEditAction.save}">
<rule if-outcome="updated">
<end-conversation/>
<redirect view-id="/quartz/QuartzHandles.xhtml"/>
</rule>
</navigation>
<navigation from-action="#{quartzEditAction.pause}">
<rule if-outcome="updated">
<end-conversation/>
<redirect view-id="/quartz/QuartzHandles.xhtml"/>
</rule>
</navigation>
<navigation from-action="#{quartzEditAction.resume}">
<rule if-outcome="updated">
<end-conversation/>
<redirect view-id="/quartz/QuartzHandles.xhtml"/>
</rule>
</navigation>
<navigation from-action="#{quartzEditAction.remove}">
<rule if-outcome="removed">
<end-conversation/>
<redirect view-id="/quartz/QuartzHandlesList.xhtml"/>
</rule>
</navigation>
</page>
i got tired of making individual asyc beans to handle each job. hence the added complexity with generics.. but write once,, use it everywhere..... if someone knows how to do this in Seam3 / JavaEE6 i would appreciate a help hint
hopefully i didnt fat finger anything copying it over.. should get you up and going in no time.