Skip to content

Instantly share code, notes, and snippets.

@falco467
Created March 17, 2023 11:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save falco467/0802482e886e17a215e97d35eb49488e to your computer and use it in GitHub Desktop.
Save falco467/0802482e886e17a215e97d35eb49488e to your computer and use it in GitHub Desktop.
Helper for Camunda OptimisticLockingException from erroneous updates when only reading variables
package de.bfs.camunda;
import java.lang.reflect.Field;
import java.util.LinkedList;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.interceptor.CommandContextListener;
/**
* This Class provides a method to prevent the auto-update mechanism
* of Camunda when reading variables from a task or execution.
* <p>
* Normally when getVariable() (or analogous methods) are called, the
* underlying Class will register a listener with the CommandContext
* and when the current Transaction is closed, all variables will check
* if their value has been changed and send an update to the database.
* <p>
* Under certain circumstances* this can trigger an update in the database
* even when the variable is only read and not changed. You can use
* this class to read Variables without them being registered for automatic
* update checking, if you need the variables only read-only.
* <p>
* <h4>Example:</h4>
* <pre>
* // This will read variables without registering them for auto-update
* SuppressAutoUpdateContext.executeWithoutVariableUpdates(() -> {
* vars = execution.getVariables();
* });
* </pre>
* <p>
* <h4>*certain circumstances:</h4>
* The TypedVariable class checks serialized variables for changes by
* serializing the java object again and comparing the resulting byte-array
* with the byte-array saved in the database. Depending on the serializer
* the resulting bytes can be different depending on available type-information.
* This often happens for external variables which are provided without
* generic type information (e.g. HashMap instead of HashMap<String,String>)
*
* @author falco467@gmail.com
* @license MIT (https://opensource.org/license/mit/)
*/
public class SuppressAutoUpdateContext {
/**
* This method will run the function f without registering any Listeners
* in the CommandContext (where variables are registered for updating)
*
* Use this method only for reading variables, since it will prevent any
* CommandContext-Listeners from being added while running {@code f}
*
* @param f A function which is run without registering CommandContextListeners
*/
@SuppressWarnings("unchecked")
public static void executeWithoutVariableUpdates(Runnable f) {
CommandContext ctx = Context.getCommandContext();
Field ctxListenerField = null;
LinkedList<CommandContextListener> originalListeners = null;
try {
ctxListenerField = ctx.getClass().getDeclaredField("commandContextListeners");
ctxListenerField.setAccessible(true);
originalListeners = (LinkedList<CommandContextListener>) ctxListenerField.get(ctx);
ctxListenerField.set(ctx, new LinkedList<>());
f.run();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (ctxListenerField != null) {
if (originalListeners != null) {
ctxListenerField.set(ctx, originalListeners);
}
ctxListenerField.setAccessible(false);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment