How to Get and Update Process Variable in jBPM 6 via REST API – Part 1

The GET part is fairly simple, you just need to form a URL with the right values.

So assuming you have a BPM process running on a remote server (business-central) and you would like to get/read the Process Variables, lets see how.

JBPM has published REST APIs to interact with it and get most of the information.

For our case, the REST URL to get the process variable information has this pattern:

/runtime/{deploymentId}/process/instance/{procInstanceID}/variable/{varId}

It’s HTTP GET which returns the variable from a given process instance.

A complete URL may look like below:

http://localhost:8080/business-central/rest/runtime/com.prashantp.demo:bpm-sample:0.1.0/process/instance/4545/variable/person

The above would return the XML value of the variable (person), so if you had a POJO then this can be thought of as the XML version of the same. (JAXB at play).

Writing code to fetch the information can be in plain Java without any BPM libraries, after all its just a REST API which returns XML, the only complexity could be about BASIC AUTH.

Here’s a sample code for retrieving the variable value.


import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.logging.Logger;

import org.apache.commons.codec.binary.Base64;

/**
 * A general Helper class that once configured would return the XML value of a given Variable
 * under a process instance.
 *
 * Sample URL used for REST API of JBPM 6:
 * http://localhost:8080/business-central/rest/runtime
 * /com.infibeam.sapphire:bpm-order-activation:0.3.9
 * /process/instance/4545
 * /variable/person
 *
 * @author prashantp.org
 *
 */
public class BPMGetVariable {
	private Logger logger = Logger.getLogger(BPMGetVariable.class.getName());

	private String variableName;
	private String bpmUrl;
	private String username;
	private String password;

	public BPMGetVariable(long processId, String variableName, String bpmUrl,
			String username, String password, String deploymentId) {
		this.variableName = variableName;
		this.bpmUrl = bpmUrl + deploymentId + "/process/instance/" + processId + "/variable/" + variableName;
		this.username = username;
		this.password = password;
	}

	public static void main(String[] args) throws Exception {
		long processId = 4545;
		String variableName = "person";
		String name = "bpmsAdmin";
		String password = "bpmspasswordhere";
		String deploymentId = "com.prashantp.demo:bpm-sample:0.1.0";

		BPMGetVariable bpmGetVariable = BPMGetVariable.builder()
				.bpmUrl("http://localhost:38080/business-central")
				.credentials(name, password).addDeploymentId(deploymentId)
				.processId(processId).variableName(variableName).build();
		//Save as XML
		Files.write(Paths.get("./" + processId + "_" + variableName + ".xml"),
				bpmGetVariable.getValue().getBytes());
	}

	public String getValue()
			throws MalformedURLException, IOException {

		String variableValue = getFromSecureRESTUrl(bpmUrl, username, password);
		logger.info("Variable Name=" + variableName + ", value=" + variableValue);

		return variableValue;
	}

	private String getFromSecureRESTUrl(String webpageUrl, String username,
			String password) throws MalformedURLException, IOException {
		logger.info(webpageUrl);
		URL url = new URL(webpageUrl);
		URLConnection urlConnection = url.openConnection();
		setSecurityCredentials(urlConnection, username, password);
		return readResponse(urlConnection);
	}

	private void setSecurityCredentials(URLConnection toSecure,
			String username, String password) {
		String authString = username + ":" + password;
		byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());

		toSecure.setRequestProperty("Authorization", "Basic "
				+ new String(authEncBytes));
	}

	private String readResponse(URLConnection urlConnection) throws IOException {
		InputStream is = urlConnection.getInputStream();
		InputStreamReader isr = new InputStreamReader(is);

		int numCharsRead;
		char[] charArray = new char[1024];
		StringBuffer sb = new StringBuffer();
		while ((numCharsRead = isr.read(charArray)) > 0) {
			sb.append(charArray, 0, numCharsRead);
		}
		return sb.toString();
	}

	public static BPMGetVariableBuilder builder() {
		return new BPMGetVariableBuilder();
	}

	static class BPMGetVariableBuilder {
		private long processId;
		private String variableName;
		private String bpmUrl;
		private String username;
		private String password;
		private String deploymentId;

		public BPMGetVariableBuilder processId(long processId) {
			this.processId = processId;
			return this;
		}

		public BPMGetVariableBuilder variableName(String variableName) {
			this.variableName = variableName;
			return this;
		}

		public BPMGetVariableBuilder bpmUrl(String bpmUrl) {
			this.bpmUrl = bpmUrl;
			if (this.bpmUrl.endsWith("/")) {
				this.bpmUrl = this.bpmUrl + "rest/runtime/";
			} else {
				this.bpmUrl = this.bpmUrl + "/rest/runtime/";
			}
			return this;
		}

		public BPMGetVariableBuilder credentials(String name, String password) {
			this.username = name;
			this.password = password;
			return this;
		}

		public BPMGetVariableBuilder addDeploymentId(String deploymentId) {
			this.deploymentId = deploymentId;
			return this;
		}

		public BPMGetVariable build() {
			if (processId == 0 || Objects.isNull(variableName)
					|| Objects.isNull(bpmUrl) || Objects.isNull(username)
					|| Objects.isNull(password) || Objects.isNull(deploymentId)) {
				throw new IllegalStateException();
			}
			return new BPMGetVariable(processId,variableName,bpmUrl,username,password,deploymentId);
		}

	}

}

Now that we have done the GET part how about updating the same Process Variable via the REST API, lets see how.

That’s coming in Part 2 soon.

 

Advertisements

Dealing with Sun JDK related NoClassDefFoundError under Jboss

On a recent server upgrade from Tomcat to Jboss 7 we kept facing issues related to code that made use of sun jdk classes.

Few of Exceptions were:
java.lang.NoClassDefFoundError: com/sun/net/ssl/internal/ssl/Provider
java.lang.NoClassDefFoundError: sun/net/www/protocol/http/Handler

JBoss was not kind enough to allow such references which tomcat or prior Jboss servers were happy to serve!

The solution:

JBoss has a module “sun.jdk” which is used to load the jdk classes. For some reason not all classes are referenced.

In order to allow for the sun or com related jdk packages which are not getting loaded we need to register them under the “sun.jdk” module.

So, first step is to register the various sun/jdk packages that your application is going to depend upon.

This is done by adding few entries into the module.xml found here:

Eg: jboss-as-7.1.1.Final/modules/sun/jdk/main/module.xml

The complete module.xml for reference:

<module xmlns="urn:jboss:module:1.1" name="sun.jdk">
    <resources>
        <!-- currently jboss modules has not way of importing services from
        classes.jar so we duplicate them here -->
        <resource-root path="service-loader-resources"/>
    </resources>
    <dependencies>
        <system export="true">
            <paths>
                <path name="com/sun/script/javascript"/>
                <path name="com/sun/jndi/dns"/>
                <path name="com/sun/jndi/ldap"/>
                <path name="com/sun/jndi/url"/>
                <path name="com/sun/jndi/url/dns"/>
                <path name="com/sun/security/auth"/>
                <path name="com/sun/security/auth/login"/>
                <path name="com/sun/security/auth/module"/>
                <path name="sun/misc"/>
                <path name="sun/io"/>
                <path name="sun/nio"/>
                <path name="sun/nio/ch"/>
                <path name="sun/security"/>
                <path name="sun/security/krb5"/>
                <path name="sun/util"/>
                <path name="sun/util/calendar"/>
                <path name="sun/util/locale"/>
                <path name="sun/security/provider"/>
		<!-- Registered additional packages below which are not enabled by default -->
		<path name="sun/net/www/protocol/https"/>
		<path name="sun/net/www/protocol/http"/>
		<path name="com/sun/net/ssl/internal/ssl"/>
		<!-- Additional package additions ends here -->

                <path name="META-INF/services"/>
            </paths>
            <exports>
                <include-set>
                    <path name="META-INF/services"/>
                </include-set>
            </exports>
        </system>
    </dependencies>
</module>

Note: In this example I have added the below three packages only:

<path name="sun/net/www/protocol/https"/>
<path name="sun/net/www/protocol/http"/>
<path name="com/sun/net/ssl/internal/ssl"/>

Ok, do not rush off to start the server yet, there is one more part remaining to get rid off that nasty NoClassDefFoundError.
Now, we need to reference the “sun.jdk” module.
There are two options we have now:

Approach 1
Reference the module (“sun.jdk”) from your web application by creating a jboss-deployment-structure.xml under your applications WEB-INF/ folder.
Sample:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
    <deployment>
        <dependencies>
                <module name="sun.jdk"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Approach 2
Reference the module (“sun.jdk”) globally which makes it available to any application deployed on the server. With this you can ignore the custom jboss-deployment-structure.xml (No longer required to be put in each of your web applications).
For this you have to edit the standalone.xml and register the module globally.
Find the tag:

<subsystem xmlns="urn:jboss:domain:ee:1.0"/>

Then change it with this:

<subsystem xmlns="urn:jboss:domain:ee:1.0">
   <global-modules>
      <module name="sun.jdk" slot="main" /> 
   </global-modules> 
</subsystem>

With either of the above two approaches done, you can go ahead and start up your server.
You should no longer see the NoClassDefFoundError for the packages that you have registered.

References that helped me arrive at this solution:
http://www.mastertheboss.com/jboss-server/jboss-as-7/how-to-install-a-module-on-jboss-as-7
https://developer.jboss.org/thread/171932?tstart=0

Setting up Java developmment environment in Ubuntu – Gnome

Having struggled to set up a working and decent looking Java environment in Ubuntu (Linux), here’s my attempt at collating few steps/tweaks in one place.

These steps are scattered out there on the net, but it’s helpful to have it in one post.

On a freshly installed Ubuntu system these are the steps used to get up and running with Java development:

Hmm, but wait before diving into the Java specifics I first like to set the fonts a little smaller to my liking.

Using gnome-tweak-tool I prefer to set the fonts to something like the below:

Image

*Note Consolas may not be available on your system, so use something that you prefer for a terminal/editor font.

Setting up the JDK

Next, since I prefer Oracle JDK over the openjdk version I will go ahead and install that.

Unlike the openjdk* option, oracle jdk isn’t readily available in the ubuntu repo, so first add the PPA using the below:

sudo add-apt-repository ppa:webupd8team/java

sudo apt-get update

sudo apt-get install oracle-java7-installer

To set few environment variables just run this:

sudo apt-get install oracle-java7-set-default

To confirm the installation worked.
Run

java -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)

Setting up Eclipse

Download eclipse from eclipse.org

At the time of this writing the latest for linux (64bit) version is:

eclipse-jee-kepler-SR2-linux-gtk-x86_64.tar.gz

Once downloaded extract it into your preferred location!

Run eclipse by going to eclipse home directory (extracted folder location) and you should have a working eclipse ide!

Making Eclipse look Good in Linux/ Ubuntu

Now, there is one more bit of tweak needed to make this eclipse look good on ubuntu (By default the font/tabs would appear bigger than the windows counterpart).

To make Eclipse look more compact use .gtkrc-2.0 file as described below.

Create a file named .gtkrc-2.0 in your ~ (Home directory) with the following contents:

style "gtkcompact" {
GtkButton::default_border={0,0,0,0}
GtkButton::default_outside_border={0,0,0,0}
GtkButtonBox::child_min_width=0
GtkButtonBox::child_min_heigth=0
GtkButtonBox::child_internal_pad_x=0
GtkButtonBox::child_internal_pad_y=0
GtkMenu::vertical-padding=1
GtkMenuBar::internal_padding=0
GtkMenuItem::horizontal_padding=4
GtkToolbar::internal-padding=0
GtkToolbar::space-size=0
GtkOptionMenu::indicator_size=0
GtkOptionMenu::indicator_spacing=0
GtkPaned::handle_size=4
GtkRange::trough_border=0
GtkRange::stepper_spacing=0
GtkScale::value_spacing=0
GtkScrolledWindow::scrollbar_spacing=0
GtkTreeView::vertical-separator=0
GtkTreeView::horizontal-separator=0
GtkTreeView::fixed-height-mode=TRUE
GtkWidget::focus_padding=0
}
class "GtkWidget" style "gtkcompact"

style "compact-toolbar"
{
GtkToolbar::internal-padding = 1
xthickness = 2
ythickness = 2
}

style "compact-button"
{
xthickness = 1
ythickness = 1
}

class "GtkToolbar"                   style "compact-toolbar"
widget_class "*&lt;GtkToolbar&gt;*&lt;GtkButton&gt;"    style "compact-button"

Now restart eclipse and your IDE should look a little better!

To reduce the tab size of Eclipse you will need to edit a file which is found under eclipse as described below:

Find the file called e4_default_gtk.css under your eclipse installation folder:

Example location: eclipse/plugins/org.eclipse.platform_<ur eclipse version>/css/e4_default_gtk.css

Change the .MPartStack font-size from 11 or whatever value to a 9

Example:

.MPartStack {
	font-size: 9;
	swt-simple: false;
	swt-mru-visible: false;
}

Before all the tweaks how Eclipse appears:

Image

After the tweaks applied:

Image

That should complete the settings needed to have a working (and good looking in my opinion) Eclipse – Java development environment in Ubuntu.

I have been using ubuntu 13 and Ubuntu 14 versions so far and the options above have worked for me.

Hope this helps someone!

REST API exception handling using JAX RS in JEE 6

It’s considered a good practice to use HTTP Status Codes in RESTful APIs for sharing the outcome of an API call.

There are plenty of HTTP Status codes to make use of. Unfortunately complex cases lead developers to returning custom errors rather than sticking to the HTTP standards. But there is a way to stick to the standard and still make use of custom errors as per the application needs. Let’s see an approach using JAXRS Exception mapping providers.

  • For simple requests the service may indeed return a single HTTP response code which makes sense.
  • But for complex requests which fail a more detailed error list (business specific) may be desired.

Let’s start building a JAXRS RESTful service to handle the simple case first and then see what we can do to handle the complex case later using the Exception mapping providers.

The code is available here https://github.com/prashantpro/jaxrs-movie-service

Scenario

Consider a movie library service which returns the list of movies released on a given year.

Resource

Method: GET

URL: /movie/{year}

Parameters: Year

Response HTTP Status Code Description
Success 200 – Ok Movie list is returned
Input Invalid 400 – Bad Request The input year maybe invalid
Failure 500 – Internal Error Uncaught exception occurred such as RuntimeException

Setup a Web project

Create a dynamic web project in eclipse and name it jaxrs-movie-service. I have used a JEE 6 server i.e. JBoss 7.

Note: You can use maven and create a maven-archetype-webapp project as well. We won’t need any jars in our lib as JAXRS is part of JEE 6.

Deployment descriptor updates

Update web.xml to match the below

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>jaxrs-movie-service</display-name>
	
	<servlet-mapping>
		<servlet-name>javax.ws.rs.core.Application</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

Create an empty beans.xml under WEB-INF folder. (This will enable CDI capabilities which can be made use of if desired).

The source code

Let’s define a POJO which will hold the movie information.

package org.prashantpro.jaxrs.movie;

/**
 * @author Prashant Padmanabhan <https://javaspecialist.wordpress.com>
 *
 */
public class Movie {

	private int year;
	private String title;
	private String genre;

	public Movie(int year, String title, String genre) {
		this.year = year;
		this.title = title;
		this.genre = genre;
	}
	//Getters and setters left out for brevity.
	...
}

Next, create the REST service which will expose the movie libraries movie list. Here we aren’t using any checked business exception handlers. This is a simple service which returns the HTTP Status code based on conditional constructs.

package org.prashantpro.jaxrs.movie;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
 * @author Prashant Padmanabhan <https://javaspecialist.wordpress.com>
 *
 */
@Path("/movie")
@Produces(MediaType.APPLICATION_JSON)
public class MovieLibrary {
	//Store the in memory movie list as our repository.
	static final List MOVIE_LIST = new ArrayList();

	//Build a dummy list of movies to work with.
	static {
		MOVIE_LIST.add(new Movie(1971,"Dirty Harry","Action"));
		MOVIE_LIST.add(new Movie(2008,"Gran Torino","Drama"));
		MOVIE_LIST.add(new Movie(2012,"Argo","Drama"));
	}

	@GET
	@Path("/{year}")
	public Response getMovies(@PathParam("year") int year) {
		if(year < 1880 || year > 9999) {
			//Invalid input for year so return HTTP Status 400
			return Response.status(Response.Status.BAD_REQUEST).build();
		}
		List list = getMoviesByYear(year);
		return Response.status(Response.Status.OK).entity(list).build();
	}

	private List getMoviesByYear(int targetYear) {
		List found = new ArrayList();
		for(Movie movie : MOVIE_LIST) {
			if(movie.getYear() == targetYear)
				found.add(movie);
		}
		return found;
	}

}

Once the above code is deployed as a WAR file in a JEE 6 server such as JBoss 7, we can then hit the URL:

http://localhost:8080/jaxrs-movie-service/movie/1971

Where;

jaxrs-movie-service – Is the context name of our application

/movie – Is the REST resource identified by @Path(“/movie”)

1971 – Is the input which maps to the getMovies method via @Path(“/{year}”)

The above should result in the below response:

Response headers:

  • Status Code: 200 OK
  • Content-Type: application/json

Response body:

[{"year":1971,"title":"Dirty Harry","genre":"Action"}]

