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.

Friday, May 29, 2009

RichFaces tree - dynamic update of tree model

The previous RidhFaces simple tree example allows you to render tree data which are output at one render time. But what if your tree data model is so large, so that you can't render it all at once. Instead you have to update your tree model at run time on user request?

Rich faces provides a listener called change expand listener. In case the user selects certain node in your tree, this listener will be called and here you can update your tree model. In the following section I will show how this is done. Let's first see how this listener is bound to a tree component:








Please notice that I left all other tree property's intentionally. As you can see, this property will call the assigned method where you can handle your tree model. Let us look into the method closely and see how we can update our tree model.


import org.richfaces.event.NodeExpandedEvent;

public class MyBackingBean{

/**
* The tree expand expand listener method.
*/
public void processExpansion(NodeExpandedEvent nodeExpandedEvent){
// get the source or the component who fired this event.
Object source = nodeExpandedEvent.getSource();
if (source instanceof HtmlTreeNode) {
// It should be a html tree node, if yes get
// the ui tree which contains this node.
UITree tree = ((HtmlTreeNode) source).getUITree();
// avoid null pointer exceptions even though not needed. but safety first ;-)
if (tree == null) {
return;
}
// get the row key i.e. id of the given node.
Object rowKey = tree.getRowKey();
// get the model node of this node.
TreeNode selectedTreeModelNode = tree.getModelTreeNode(rowKey);
if(null != selectedTreeModelNode){
// add the children nodes.
addChildrenNodes(selectedTreeModelNode);
}
}
}
/**
* Method for adding children nodes to a given tree model node.
*/
public void addChildrenNodes(TreeNode node){
//process your node somehow. For this example I add only one node.
TreeNodeImpl newNode = new TreeNodeImpl();
newNode.setData("My New Node");
newNode.setParent(node);
node.addChild("rowKey", newNode);
}
}


So every time user clicks the icon for expanding nodes, this listener will be called and the tree model will be updated. When the response will be rendered, the updated tree model will be rendered out, including your newly added node(s).

The same process can be done while the user selects particular node of the tree. When the user selects particular node, a RichFaces will look for queued Select node listeners to be called. Such listener can be registered is a Select Node Listener.







To queue select node listener to the tree, you can use the nodeSelectListener property. Than you can create new public listener method in your backing bean as follow:


import org.richfaces.event.NodeSelectedEvent;

public class MyBackingBean{

/**
* The select node listener method.
*/
public void selectNode(NodeSelectedEvent event){
// get the component that fired this event.
UIComponent component = event.getComponent();
if (component instanceof HtmlTree) {
// if a rich faces html tree (it is sometimes also a ui tree)
//get the row key of selected node.
Object rowKey = ((HtmlTree) component).getRowKey();
//pull the model tree node out of ther.
TreeNode selectedTreeModelNode = ((HtmlTree) component).getModelTreeNode(rowKey);
if(null != selectedTreeModelNode){
// add the children to the model.
addChildrenNodes(selectedTreeModelNode);
}
}
}
}


You should notice that the same method for adding children to the model is called as in previous example for expanding the node. Now that we covered how we can update our tree model, we could decide that after selecting a node and updating it's model we would like to expand the node. To do this, there are few way's. The safest and preferable way would be through the tree state saving property. Let's look into it closer:








In the backing bean, you should create new property called tree state. This should look something like this:



/**
* Getters and Setters added.
*/
public class MyBackingBean{

private org.richfaces.component.state.TreeState treeState;

public void setTreeState(org.richfaces.component.state.TreeState treeState) {
this.treeState = treeState;
}

public org.richfaces.component.state.TreeState getTreeState() {
return treeState;
}

}


Now that we added the Getters and Setters for this property, we could add this line to our select node listener method:



import org.richfaces.event.NodeSelectedEvent;

public class MyBackingBean{

public void selectNode(NodeSelectedEvent event) throws Exception {

// ... same as in the previous section
if(null != selectedTreeModelNode){
//.... as in previous section
// queue the node to be expanded.
getTreeState().expandNode((UITree) tree,
(TreeRowKey) rowKey);
}
}
}
}


The tree state instance will be consulted at render time of the tree. It contains map's where the state of the nodes is saved. Expanded nodes have own map where the row key's are queued for expanded rendering. The method expand node of the tree state class queue's the row key of the given node to be rendered as expanded.

As you see, RichFaces is very flexible and provides methods for dynamic extending of the tree structure, avoiding to render the whole content of the tree at the beginning.

Pity that they don't have good tutorials on their components.