Hi,
so after a few hours of searching and getting no relevant results I came up with a solution which I am not proud of.
Let me sum it up for any person interested in this.
I was trying to implement a custom history level, which would decide which history level (already implemented in Camunda) would be used according to definition of a process in which the history-event occured.
I have found a solution that solves a slightly different task. It is the per-process history level available in camunda repository. Since I wanted to decide by process definition id (not by process instance id), I tried to modify the solution.
At this point, I faced a problem that I couldn’t figure out elegantly.
These are types of entities that are being resolved by history level:
ExecutionEntity
VariableInstance
HistoryEvent
IdentityLinkEntity
TaskEntity
DecisionDefinitionEntity
The VariableInstance was critical for me, because I couldn’t get ID of its process definition from the instance. I needed to make it work, so I used reflection for it.
I am attaching my current code:
public class PerProcessHistoryLevel implements HistoryLevel {
private static final String HISTORY_LEVEL_NAME = "per-process";
private static final int HISTORY_LEVEL_ID = 12;
private static final String DEFAULT_HISTORY_LEVEL_NAME = HISTORY_LEVEL_FULL.getName();
private static final String PROPERTY_NAME = "history";
protected Map<String, HistoryLevel> historyLevels = new HashMap<>();
protected Map<String, HistoryLevel> delegateHistoryLevelPerProcess = new HashMap<>();
private RepositoryService repositoryService;
private final ProcessEngineConfigurationImpl processEngineConfiguration;
public PerProcessHistoryLevel(ProcessEngineConfigurationImpl processEngineConfiguration) {
historyLevels.put(HISTORY_LEVEL_NONE.getName(), HISTORY_LEVEL_NONE);
historyLevels.put(HISTORY_LEVEL_ACTIVITY.getName(), HISTORY_LEVEL_ACTIVITY);
historyLevels.put(HISTORY_LEVEL_AUDIT.getName(), HISTORY_LEVEL_AUDIT);
historyLevels.put(HISTORY_LEVEL_FULL.getName(), HISTORY_LEVEL_FULL);
this.processEngineConfiguration = processEngineConfiguration;
}
public void addHistoryLevels(List<HistoryLevel> historyLevels) {
for (HistoryLevel historyLevel : historyLevels) {
this.historyLevels.put(historyLevel.getName(), historyLevel);
}
}
public int getId() {
return HISTORY_LEVEL_ID;
}
public String getName() {
return HISTORY_LEVEL_NAME;
}
public boolean isHistoryEventProduced(HistoryEventType historyEventType, Object entity) {
if (entity == null) {
return true;
}
return isDelegateHistoryLevelEventProduced(historyEventType, entity);
}
protected Collection<CamundaProperty> getCamundaProperties(String processDefinitionId) {
Process process = (Process) getRepositoryService().getBpmnModelInstance(processDefinitionId).getDefinitions().getUniqueChildElementByType(Process.class);
ExtensionElements extensionElements = process.getExtensionElements();
if (extensionElements != null) {
CamundaProperties properties = (CamundaProperties) extensionElements.getUniqueChildElementByType(CamundaProperties.class);
if (properties != null) {
return properties.getCamundaProperties();
}
}
return null;
}
protected boolean isDelegateHistoryLevelEventProduced(HistoryEventType historyEventType, Object entity) {
String processDefinitionId = getProcessDefinitionId(entity);
if (processDefinitionId == null) {
return historyLevels.get(DEFAULT_HISTORY_LEVEL_NAME).isHistoryEventProduced(historyEventType, entity);
}
HistoryLevel delegateHistoryLevel = getDelegateHistoryLevel(processDefinitionId);
return delegateHistoryLevel != null && delegateHistoryLevel.isHistoryEventProduced(historyEventType, entity);
}
protected HistoryLevel getDelegateHistoryLevel(String processDefinitionId) {
HistoryLevel delegateHistoryLevel = delegateHistoryLevelPerProcess.get(processDefinitionId);
if (delegateHistoryLevel == null) {
setDelegateHistoryLevel(processDefinitionId);
delegateHistoryLevel = delegateHistoryLevelPerProcess.get(processDefinitionId);
}
return delegateHistoryLevel;
}
protected void setDelegateHistoryLevel(String processDefinitionId) {
Collection<CamundaProperty> camundaProperties = getCamundaProperties(processDefinitionId);
String historyLevelName = DEFAULT_HISTORY_LEVEL_NAME;
if (camundaProperties != null) {
for (CamundaProperty camundaProperty : camundaProperties) {
if (camundaProperty.getCamundaName().equals(PROPERTY_NAME)) {
historyLevelName = camundaProperty.getCamundaValue();
break;
}
}
}
HistoryLevel historyLevelToSet = historyLevels.get(historyLevelName);
delegateHistoryLevelPerProcess.put(processDefinitionId, historyLevelToSet);
}
protected RepositoryService getRepositoryService() {
if (repositoryService == null) {
repositoryService = processEngineConfiguration.getProcessEngine().getRepositoryService();
}
return repositoryService;
}
protected String getProcessDefinitionId(Object entity) {
if (entity instanceof ExecutionEntity) {
return ((ExecutionEntity) entity).getProcessDefinitionId();
} else if (entity instanceof VariableInstance) {
VariableInstanceEntity variableInstance = (VariableInstanceEntity) entity;
Class<?> c = variableInstance.getClass();
try {
Field executionField = c.getDeclaredField("execution");
executionField.setAccessible(true);
Object executionFieldObj = executionField.get(variableInstance);
ExecutionEntity execution = (ExecutionEntity) executionFieldObj;
if (execution != null) {
return execution.getProcessDefinitionId();
} else {
return null;
}
} catch (NoSuchFieldException e) {
throw new ProcessEngineException(e);
} catch (IllegalAccessException e) {
throw new ProcessEngineException(e);
}
} else if (entity instanceof HistoryEvent) {
return ((HistoryEvent) entity).getProcessDefinitionId();
} else if (entity instanceof IdentityLinkEntity) {
return ((IdentityLinkEntity) entity).getProcessDefId();
} else if (entity instanceof TaskEntity) {
return ((TaskEntity) entity).getProcessDefinitionId();
} else if (entity instanceof DecisionDefinitionEntity) {
return null;
} else {
throw new ProcessEngineException("Unable to find process definition id for class " + entity.getClass().getName());
}
}
}
I know that there are many parts miserably designed and I would be trully grateful for any suggestions on how to implement it properly.
Thank you,
Denis