This was simple to begin with, but in real world we would have some facade or service layer which would do the processing. The facade may throw business exceptions which would then result in passing a different response code with an appropriate message.

The complex case discussed below explains the same.

Complex case

Consider the movie library service would also require to list out movies based on a genre in the given order of ASC or DESC.

For simplicity let’s say we have only two genres “Action” and “Drama“.

In case of any problem the detailed message must be returned by the REST Service. This means we need to return some error messages along with the right HTTP Status code.

We can start throwing business exceptions but these won’t get translated to an appropriate HTTP Response code.

Here, we make use of Exception Mapping providers. These map a checked or runtime exception to an instance of Response. An exception mapping provider implements the ExceptionMapper<T> interface and is annotated with @Provider.

Resource

Method: GET

URL: /movie/list

Parameters: order, genre

Response HTTP Status Code Description
Success 200 – Ok Movie list is returned
Input Invalid 400 – Bad Request Service must return the details of what went wrong.Example genre specified was wrong.Sort order related issue.

Let’s create a application exception which would contain the error details.

package org.prashantpro.jaxrs.movie;

import java.util.List;

/**
 * @author Prashant Padmanabhan <https://javaspecialist.wordpress.com>
 *
 */
public class BusinessException extends Exception {

	private static final long serialVersionUID = 1L;

	private List messages;

	public BusinessException(List messages) {
		super();
		this.messages = messages;
	}

	public List getMessages() {
		return messages;
	}
}

Now, the Exception Mapper which will map our Business Exception to the correct Response code.

package org.prashantpro.jaxrs.movie;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * @author Prashant Padmanabhan <https://javaspecialist.wordpress.com>
 *
 */
@Provider
public class ExceptionHttpStatusResolver implements
		ExceptionMapper {

	@Override
	public Response toResponse(BusinessException exception) {
		Response.Status httpStatus = Response.Status.INTERNAL_SERVER_ERROR;

		if (exception instanceof BusinessException)
			httpStatus = Response.Status.BAD_REQUEST;

		return Response.status(httpStatus).entity(exception.getMessages())
				.build();
	}
}

The MovieLibrary.java gets a new method as shown below:

	@GET
	@Path("/list")
	public Response getMoviesByGenre(@QueryParam("order") String order,@QueryParam("genre") String genre) throws BusinessException {
		List errorMessages = new ArrayList();

		if(order == null || order.length() == 0) {
			errorMessages.add("order is required");
		}

		if(genre == null || genre.length() == 0) {
			errorMessages.add("genre is required");
		}

		if(!"ASC".equals(order) && !"DESC".equals(order)) {
			errorMessages.add("order of either ASC or DESC must be specified");
		}

		if(!"Action".equals(genre) &&  !"Drama".equals(genre)) {
			errorMessages.add("genre of either Action or Drama must be specified");
		}
		if(!errorMessages.isEmpty()) {
			throw new BusinessException(errorMessages);
		}

		List list = listMoviesByGenre(genre,order);
		return Response.status(Response.Status.OK).entity(list).build();
	}

	private List listMoviesByGenre(String genre, String order) {
		//Just return the list as is as this is a demo
		//We would use some logic to do the filtering and ordering in real world apps.
		return MOVIE_LIST;
	}

If you invoke the service without any of the required parameters, it would return the HTTP status code of 400 along with the detailed messages. This works because our BusinessException thrown during validation checks is processed by the ExceptionHttpStatusResolver which translates the exception to the correct Response code.

When a resource method throws an exception for which there is an exception mapping provider, the matching provider is used to obtain a Response instance. The resulting Response is processed as if the method throwing the exception had instead returned the Response.

Example invocation URL:
http://localhost:8080/jaxrs-movie-service/movie/list?order=dummy

The above should result in the below response:

Response headers:

  • Status Code: 400 Bad Request
  • Content-Type: application/json

Response body:

[
"genre is required",
"order of either ASC or DESC must be specified",
"genre of either Action or Drama must be specified"
]

Conclusion

Thus we can make use of JAXRS Exception Mapping providers for returning HTTP Status codes and add the needed error details in the response body as well.

Restoring ubuntu grub in dual boot

With the proliferation of linux variants out there, i couldn’t resist the temptation to try out yet another one of the ubuntus, mints, or the fedoras…

But installing a second/third OS next to the default meant loosing out on the already installed GRUB/Boot options during a dual boot.

Note: The scenario below is with all boot options still appearing. Not where your boot option is missing, there are many posts out there which explain that using live cd/usb.

