Starting a jBPM pageflow

This week i struggled a bit to get a jBPM pageflow running from my seam-gen application. It’s one of the examples in the Seam in Action book from Manning. The goal is to kick off a wizard containing a couple of screens for entering a new entity (in this case a golf course) in the database. As the wizard adds a new record in the database, one of the requirements in the application is that the user doing this is logged in.

There are basically 2 ways to accomplish this. You can start the pageflow in the first page of the wizard during its Render Response phase by making use of a Seam factory. Your other option is to kick off the pageflow from an action method. The second method seems the more natural to me and is the one that gave me some headaches. I’ll shortly describe both the options and provide a simple solution to the issues regarding the second option.

Example

A golf facility can contain multiple golf courses. To add a golf course to a facility you can launch a course wizard which is linked to a jBPM pageflow.

Launching the course wizard from the Facility page.

In this flow we have a facility page /Facility.xhtml which is bound to the Seam component facilityHome and the first page of the course wizard /coursewizard/BasicCourseInfo.xhtml which is bound to the Seam components courseWizard and course (which contains the actual course being added). The pageflow is defined in the jPDL file courseWizard-pageflow.jpdl.xml.

Adding a course to the database requires a user that’s logged in. This restriction is applied in the general page descriptor pages.xml by the following XML stanza

<page view-id="/coursewizard/*" login-required="true"/>

Starting the wizard on its first page

In this option, the Add Course… button on the facility page contains navigation to the first page of the wizard and passes the id of the current facility:

<s:button id="courseWizard" value="Add course..."
 propagation="none" view="/coursewizard/BasicCourseInfo.xhtml">
	<f:param name="facilityId" value="#{facilityHome.instance.id}" />
</s:button>

The input components on the /coursewizard/BasicCourseInfo.xhtml page are all bound to a conversation scoped Seam component named course. This component is created by a factory in the courseWizard component. The facility is weaved in by a request parameter:

@Name("courseWizard")
@Scope(ScopeType.CONVERSATION)
public class CourseWizard implements Serializable {
    @In
    protected EntityManager entityManager;
    @RequestParameter
    protected Long facilityId;
    @Out
    protected Course course;
    ....

    @Begin(pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
    @Factory("course")
    public void initCourse() {
        course = new Course();
        course.setFacility(entityManager.find(Facility.class, facilityId));
        // setup some defaults
        course.setNumHoles(18);
        course.setFairways("BENT");
        course.setGreens("BENT");
    }
    ....
}

Finally the pageflow is started in the jPDL file

<?xml version="1.0" encoding="UTF-8"?>
<pageflow-definition xmlns="http://jboss.com/products/seam/pageflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.com/products/seam/pageflow http://jboss.com/products/seam/pageflow-2.0.xsd"
	name="Course Wizard">
	<start-page name="basicCourseInfo" view-id="/coursewizard/basicCourseInfo.xhtml"
		no-conversation-view-id="/CourseList.xhtml">
		<redirect />
		<transition name="cancel" to="cancel" />
		<transition name="next" to="description" />
	</start-page>
	....
</pageflow-definition>

And that’s all there is to it. You click on the Add course… button, the login screen appears (if you’re not logged in) and after successfully logging into the application the first page of the course wizard appears.

Starting the wizard from an action method

Now for this second option the Add course… button will be coupled to an action method which will launch the pageflow.
Once again the code behind the button, which now contains the action method but doesn’t contain navigation anymore. The facilityid parameter isn’t needed anymore as it’s weaved in before the wizard is launched.

<s:button id="courseWizard" value="Add course..."
	action="#{courseWizard.addCourse}" propagation="none">
</s:button>

Now the action method will take care of launching the pageflow. It also weaves the facility into the course to be created.


@Name("courseWizard")
@Scope(ScopeType.CONVERSATION)
public class CourseWizard implements Serializable {

	@In
	protected EntityManager entityManager;
	@RequestParameter
	protected Long facilityId;
	@Out
	protected Course course;
	....

	@Begin(join = true, pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
	public void addCourse() {
		course = new Course();
		course.setFacility(entityManager.find(Facility.class, facilityId));
		// setup some defaults
		course.setNumHoles(18);
		course.setFairways("BENT");
		course.setGreens("BENT");
	}
	....
}

Because you start the pageflow from an action method, you have to define a start-state element in your jPDL file as opposed to the start-page element in the previous example.

<?xml version="1.0" encoding="UTF-8"?>
<pageflow-definition xmlns="http://jboss.com/products/seam/pageflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.com/products/seam/pageflow http://jboss.com/products/seam/pageflow-2.0.xsd"
	name="Course Wizard">
	<start-state>
		<transition to="basicCourseInfo" />
	</start-state>

	<page name="basicCourseInfo" view-id="/coursewizard/basicCourseInfo.xhtml"
		no-conversation-view-id="/CourseList.xhtml">
		<redirect />
		<transition name="cancel" to="cancel" />
		<transition name="next" to="description" />
	</page>
	....
</pageflow-definition>

Now, when clicking the Add course… button when you’re not logged in yet, the login screen will NOT be displayed. Instead you’ll get the following error when using firefox

The page isn't redirecting properly
Firefox has detected that the server is redirecting the request for
this address in a way that will never complete.

Redirect error

If  firebug is enabled, you can see that the system gets into some sort of redirect loop. I tried a lot of things to fix this issue and eventually stumbled upon a solution which is really very simple. You just have to make sure that the user is logged in before the pageflow gets called. This is done by securing the action method which calls the pageflow. Just add a Restrict annotation.

	@Begin(join = true, pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
	@Restrict("#{identity.loggedIn}")
	public void addCourse() {
		course = new Course();
		course.setFacility(entityManager.find(Facility.class, facilityId));
		// setup some defaults
		course.setNumHoles(18);
		course.setFairways("BENT");
		course.setGreens("BENT");
	}

And that’s it. Now the solution with the action method will behave exactly the same as the solution without it. You click the Add course… button, the login screen appears and after successfully logging in, the first page of the course wizard appears.

Advertisements

About Roger Goossens
I'm an integration consultant with a strong affiliation for JEE and open source development.

One Response to Starting a jBPM pageflow

  1. Thank you!! I was having a nightmare getting a jBPM pageflow working from an action method. Without this post I wouldn’t have got there. Now it’s working perfectly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: