cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamic JavaDelegates

mmaker1234
Champ in-the-making
Champ in-the-making
Hello Activiti developers, community,

We have a requirement to provide the ability to load new and update workflow process definitions and their corresponding JavaDelegates during the application run. And before you to send me to the forum search let me note that I already read the following:
  1. Deploying delegate classes
  2. Multiple Classloaders per Engine
  3. Support Deployment-specific Classloaders
  4. Add business archive classloading for delegation classes
  5. Deploying current version of classes with process definition
  6. ReflectUtil.loadClass() and custom classloader
  7. JavaDelegate instances should not be cached
  8. Setting a context classloader when calling client code
  9. Activiti Classloading
  10. Activiti 5.13 User Guide
Unfortunately I did not found a solution to our problem - there are lot of questions and approaches but I can not see a solution. Did I miss or overlook some solution already provided in the forum?

Our environment is the following:
  • Enterprise application (EAR) running in WebLogic application server;
  • Activiti Engine 5.13 provided as a library in the EAR;
  • Activiti Engine configuration file (activiti.cfg.xml) provided in the EAR;
  • We do not use Spring;
  • Due to security reasons we have to expose the minimum possible web interfaces, therefore we can use neither the engine REST API nor the Aciviti Explorer;
  • We have a separate, command line application to upload the process definitions to the database. It actually invokes a separate, standalone Activiti Engine instance connected to the same database that the enterprise application uses;
What we need to achieve is:
  • To be able to add or update a process definition at any time during the enterprise application run;
  • To be able to change/update the JavaDelegates at any time during the enterprise application run;
  • The changes in the JavaDelegates must apply immediately. Preferably immediate after the (re-) deployment (i.e. for the already existing process instances) but latest with the next created process instance;
  • We are not allowed to restart the application (as this is the only way to restart the Activiti Engine) to apply the changes.
We mainly elaborated on Daniel's tips. Unfortunately
  • "deactivate the deployment cache (implement a subclass which does not cache anything)" is not possible because the
    DeploymentManagerresolveProcessDefinition(ProcessDefinitionEntity)
    heavily relays on cache. We tried with an analog of
    org.activiti.standalone.deploy.CustomDeploymentCache
    but in our test with a single definition the changes in the JavaDelegates were not reflected (until engine restart). The test result draws this solution as at least unreliable - we can not guarantee that instances of other process definitions will be served in order to force the cache update;
  • "use proxy delegates (a delegate which loads the "actual" delegate)" is not quite applicable as we should loose or at least have big headaches with the transfer of the fields of the JavaDelegate;
  • "do not use java delegates (but lookup objects the lifecycle of which is managed by some other container like EJB/CDI, OSGi, Spring, … )" To be honest we do not understand this proposal quite good. We tried with DelegateExpression but it seems there is no big difference with the JavaDelegate - instead to provide the delegate class name in the process definition, one should provide that class name in the engine configuration (which is read only once - at the engine start)
  • "You have to garbage collect the classloader that loaded a class / create a new instance of your classloader" How to garbage collect the class loader when
    ReflectUtil.getCustomClassLoader()
    returns the result of
    processEngineConfiguration.getClassLoader()
    which always provides the same object?!
  • In regard to JavaDelegate instances should not be cached: "I propose that we do not cache DelegateInstances in ClassDelegate", Resolved, Fixed in 5.9. I do not observe this fix in 5.13 - the javadoc for
    DefaultDeploymentCache
    states "keep everything in memory, unless a limit is set".
Currently we did the following to satisfy the requirements:
  • Created a custom class loader;
  • Provided the class loader in the Activiti Engine configuration;
  • Provided for each process definition a deployment package (.jar) containing all JavaDelegates referred by the process definition. These packages are deployed in the application server as separate applications;
  • Forced
    ClassDelegate.execute(ActivityExecution)
    and
    ClassDelegate.signal(ActivityExecution)
    to always call
    getActivityBehaviorInstance(execution)
    ;