Here’s what I had: Windows 7, Ubuntu 11.10. (Dual boot).

Now, I installed Linux Mint 12 after it’s recent jump in rankings. But this changed the GRUB of ubuntu (earlier one) to the one provided by Mint with default choice as Mint.

So i booted in ubuntu and just fired off these commands in sequence.

sudo update-grub

sudo grub-install /dev/sda

That’s it! rebooted and voila the purple GRUB2 loader was back 🙂

 

 

 

 

Tips for SEO of your site with Richfaces

In today’s internet world getting ranked high on various search engine results
matter a lot. Cooking up the best site and having the right content for the users
isn’t good enough. For starters your site needs to be found in search engine
results, and once found it needs to load up quickly. Users won’t necessarily wait
for a slow loading site, especially when their web browsers come with an empty cache.

When a page loads whether it’s a Home page or another static page of a site,
the number of scripts/css on that page will dictate the page load time.
It would therefore make sense to remove or minimize the scripts that your page
needs. A lot of scripts at the beginning of a page is bad for your prospects at being
number one in search results. When using Ajax libraries like Richfaces the problem
just adds up. Not only do you have to take care of your own scripts but scripts and
css contributed by Ajax libraries as well.

Richfaces outputs lot of scripts and css which may not be ideal from
a SEO perspective.
For example Richface 3.3.x puts around 21 script tags and 12 link tags (css).
Some of the tips mentioned below should help your projects and thus I’m putting it out here for all.

Solution:
Luckily you can configure (upto certain extent) the number of scripts/css Richfaces
adds to your page.

The following parameter in web.xml will enforce Richfaces scripts and css to be
loaded as a pack (Single file rather than multiple files).

<!-- Change load strategy to ALL to send scripts/styles as packs -->    
	<context-param>
		<param-name>org.richfaces.LoadStyleStrategy</param-name>
		<param-value>ALL</param-value>
	</context-param>
	<context-param>
		<param-name>org.richfaces.LoadScriptStrategy</param-name>
		<param-value>ALL</param-value>
	</context-param>

This would result in the below includes, which is a lot less than the default strategy
of 21 scripts and 12 links.

<link rel='stylesheet' class='component' type='text/css'
	href='/richdemo/a4j_3_2_1-SNAPSHOTorg/richfaces/skin-ext-classes.xcss/DATB/eAELXT5DOhSIAQ.sA18_' />
<script type='text/javascript'
	src='/richdemo/a4j_3_2_1-SNAPSHOT/org/ajax4jsf/framework.pack.js'></script>
<script type='text/javascript'
	src='/richdemo/a4j_3_2_1-SNAPSHOT/org/richfaces/ui.pack.js'></script>

With 3.2.x library
ui.pack.js is 367 KB
framework.pack.js is 280 KB

Which is pretty reasonable, But
with 3.3.x library
ui.pack.js is 661.5 KB
framework.pack.js is 311.7 KB

Sadly that’s pretty close to 1MB of plain scripts. That’s part and parcel of the framework.

If you don’t use the LoadScriptStrategy/All setting, you could enable JavaScript
compression for a4j by setting the below param in web.xml
(This isn’t same as gzip compression for http).

  <context-param>
    <param-name>org.ajax4jsf.COMPRESS_SCRIPT</param-name>
    <param-value>true</param-value>
  </context-param>

Custom Scripts and CSS
Now with that taken care what remains is your own scripts and css.
There are some nice resources on the net that will help you solve this part,
so I wont duplicate this information. You can find some pointers here
http://developer.yahoo.com/performance/rules.html.

