Tuesday, April 7, 2009

Working with jBPM workflows in Alfresco - Part 3 : Alfresco scripts

In the previous blog entry: Working with jBPM Workflows in Alfresco - Part 1 : jBPM, we saw how to create a simple jBPM workflow and test it.

In a blog entry before that: Working with jBPM Workflows in Alfresco - Part 2 : Embedded Alf - SDK, we saw how to run our workflow in Alfresco Embedded using the SDK.

In this blog entry, we will add some Alfresco scripts into our workflow that make use of Alfresco APIs.

Step 1 - Logging in JavaScript via Alfresco logger

Starting simple, lets modify our process definition to print out a log message every time we transition to a new state. This is a handy technique if we're not sure our logic is going the way we think it should. Here is the updated processs definiton:
<?xml version="1.0" encoding="UTF-8"?>

<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="publishContentBasic">
<start-state name="start">
<transition name="to_requested" to="requested">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Going to requested state");
</script>
</action>
</transition>
</start-state>

<state name="requested">
<transition to="processing" name="to_processing">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Going to processing state");
</script>
</action>
</transition>
</state>

<state name="processing">
<transition to="succeeded" name="to_succeeded">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Going to succeeded state");
</script>
</action>
</transition>
<transition to="failed" name="to_failed">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Going to failed state");
</script>
</action>
</transition>
</state>

<state name="succeeded">
<transition to="end" name="to_end">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Going to end state");
</script>
</action>
</transition>
</state>

<state name="failed">
<transition to="end" name="to_end">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
logger.log("Finishing");
</script>
</action>
</transition>
</state>

<end-state name="end"></end-state>
</process-definition>
Here we see the new process containing an action element in the transition element calling out the org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript class. This class provides a java script environment with the Alfresco JavaScript API default objects instantiated and ready to use. One of which is the logger.
This action wraps a <script> element containing the javascript to run.

To view the results, first change the log4j.properties config to show the javascipt log messages (and reduce some of the noise).
# Set root logger level to DEBUG and its only appender to CONSOLE.
log4j.rootLogger=WARN, CONSOLE

# CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %C{1} : %m%n

# LIMIT CATEGORIES
log4j.logger.org.jbpm=INFO
#log4j.logger.org.jbpm.graph=DEBUG
log4j.logger.com.sample=DEBUG
# Hibernate debugging levels and their output
log4j.logger.org.hibernate=WARN
#Log all SQL DML statements as they are executed
#log4j.logger.org.hibernate.SQL=TRACE
#Log all JDBC parameters
#log4j.logger.org.hibernate.type=TRACE
#Log all SQL DDL statements as they are executed
#log4j.logger.org.hibernate.tool.hbm2ddl=DEBUG
#Log the state of all entities (max 20 entities) associated with the session at flush time
#log4j.logger.org.hibernate.pretty=DEBUG
#Log all second-level cache activity
#log4j.logger.org.hibernate.cache=DEBUG
#Log transaction related activity
#log4j.logger.org.hibernate.transaction=DEBUG
#Log all JDBC resource acquisition
#log4j.logger.org.hibernate.jdbc=TRACE
#Log HQL and SQL ASTs and other information about query parsing
#log4j.logger.org.hibernate.hql.ast=DEBUG
#Log all JAAS authorization requests
#log4j.logger.org.hibernate.secure=DEBUG
#Log everything (a lot of information, but very useful for troubleshooting)
#log4j.logger.org.hibernate=DEBUG
#log4j.logger.org.hibernate.tools=DEBUG

log4j.logger.org.alfresco=INFO

log4j.logger.org.alfresco.repo.jscript=DEBUG



The last line: log4j.logger.org.alfresco.repo.jscript=DEBUG does the trick. Now run the PublishContentBasicProcessAlfTest as a JUnit test and see the results. You should get something like the following in the log:
20:42:22,574 [main] DEBUG ScriptResourceHelper : Imports resolved, adding resource '_root
20:42:22,761 [main] DEBUG ScriptLogger : Going to requested state
20:42:22,765 [main] DEBUG RhinoScriptProcessor : Time to execute script: 189ms
20:42:22,893 [main] DEBUG ScriptResourceHelper : Imports resolved, adding resource '_root
20:42:22,899 [main] DEBUG ScriptLogger : Going to processing state
20:42:22,901 [main] DEBUG RhinoScriptProcessor : Time to execute script: 6ms
20:42:22,936 [main] DEBUG ScriptResourceHelper : Imports resolved, adding resource '_root
20:42:22,942 [main] DEBUG ScriptLogger : Going to succeeded state
20:42:22,943 [main] DEBUG RhinoScriptProcessor : Time to execute script: 5ms
20:42:23,004 [main] DEBUG ScriptResourceHelper : Imports resolved, adding resource '_root
20:42:23,010 [main] DEBUG ScriptLogger : Going to end state
20:42:23,011 [main] DEBUG RhinoScriptProcessor : Time to execute script: 5ms

Lets do something more interesting. Supposing we wanted to create and/or update some content. The Alfresco JavaScipt API cookbook has an interesting example which I will steal for this purpose. Add the following javascript to the script element of the 'to_requested' transition:
<script>
logger.log("Going to requested state");
logger.log("trying to create file and make it versionable");

var doc = userhome.createFile("checkmeout.txt");
doc.addAspect("cm:versionable");
doc.content = "original text";
logger.log("created versionable doc with content '"+doc.content+"'");

var workingCopy = doc.checkout();
workingCopy.content = "updated text 1";

doc = workingCopy.checkin();

workingCopy = doc.checkout();
workingCopy.content = "updated text 2";

doc = workingCopy.checkin("a history note", true);
logger.log("checked doc out and in a couple of times");
</script>


In this example, we create a new item of content with some text and make it versionable. Then we check it out and updated it. And again checkout and update it, and on check in we use a comment.

Note: be careful as to what syntax you use in the script element. Since this appears in XML, we must escape any characters that can cause this script body to parse incorrectly (such as < and > characters). I had to remove the // comment lines to make this work correctly.

Step 2 - Running a Java class with Alfresco

Now we will configure to run alfresco java foundation API services.

END

2 comments:

Nicolas Raoul said...

Thank you for this explanation!

What would be immensely useful would be to access the files that are specific to this workflow instance, for example the inputted data of the XForm that started the whole workflow.

Cheers,
Nicolas Raoul

Jon said...

Thanks for the guide, but are you going to create one for implementing Java class handlers in workflow?

Thanks,
Jon