What we achieved is:
  • We can add or update the process definition at any time and it applies with the next process instantiation;
  • We can add (deploy) the corresponding JavaDelegates and they apply with their reference by the process instance;
What we still miss is the update of the JavaDelegates to take place immediately and not only after the Activiti Engine restart.

Now the question is (phew, at last!): (again) How to achieve dynamic JavaDelegates update (in an application server environment)?

By the way, anyone to see a feasible or promising approach we miss?

Thank you in advance for your efforts to answer,
Monique
13 REPLIES 13

pmsevestre
Champ in-the-making
Champ in-the-making
IMO, you should not even try to do this with Java. You can achieve what you want with Groovy delegates, which is almost exactly what I'm doing in my Ativiti-based app.

1. Use the process parsing hooks (http://www.activiti.org/userguide/index.html#advanced_parseHandlers) to dinamically add delegates to a wrapper that will load user scripts on demand. You can use a convention-based approach or something more elaborated (eg: a DB lookup)
ca
2. When a process instance reaches a given activity that, it will execute your (fixed) delegate, which in turn will:
2.1. lookup ou create a GroovyClassLoader instance that load scripts from your script repository (eg: filesystem ou a JCR based content - the former can give you script versioning,for instance).
2.2. Use this classloader to instantiate the actual delegate
2.3. execute the delegate
2.4. handle exceptions, etc

Groovy scripts can do anything Java can, so there is no loss of functionality there.

If the user changes any script, GroovyClassLoader will pick the changes and apply them on the next invocation.

Hello,

What you are loosing in your approach is at least the possibility to debug especially when you need a complicated logic. That is why we prefer to stay with the Java classes.

In our case we need to load Domain Objects (EJBs) from the DB, to evaluate their state (with somewhat complex logic, including calls to the application's business logic) and then store back some changes using our persistence mechanism. All this should happen under the transaction management of the application server. I'm not quite sure the Groovy script will allow us all of this.

You can set breakpoints in Groovy scripts and single-step them, inspect variables and so forth, so there is no loss in my serviceability in my approach. This is what I do everyday in my environment. The IDE (Eclipse) connects to the application server (regular Tomcat, in my case) and I'm able to do all debugging I want. Usually, those scripts perform some business logic that ties state from a given process instance with backend services (webservices, EJB calls).

Please note that this approach works with groovy classes instantiated using the GroovyClassLoader approach I've described.I tried to do this with other JSR-223 script engines but their debug support is poor. This is also the reason that made me ruled out the use of "script tasks" in my processes.

dimitrihautot
Champ in-the-making
Champ in-the-making
Hello,

I came up with a different solution.
It might not be a perfect use case for other users, but I thought I could share it anyway. It could open new possibilities. 🙂

My web application doesn't have the Activiti engine embedded. The engine is deployed on a slightly modified of the default activiti-rest-webapp, provided by Activiti.

The web application talks to the Engine through REST calls, thanks to the now standardized REST API.

On the BPMN definition, the only JavaDelegate defined is a class that invoke custom URIs, providing its current Execution state, variables, tasks, … as a set of JSON objects.

These custom URIs (=callback URIs) are "hardcoded" in the XML. They usually point back to the web application, which is then allowed to notify clients of the BPM progress.

The callback URIs can also query the engine using the REST API, but are <strong>strongly</strong> discouraged to perform any update with the REST API.
The callback URIs may also update process variables and ship them in the response sent back to the process engine.

Finally, the JavaDelegate inspect the response and persist updated variables in the database, all within the same transaction.

With this approach, the only classes I need to provide to the engine are rather generic. All the decision logic is delegated to another webapp/JVM.


Implementation note: I initially based these URI invocations on Restlet, but it seemed to randomly close response streams. Activiti REST webapp is compiled against Restlet 2.0.x branch, so I couldn't upgrade to the latest 2.1.y branch.
In the end, I replaced the invocation logic by using java.net.URL. Rather basic, but enough for a simple POST request.