Wednesday, July 29, 2009

Maven Profile- Dynamic compilation for different target platforms

Have you ever had the case where you have one project that has to be deployed to different servers? You certainly know that different servers include different JAR files. You certainly know that the configuration files are not necessary the same. This can give you a lot of headache, it can also make you maintain two different project just cause the configuration.

Well I recently had similar problems. Had to maintain the same project that should be deployed to JBoss and WebSphere. To keep the long story short, Maven gave me the perfect opportunity to do this.

Let us assume that your Maven project looks similar to this:

+main-project
|
|--+ web-app-project
| |
| |-+src/main/java
| |-+src/main/webapp
| | |
| | +WEB-INF
| | |--+web.xml
| | |--+faces-config.xml
| |
| +pom.xml
+ pom.xml

As you may know the main-project pom.xml is where you keep your dependency management. In the dependency management you enter artifact id and group id of the JAR file you want to be inluded and it's version. Usually, you can define the scope of the JAR here. There are several of such scopes. The compile scope is default and means that your JAR will be included in your application. The other common scope is the provided scope. It tells maven, that this JAR should not be included in your final product, and the application will find it either on the server or it's runtime. This dependency management in your main pom.xml should look something like this:





javax.faces
jsf-impl
${jsf.version}






The dependency management tag will tell maven which JAR files and it's versions are needed in your project. But this won't make this Jars be yet included in your project. To indeed include them, you should put the same dependency's with out version between separate dependencies tag.

This you can do either in the main-project pom.xml or in the web-app-project pom.xml. I would suggest the later one, which will make Maven put those Jars under the WEB-INF/lib folder.

Your web-app-project pom.xml could than include those dependencies as follow:




javax.faces
jsf-impl





Notice that the dependency management tag is missing in this part of the configuration.

I must admit that this is little error prone code, but if you keep it nice and clear, it will allow you to manage your dependencies very easy, and if you store your project in a SVN Repository, you will have no need to store the Jar files into the repository.

Thus far, we have gone through basic maven configuration. You certainly are interested how all those Jar files can be managed for different servers.

The answer is maven profiles. Every maven profile needs unique profile id. Let's start with the maven command line that will call certain profile.

mvn -Pprofileid clean install
The accent is set to the parameter -P followed with the profile id. It is important to understand that there is no space between the parameter and the id. If you execute this maven command at this point having the above configuration, the profile will be ignored and the project will be compiled normally.

To add a profile for certain project, you can do this in your web-app-project pom.xml as follows:




websphere



This defines the new profile with id websphere. It can be called as follows:
mvn -Pwebsphere clean install
As you correctly assumed this is a empty profile, thus won't do any difference. But before we go on and add our WebSphere dependencies, remove the previous dependencies we added above. Now adding the WebSphere dependencies is only a trivial task, and is done as follow:




websphere


javax.faces
jsf-impl







As you can see, no mysterious thing happened here. Actually, you can use about any of the normal maven tags inside profile.





...
...
...
...
...

...
...
...
...
...
...
...
...





You can see more here.

Now that we have managed the dependencies for WebSphere, we would also like to alter different configuration files at compile time. This files can be various XML files, such as web.xml or faces-config.xml. Why is this necessary will be shown on simple example on the web.xml file configuring the RichFaces for WebSphere and JBoss.

In WebSphere you configure the RichFaces mapping as follow:



