Hi everybody,
me and Simone spent few days trying to obtain a working javaflow again in
cocoon 2.2, and to understand how it works deeply; we would like to report our
achievements and problems.
We started trying to obtain a complete system (reloading, runtime
instrumentation etc..). But then scaled down step by step and arrived to the
problem which is stopping us right now.
We are working with the flow class instrumented statically, as well as
AbstractContinuable, FormInstance and the Invoker.
The form shows up correctly, but when we submit the form, ClassCastException :
Caused by: java.lang.ClassCastException:
org.apache.cocoon.samples.flow.java.SimpleFlow
at org.apache.cocoon.components.flow.java.Invoker.run(Invoker.java)
at
org.apache.commons.javaflow.bytecode.StackRecorder.execute(StackRecorder.java:95)
at org.apache.commons.javaflow.Continuation.continueWith(Continuation.java:170)
at
org.apache.cocoon.components.flow.java.JavaInterpreter.handleContinuation(JavaInterpreter.java:182)
at
org.apache.cocoon.components.treeprocessor.sitemap.CallFunctionNode.invoke(CallFunctionNode.java:98)
When the form is rendered, the JavaInterprer instanciates an Invoker to pass to
the Continuation object; the Invoker is responsible to call the Javaflow method
that has been passed by the JavaInterpreter, using reflection. Since the class
must be enhanced, also the method.invoke instruction should be, but it seems
that the BcelClassTransformer doesn't parse this case (In this part we has
serious difficulties to understand the code because we're not to deep into the
JVM bytecode specs and stuff like that).
In fact, if we replace the "method.invoke(o)" with "o.method()" (no reflection
involved) it works correctly.
IIUC the core of the instrumentation is done into the rewrite method, that
parses the bytecode operations and decides what to do; adding a
block that identifies the invoke operation and adds a method call would work;
we tried unluckily to implement it, maybe someone could help us to understand
how to do it.
As I said, we arrived to this error by scaling down our target, our first test
was to enhance a simple Java class, out of cocoon environment, using the
FilesystemAlterationMonitor, that monitors a certain folder (in this case the
folder containing the class file of the Javaflow) and enhance it, if it has
been changed. We found a ClassFormatException ( java.lang.ClassFormatError:
Incompatible magic value -272646673 in class file jci/Simple ) trying to read
the original bytecode of the class; debugging the code we discovered that the
ReloadingListener is using a FileReader to read the bytecode of the class; the
FileReader parses the byte stream depending on a certain char encoding. so the
bytecode is not recognized by the classloader. Using a FileInputStream it works
(ReloadingClassLoaderTestCase confirms; with the original FileReader
implementation it fails).
Then we tried to integrate the FilesystemAlterationMonitor with the cocoon
classloader configuration, in order to automatically reload and enhance the
Javaflows when changed, but we faced with some problems.
- The FilesystemAlterationMonitor always notifies the changes of those classes
that are enhanced; the check is based on file.getLastModified() value, but
every time the value is lower then the last one, so the change notification is
triggered. We didn't investigate more.
- The FileResourceStore fails trying to get the file by the filesystem:
private File getFile(final String pResourceName) {
final String fileName = pResourceName.replace('/',File.separatorChar);
return new File(root, fileName);
}
where pResourceName = "org.apache.cocoon.samples.flow.java.SimpleFlow";
changing the return statement to :
return new File(root,
fileName.replace('.',File.separatorChar).concat(".class"));
it seems to work correctly.
Before send some patches we'd like to know if we're investigating in the right
direction or not.
We can also provide our temporary changes to the javaflow poms in order to
enhance the JavaInterpreter and the javaflow classes at build time, using the
RewriteAntTask provided by commons-javaflow and some fix to the
cocoon-javaflow-sample block.
Regards,
Maurizio and Simone