OSWorkflow bug:
a NullPointerException caused
by AbstractWorkflow.canInitialize
Using OSWorkflow in several projects, I faced one day a strange exception. That was the kind of ones that you can’t find out its cause at a glance.

Cet article est disponible aussi en Français
java.lang.NullPointerException at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:261) at java.lang.ClassLoader.loadClass(ClassLoader.java:299) at com.sun.appserv.server.util.ASURLClassLoader.loadClass(ASURLClassLoader.java:100) at java.lang.ClassLoader.loadClass(ClassLoader.java:299) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at com.sun.enterprise.util.ConnectorClassLoader.loadClass(ConnectorClassLoader.java:176) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1405) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1292) at com.opensymphony.module.propertyset.PropertySetManager.getInstance(PropertySetManager.java:51) at com.opensymphony.module.propertyset.PropertySetManager.getInstance(PropertySetManager.java:31) at com.opensymphony.workflow.AbstractWorkflow.canInitialize(AbstractWorkflow.java:395) at com.opensymphony.workflow.AbstractWorkflow.canInitialize(AbstractWorkflow.java:364)
My first reflex was obviously googling it. Browsing the few results I found (like here, here and here), didn’t brought any solution. Some questions remain responseless, the others suggest verifying the jars or the application packaging. That pushed me to have a look at the source code and to debug it in order to track the problem’s origin. What I found, is that the problem resides in the canInitialize method of the AbstractWorkflow class. As every Workflow (by ‘Workflow’, I mean the classes implementing the Workflow interface) inherits from AbstractWorkflow (even if you develop your own implementation, you are highly encouraged to inherits from AbstractWorkflow), you can have this kind of exception thrown when you call canInitialize. It’s clear that calling canInitialize is present in almost every OSWorkflow based application, as this method is responsible for telling the caller where the current user have the ability to start a new instance of a given workflow.
The 394 and the 395 lines are the most intersting part of the code (of the canInitialize method).
// since no state change happens here, a memory instance is just fine
PropertySet ps = PropertySetManager.getInstance("memory", null);
What this piece of code exactly means is : Tell the PropertySetManager to instantiate the PropertySet implementation that was configured with the name “memory” in the PropertySet configuration file. That can works fine with the default configuration of PropertySet.
But, what if the developer chooses to have a custom PropertySet configuration (by having its own propertyset.xml) that does not define a PropertySet with the “memory” name ?
That was exactly my case and of many others. In my opinion, such implementation makes a tight constraint which is not mentioned anywhere in the documentation.
First solution : The workaround
The first solution that comes to someone’s mind is the workaround of defining a “memory” PropertySet, by adding :
<propertyset name="memory" class="com.opensymphony.module.propertyset.memory.MemoryPropertySet" />
in the custom configuration file.
This is the kind of solutions that are fast, and very easy to implement, but also, the kind of ones that I personally hate, simply because I hate that unused and useless piece of code or piece of configuration, that is here just “to make it work” and that anyway “doesn’t hurt” !
Second solution : Redefining the AbstractyWorkflow class
A cleaner solution would be to rewrite the AbstractWorkflow class (i.e : correct the bug). What have to be changed is obviously the 395th line.
Let’s begin by understanding what the PropertySet instance (stored in ps variable) is used for. If we try to examine the following lines of code, we easily notice that it is just used for nothing !
It’s there just because the passing conditions methods require a PropertySet instance as one of their parameters. And that’s why, the comment line says “a memory instance is just fine”. And since a PropertySet is always associated with a workflow instance, and we are not dealing with any workflow instance here (remember, we just want to know if the current user can start a workflow), it has no sense to talk about a PropertySet instance here.
That’s why, storing ‘null’ in the ps variable can be enough. But, if your (custom) condition classes rely on the PropertySet passed parameter as below :
public boolean passesCondition(Map transientVars, Map args, PropertySet ps) throws StoreException {
String some = ps.getString("param");
which, by the way, is really weird for a starting workflow condition (only if it’s a reused class for other conditions), you’ll have a NullPointerException thrown (if you’re not testing in the ps variable). So, I suggest a safer replacement for our famous 395th line :
//directly instantiate the memory propertyset implementation //no longer rely on the way the developer configuration PropertySet ps = new MemoryPropertySet();
Or you can also store an anonymous class instance :
PropertySet ps = new PropertySet(){
public boolean exists(String arg0) throws PropertyException {
return false;
}
public Object getAsActualType(String arg0) throws PropertyException {
return null;
}
public boolean getBoolean(String arg0) throws PropertyException {
return false;
}
...
...
...
public int getInt(String arg0) throws PropertyException {
return 0;
}
public Collection getKeys() throws PropertyException {
return Collections.EMPTY_LIST;
}
...
...
public boolean supportsTypes() {
return false;
}
};
I hope this post could help some people, I want just to add finally that the second solution can cause problems or unexpected behaviour if you make an update of your OSWorkflow jars, and decide to rely on a newer version of the framework, and besides, forget to merge your modifications with the newer class.
Tags: bug, canInitialize, exception, NullPointerException, OSWorkflow, PropertySet, PropertySetManager.getInstance