richfaces
/*
REQUEST
FORWARD
INCLUDE
ERROR



In JBoss you don't configure the RichFaces filter through URL mapping, but through the Faces Servlet name, like this:


richfaces
Faces Servlet
REQUEST
FORWARD
INCLUDE
ERROR



As you see, this is very conflicting. While the WebSphere configuration will also work for JBoss, still you will need additional configuration, to get RichFaces running on WebSphere as follow:


richfaces
/faces/rfRes/*
REQUEST
FORWARD
INCLUDE
ERROR



So, the both mappings did not work for me on JBoss Portal, thus had problem to maintain the configuration of RichFaces for both servers in the same Project.
Luckily, Maven gives the opportunity to filter resources of all kind. Here we need to filter the web resources, thus can be done in few steps:
  1. In your web-app-project add new directory named conf
  2. In this directory you create new properties file named something like websphere.properties
  3. Add new property such as richfaces.servlet.name.mapping=
  4. Add another property such as richfaces.url.mapping=
    /*

Please notice that this properties are added in the websphere.properties file. To have JBoss profile filtering, you need another property file called for example jboss.properties. Inside this file, you have to define the same properties and assign them different values.
Also notice that the url mapping is not needed for WebSphere RichFaces configuration, thus the property is assigned no value, which will result in empty line in the web.xml file.

Now that we have this properties which define different patterns or configuration tags. As you assumed, this won't do anything. Now we will have to tell maven that he needs to take this property files and filter our web.xml. this is done as follow:




websphere



org.apache.maven.plugins
maven-war-plugin




${basedir}/src/main/webapp/WEB-INF

WEB-INF
true






${basedir}/src/conf/jboss-portal.properties




javax.faces
jsf-impl






Here we add new maven plug in for this profile to the standard build. This plug in is the maven war plug in which enables filtering of the WEB-INF content of an web app. With the directory tag, we tell the plug in which directory should be filtered and set the filtering on true.

In order to add the values assigned to the properties in the websphere.properties file in to the web.xml, the following changes are made in to the web.xml file:


richfaces
${richfaces.servlet.name.mapping}
${richfaces.url.mapping}
REQUEST
FORWARD
INCLUDE
ERROR


At compile time, the properties will be replaced with their values, so the out coming file will result in what you preconfigured in your websphere.properties file.

So that is all about it. One needs time to get around this, but once one understands it, it is easy and clean.
I hope this helps.

Tuesday, July 14, 2009

Porting RichFaces Portlets in WebSphere 6.1 Portal

This was painful week of trying to get the rich faces to get work in WebSphere Portal 6.1 running on the WebSphereAS 6.1. I used fully functional RichFaces 3.3.0.GA enabled Portlets (JSR 286) which work fine in JBoss Portal 2.7.1 with JBoss Bridge 1.0.0.CR2, JSF 1.2_12.

When deploying this Portlets on WPS, they got started, but running the Portlets always failed. The problem was closely related to RichFaces and it's filter and how the Mapping of the Resources URL was built.

The following Exception was trhrown:


Rendering View[/myportlet/view.xhtml]
javax.faces.FacesException: Resources framework is not initialised, check web.xml for Filter configuration
at org.ajax4jsf.resource.ResourceBuilderImpl.getWebXml(ResourceBuilderImpl.java:116)
at org.ajax4jsf.resource.ResourceBuilderImpl.getUri(ResourceBuilderImpl.java:323)
at org.jboss.portletbridge.richfaces.PortalResourceBuilder.getUri(PortalResourceBuilder.java:29)

In short it tells that the Filter resources are not properly installed in the web.xml.

I traced the org.ajax4jsf.resource.ResourceBuilderImpl.getWebXm method and on the place where this exception is thrown, it appears that the WebXml Object is null.

I read here that the Ajax4Jsf Filter is not running in WebSphere.

I started playing with the filter mapping in the web.xml. Depending on the Filter Mapping I used in the web.xml, I either get the Portlet to render the Standart JSF with out RichFaces components or I get the above message.

When the Portlet renders the Standard JSF, I looked in to the source code of the page and I see the rich faces panels etc. printed out to XML/JS but the components are not loaded or rendered.
I noticed that the Java Script links are possibly not loading, cause they look in my opinion not correct.
For example, in the Source code I could see:

which I tried to download this way:
http:// localhost:8080/wps/myappcontextpath/rfResorg/richfaces/renderkit/html/scripts/utils.js which always returned:

Error 404: SRVE0190E: File not found: /myappcontextpath/rfResorg/richfaces/renderkit/html/scripts/utils.js


I initially thought that the part:/rfResorg/ should be more like: /rfRes/org/ and the following bug in the jboss jira gave me additional thoughts on the URLs.

I still ain't clear if this was the root cause of the problem, but with additional configuration games, I actually got it to work.

This is how the AJAX4JSF Filter Configuration in the web.xml now looks:


richfaces
org.ajax4jsf.Filter


richfaces
/*
REQUEST
FORWARD
INCLUDE
ERROR


richfaces
/faces/rfRes/*
REQUEST
FORWARD
INCLUDE
ERROR




I added one General mapping for every URL and one to get the Resources through the Faces Servlet. Don't ask me why this works, to me it looks weird enough.

This is not the whole pie. Additionally to this, I had to add the following factory in the faces-config.xml:



org.jboss.portletbridge.context.FacesContextFactoryImpl



So that was it all about. I don't know if this might work on another configuration or even WebSphere Portal, but for now it works on mine.

One more important issue: set the Classloading policy as application classes loaded first. This will make sure that your classes in your WEB-INF/lib/* Directory will be loaded first.

This way, you can run JSF 1.2_12 on your WPS 6.1

Update:

It appears that this issue is caused due to a Class loading problems in WPS. According to some sources, WPS loads the Filters and Servlets with different class loaders. This results in an Exception when the Filter Mapping is configured as followed



Faces Servlet



In this case, only the ULR based mapping as shown above will do, since the Servlet instance is not known to the Filter instance.