Created
March 17, 2023 11:51
-
-
Save falco467/0802482e886e17a215e97d35eb49488e to your computer and use it in GitHub Desktop.
Helper for Camunda OptimisticLockingException from erroneous updates when only reading variables
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 [email protected] | |
* @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