Also consider using a compression tool to reduce the size of scripts and css,
you could use the YUI Compressor to reduce file sizes (http://developer.yahoo.com/yui/compressor/). It helped in some of my projects to a great extent.
My personal favourites are CSS Sprites and Compression.

Hope this bit of information helps you guys!

Performance tuning of Seam, JSF, Richfaces for webapps

Apart from following best practices while coding an application, we often come across the need to tune it up just a bit further to get that peek performance going.
On a recent project we faced some challenges in trying to increase the performance of our web application. We finally ended up optimizing the app and got very promising results. Here I put together some of the information used by us in one location, which is scattered over the web.

The frameworks used were, Seam 2.2 and JSF 1.2 with Richfaces 3.2 and also Spring 2.5. Now without further delay here you go..

Some pointers on using JSF beans mapped in your view layer (JSP/XHTML when using facelets).

  1. Don’t put code in a getter (accessor method) of a managed bean
    The getters are invoked frequently to get the data. You could use some other method to fetch data and simply update the property.
    But if you did end up with few getters which hit the database for data loading, then consider putting a condition around that code to load data only if required.
    Example: Change code like the below;

    private List aList;
    public List getList() {
    aList = getServiceObject().loadListFromDB();
    return aList;
    }
    

    To load data conditionally as given below;

    private List aList;
    public List getList() {
    if( null == aList || aList.isEmpty() ) {
    //Load from DB only if list is empty
    aList = getServiceObject().loadListFromDB();
    }
    return aList;
    }
    
  2. Check scope of your beans. Seam provides a Conversation Scope use it instead of Session Scope if practical enough for your requirement.
  3. Richfaces does provide a huge set of components which are Ajax based.
    But many of those components also add to the processing overhead. Use them judiciously or it can affect your page load time pretty badly.
    Consider these points when trying to optimize a JSF/Richfaces application.

    • Avoid using rich:dataGrid or rich:dataTable whenever you can. The h:dataTable of JSF usually provides good enough options.
    • Do not use rich:tooltip inside a data table or grid. It’s very expensive and can lead to very slow rendering especially on IE.
      Also avoid nesting too many rich:xxx components inside a dataTable.
    • Use ajaxSingle=true attribute and a4j:region tag whenever possible. Also consider using limitToList attribute when reRendering.
    • Do not use multiple rich:modalPanel components for each view consider using one single rich:modalPanel with dynamic view.You could use a4j:include or ui:include (facelets) to change the view inside a modal panel.

Apart from following the above practices here are some goodies which will speed up your JSF with Richfaces webapp.
Add the below snippets to your web.xml

  • Use a global queue. (This is not strictly required if you are already using queuing of ajax requests)
    <context-param>
    <param-name>
     org.richfaces.queue.global.enabled
    </param-name>
    <param-value>true</param-value>
    </context-param>
    
  • Use NEKO parser instead of the default. This will speed up things.
    <context-param>
     <param-name>
      org.ajax4jsf.xmlparser.ORDER
     </param-name>
     <param-value>
      NONE,NEKO,TIDY
     </param-value>
    </context-param>
    
    <context-param>
    <param-name>org.ajax4jsf.xmlparser.NEKO</param-name>
    <param-value>.*\..*</param-value>
    </context-param>
    
  • Change the way css and java script files are loaded.
    If you view the source of your page you will notice that by default richfaces will add some includes of css and scripts which lead to multiple calls to load.
    For example with our web app we had like 8 css files and 23 js scripts getting loaded separately.
    and with the below setting our pages had just 1 css include and 2 script includes.

    <context-param>
    <param-name>
    org.richfaces.LoadStyleStrategy
    </param-name>
    <param-value>ALL</param-value>
    </context-param>
    
    <context-param>
    <param-name>
    org.richfaces.LoadScriptStrategy
    </param-name>
    <param-value>ALL</param-value>
    </context-param>
    
    <!--  If you use LoadScriptStrategy ALL, turn the compression off  -->
    <context-param>
    <param-name>
    org.ajax4jsf.COMPRESS_SCRIPT
    </param-name>
    <param-value>false</param-value>
    </context-param>
    

    Using the above settings we had reduced the css and js files to be loaded:

    <link rel='stylesheet' class='component' type='text/css'
            href='/excel/a4j_3_2_1-SNAPSHOTorg/richfaces/skin-ext-classes.xcss/DATB/eAELXT5DOhSIAQ.sA18_'
    />
    <script type='text/javascript'
            src='/excel/a4j_3_2_1-SNAPSHOT/org/ajax4jsf/framework.pack.js'></script>
    <script type='text/javascript'
            src='/excel/a4j_3_2_1-SNAPSHOT/org/richfaces/ui.pack.js'></script>
    
  • For Facelets and JSF RI from Sun you can use the below settings:
    <!--  Facelets tuning -->
    <context-param>
    <param-name>
    facelets.RECREATE_VALUE_EXPRESSION_ON_BUILD_BEFORE_RESTORE
    </param-name>
    <param-value>false</param-value>
    </context-param>
    
    <context-param>
    <param-name>
    facelets.BUILD_BEFORE_RESTORE
    </param-name>
    <param-value>false</param-value>
    </context-param>
    
    <context-param>
    <param-name>facelets.DEVELOPMENT</param-name>
    <param-value>false</param-value>
    </context-param>
    
    <context-param>
    <param-name>facelets.REFRESH_PERIOD</param-name>
    <param-value>-1</param-value>
    </context-param>
    
    <!--  JSF RI Performance tuning -->
    <context-param>
    <param-name>com.sun.faces.responseBufferSize</param-name>
    <param-value>500000</param-value>
    </context-param>
    
    <context-param>
    <param-name>com.sun.faces.verifyObjects</param-name>
    <param-value>false</param-value>
    </context-param>
    
    <!-- Allows the JavaScript to be cached -->
    <context-param>
    <param-name>
    com.sun.faces.externalizeJavaScript
    </param-name>
    <param-value>true</param-value>
    </context-param>
    

    Only if you are running under JBoss AS

    <context-param>
    <param-name>
    com.sun.faces.serializationProvider
    </param-name>
    <param-value>
    org.jboss.web.jsf.integration.serialization.JBossSerializationProvider
    </param-value>
    </context-param>
    

When tuning your seam application you can use the components.xml file to tweak some settings.

  • Cache images and style globally.
    <web:cache-control-filter name="imageCacheControlFilter"
    regex-url-pattern=".*(\.gif|\.png|\.jpg|\.jpeg)"
    value="max-age=86400"/>
    
    <web:cache-control-filter name="textCacheControlFilter"
    regex-url-pattern=".*(\.css|\.js)"
    value="max-age=1400"/>
    
  • Also if you are using richfaces with Seam you could configure the richfaces filter  in components.xml itself
    without needing to declare the filter in web.xml
    Disable the forceparser and turn on caching.

    <web:ajax4jsf-filter force-parser="false"
    enable-cache="true"
    url-pattern="*.jsf"/>
    

So in the end after optimizing the webapp by using framework provided settings, you can expect a lot better performance from your app. In the end you get happy visitors. Hope this bit of information helps you guys.

Jboss 5.1 and Seam 2 Redeployment issue workaround

When trying to work with Seam 2.2 applications in JBoss 5.1 you might have come across a very annoying redeploy cycle of your application. I will document the issue and workaround here.
Beginning with Seam 2.2 and JBoss 5.1 is pretty straight forward. But you could soon run into issues if you don’t pay attention to the details.

The Journey

  1. I had decided to give Seam a try after having dig into some seam docs. The first thing was to try out the examples that come bundled with seam.
  2. Seam is pretty easy to start off with. You just need to get a copy of JBoss 5.1 and Seam 2.2 (the latest during this post). Then as mentioned in the Running Seam in Jboss Just do the below two steps:
    1. Edit build.properties in your Seam directory and configure the jboss.home property to be your JBoss AS installation directory.
    2. In the jboss-seam/examples/booking directory, type ant deploy. (Here booking is one example you can do it with any other)
  3. ant deploy command will generate the (War/Ear/Ejb) artifacts and dump it into the deploy folder of JBoss AS.
  4. If JBoss is running you should see the console output showing the deployment taking place.
  5. Just point your browser to the deployed apps URL and voila.

When you start building your own application using either the seam commands or Jboss Tools plugin for Eclipse, you will notice quite a few artifacts getting generated (Not all are needed).

When you deploy your project and find that none of the @Name annotated classes are getting found. Check for seam.properties file this must be in your classpath. It’s an empty file but acts like a marker which triggers the scanning of your seam components. Without that file you will need to register each component in your components.xml file.

This should solve your problem, but under JBoss 5.1 you might run into another issue of constant undeploy/deploy of your project without any details as to why in the logs. If you look in the Seam documentation WhatHappensWhenYouDeploySeamAppInJBoss5 and couple of other JBoss JIRA pages you will find that mostly this happens due to IDE generated files getting placed under WEB-INF check for these and delete them. The files get generated when you open the pages.xml or web.xml kind of files in Visual Editor.

Even after following the simple & complex advice the problem sometimes just won’t go. What i noticed was that the presence of seam.properties file under WEB-INF/classes will also trigger the dreadful redeployment of your application. If you look in the seam reference docs for possible locations of seam.properties, you will find that it needs to be in your classes/ root or META-INF directory of a JAR. So an easier solutions is to just “move the seam.properties from WEB-INF/classes to WEB-INF/classes/META-INF” and voila the problem should be solved without any fancy modification to your JBoss AS.

I hope this helps you to get on with building your Seam applications.