/*
* Copyright 2005 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.core.audit;
import org.drools.core.WorkingMemory;
import org.drools.core.WorkingMemoryEventManager;
import org.drools.core.audit.event.ActivationLogEvent;
import org.drools.core.audit.event.ILogEventFilter;
import org.drools.core.audit.event.LogEvent;
import org.drools.core.audit.event.ObjectLogEvent;
import org.drools.core.audit.event.RuleBaseLogEvent;
import org.drools.core.audit.event.RuleFlowGroupLogEvent;
import org.drools.core.audit.event.RuleFlowLogEvent;
import org.drools.core.audit.event.RuleFlowNodeLogEvent;
import org.drools.core.audit.event.RuleFlowVariableLogEvent;
import org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession;
import org.drools.core.command.impl.KnowledgeCommandContext;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.impl.StatefulKnowledgeSessionImpl;
import org.drools.core.impl.StatelessKnowledgeSessionImpl;
import org.drools.core.reteoo.ReteooWorkingMemoryInterface;
import org.drools.core.runtime.process.InternalProcessRuntime;
import org.drools.core.spi.Activation;
import org.kie.api.definition.process.Node;
import org.kie.api.definition.process.NodeContainer;
import org.kie.api.event.kiebase.AfterFunctionRemovedEvent;
import org.kie.api.event.kiebase.AfterKieBaseLockedEvent;
import org.kie.api.event.kiebase.AfterKieBaseUnlockedEvent;
import org.kie.api.event.kiebase.AfterKiePackageAddedEvent;
import org.kie.api.event.kiebase.AfterKiePackageRemovedEvent;
import org.kie.api.event.kiebase.AfterProcessAddedEvent;
import org.kie.api.event.kiebase.AfterProcessRemovedEvent;
import org.kie.api.event.kiebase.AfterRuleAddedEvent;
import org.kie.api.event.kiebase.AfterRuleRemovedEvent;
import org.kie.api.event.kiebase.BeforeFunctionRemovedEvent;
import org.kie.api.event.kiebase.BeforeKieBaseLockedEvent;
import org.kie.api.event.kiebase.BeforeKieBaseUnlockedEvent;
import org.kie.api.event.kiebase.BeforeKiePackageAddedEvent;
import org.kie.api.event.kiebase.BeforeKiePackageRemovedEvent;
import org.kie.api.event.kiebase.BeforeProcessAddedEvent;
import org.kie.api.event.kiebase.BeforeProcessRemovedEvent;
import org.kie.api.event.kiebase.BeforeRuleAddedEvent;
import org.kie.api.event.kiebase.BeforeRuleRemovedEvent;
import org.kie.api.event.kiebase.KieBaseEventListener;
import org.kie.api.event.process.ProcessCompletedEvent;
import org.kie.api.event.process.ProcessEventListener;
import org.kie.api.event.process.ProcessNodeLeftEvent;
import org.kie.api.event.process.ProcessNodeTriggeredEvent;
import org.kie.api.event.process.ProcessStartedEvent;
import org.kie.api.event.process.ProcessVariableChangedEvent;
import org.kie.api.event.rule.AfterMatchFiredEvent;
import org.kie.api.event.rule.AgendaEventListener;
import org.kie.api.event.rule.AgendaGroupPoppedEvent;
import org.kie.api.event.rule.AgendaGroupPushedEvent;
import org.kie.api.event.rule.BeforeMatchFiredEvent;
import org.kie.api.event.rule.MatchCancelledEvent;
import org.kie.api.event.rule.MatchCreatedEvent;
import org.kie.api.event.rule.ObjectDeletedEvent;
import org.kie.api.event.rule.ObjectInsertedEvent;
import org.kie.api.event.rule.ObjectUpdatedEvent;
import org.kie.api.event.rule.RuleFlowGroupActivatedEvent;
import org.kie.api.event.rule.RuleFlowGroupDeactivatedEvent;
import org.kie.api.event.rule.RuleRuntimeEventListener;
import org.kie.api.runtime.process.NodeInstance;
import org.kie.api.runtime.process.NodeInstanceContainer;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.api.runtime.rule.Match;
import org.kie.internal.event.KnowledgeRuntimeEventManager;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
/**
* A logger of events generated by a working memory.
* It listens to the events generated by the working memory and
* creates associated log event (containing a snapshot of the
* state of the working event at that time).
*
* Filters can be used to filter out unwanted events.
*
* Subclasses of this class should implement the logEventCreated(LogEvent)
* method and store this information, like for example log to file
* or database.
*/
public abstract class WorkingMemoryLogger
implements
RuleRuntimeEventListener,
AgendaEventListener,
ProcessEventListener,
KieBaseEventListener {
private List<ILogEventFilter> filters = new ArrayList<ILogEventFilter>();
protected boolean isPhreak;
public WorkingMemoryLogger() {
}
/**
* Creates a new working memory logger for the given working memory.
*
* @param workingMemory
*/
public WorkingMemoryLogger(final WorkingMemory workingMemory) {
workingMemory.addEventListener( (RuleRuntimeEventListener) this );
workingMemory.addEventListener( (AgendaEventListener) this );
InternalProcessRuntime processRuntime = ((InternalWorkingMemory) workingMemory).getProcessRuntime();
if (processRuntime != null) {
processRuntime.addEventListener( this );
}
workingMemory.addEventListener( (KieBaseEventListener) this );
}
public WorkingMemoryLogger(final KnowledgeRuntimeEventManager session) {
if (session instanceof StatefulKnowledgeSessionImpl) {
StatefulKnowledgeSessionImpl statefulSession = ((StatefulKnowledgeSessionImpl) session);
isPhreak = statefulSession.getKnowledgeBase().getConfiguration().isPhreakEnabled();
WorkingMemoryEventManager eventManager = statefulSession;
eventManager.addEventListener( (RuleRuntimeEventListener) this );
eventManager.addEventListener( (AgendaEventListener) this );
eventManager.addEventListener( (KieBaseEventListener) this );
InternalProcessRuntime processRuntime = ((StatefulKnowledgeSessionImpl) session).getProcessRuntime();
if (processRuntime != null) {
processRuntime.addEventListener( this );
}
} else if (session instanceof StatelessKnowledgeSessionImpl) {
StatelessKnowledgeSessionImpl statelessSession = ((StatelessKnowledgeSessionImpl) session);
isPhreak = statelessSession.getKnowledgeBase().getConfiguration().isPhreakEnabled();
statelessSession.addEventListener((RuleRuntimeEventListener) this);
statelessSession.addEventListener( (AgendaEventListener) this );
statelessSession.getKnowledgeBase().addEventListener( (KieBaseEventListener) this );
} else if (session instanceof CommandBasedStatefulKnowledgeSession) {
StatefulKnowledgeSessionImpl statefulSession =
((StatefulKnowledgeSessionImpl)((KnowledgeCommandContext)((CommandBasedStatefulKnowledgeSession) session).getCommandService().getContext()).getKieSession());
isPhreak = statefulSession.getKnowledgeBase().getConfiguration().isPhreakEnabled();
ReteooWorkingMemoryInterface eventManager = statefulSession;
eventManager.addEventListener( (RuleRuntimeEventListener) this );
eventManager.addEventListener( (AgendaEventListener) this );
InternalProcessRuntime processRuntime = eventManager.getProcessRuntime();
eventManager.addEventListener( (KieBaseEventListener) this );
if (processRuntime != null) {
processRuntime.addEventListener( this );
}
} else {
throw new IllegalArgumentException("Not supported session in logger: " + session.getClass());
}
}
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
filters = (List<ILogEventFilter>) in.readObject();
isPhreak = in.readBoolean();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(filters);
out.writeBoolean(isPhreak);
}
/**
* This method is invoked every time a new log event is created.
* Subclasses should implement this method and store the event,
* like for example log to a file or database.
*
* @param logEvent
*/
public abstract void logEventCreated(LogEvent logEvent);
/**
* This method is invoked every time a new log event is created.
* It filters out unwanted events.
*
* @param logEvent
*/
private void filterLogEvent(final LogEvent logEvent) {
for ( ILogEventFilter filter: this.filters) {
// do nothing if one of the filters doesn't accept the event
if ( !filter.acceptEvent( logEvent ) ) {
return;
}
}
// if all the filters accepted the event, signal the creation
// of the event
logEventCreated( logEvent );
}
/**
* Adds the given filter to the list of filters for this event log.
* A log event must be accepted by all the filters to be entered in
* the event log.
*
* @param filter The filter that should be added.
*/
public void addFilter(final ILogEventFilter filter) {
if ( filter == null ) {
throw new NullPointerException();
}
this.filters.add( filter );
}
/**
* Removes the given filter from the list of filters for this event log.
* If the given filter was not a filter of this event log, nothing
* happens.
*
* @param filter The filter that should be removed.
*/
public void removeFilter(final ILogEventFilter filter) {
this.filters.remove( filter );
}
/**
* Clears all filters of this event log.
*/
public void clearFilters() {
this.filters.clear();
}
/**
* @see org.kie.api.event.rule.RuleRuntimeEventListener
*/
public void objectInserted(final ObjectInsertedEvent event) {
filterLogEvent( new ObjectLogEvent( LogEvent.INSERTED,
((InternalFactHandle) event.getFactHandle()).getId(),
event.getObject().toString() ) );
}
/**
* @see org.kie.api.event.rule.RuleRuntimeEventListener
*/
public void objectUpdated(final ObjectUpdatedEvent event) {
filterLogEvent( new ObjectLogEvent( LogEvent.UPDATED,
((InternalFactHandle) event.getFactHandle()).getId(),
event.getObject().toString() ) );
}
/**
* @see org.kie.api.event.rule.RuleRuntimeEventListener
*/
public void objectDeleted(final ObjectDeletedEvent event) {
filterLogEvent( new ObjectLogEvent( LogEvent.RETRACTED,
((InternalFactHandle) event.getFactHandle()).getId(),
event.getOldObject().toString() ) );
}
/**
* @see org.kie.api.event.rule.AgendaEventListener
*/
public void matchCreated(MatchCreatedEvent event) {
filterLogEvent( new ActivationLogEvent( LogEvent.ACTIVATION_CREATED,
getActivationId( event.getMatch() ),
event.getMatch().getRule().getName(),
extractDeclarations( event.getMatch() ),
((RuleImpl)event.getMatch().getRule()).getRuleFlowGroup(),
extractFactHandleIds( (Activation) event.getMatch() ) ) );
}
/**
* @see org.kie.api.event.rule.AgendaEventListener
*/
public void matchCancelled(MatchCancelledEvent event) {
filterLogEvent( new ActivationLogEvent( LogEvent.ACTIVATION_CANCELLED,
getActivationId( event.getMatch() ),
event.getMatch().getRule().getName(),
extractDeclarations( event.getMatch() ),
((RuleImpl)event.getMatch().getRule()).getRuleFlowGroup(),
extractFactHandleIds( (Activation) event.getMatch() ) ) );
}
/**
* @see org.kie.api.event.rule.AgendaEventListener
*/
public void beforeMatchFired(BeforeMatchFiredEvent event) {
filterLogEvent( new ActivationLogEvent( LogEvent.BEFORE_ACTIVATION_FIRE,
getActivationId( event.getMatch() ),
event.getMatch().getRule().getName(),
extractDeclarations( event.getMatch() ),
((RuleImpl)event.getMatch().getRule()).getRuleFlowGroup(),
extractFactHandleIds( (Activation) event.getMatch() ) ) );
}
/**
* @see org.kie.api.event.rule.AgendaEventListener
*/
public void afterMatchFired(final AfterMatchFiredEvent event) {
filterLogEvent( new ActivationLogEvent( LogEvent.AFTER_ACTIVATION_FIRE,
getActivationId( event.getMatch() ),
event.getMatch().getRule().getName(),
extractDeclarations( event.getMatch() ),
((RuleImpl)event.getMatch().getRule()).getRuleFlowGroup(),
extractFactHandleIds( (Activation) event.getMatch() ) ) );
}
/**
* Creates a string representation of the declarations of an activation.
* This is a list of name-value-pairs for each of the declarations in the
* tuple of the activation. The name is the identifier (=name) of the
* declaration, and the value is a toString of the value of the
* parameter, followed by the id of the fact between parentheses.
*
* @param match The match from which the declarations should be extracted
* @return A String represetation of the declarations of the activation.
*/
private String extractDeclarations(Match match) {
final StringBuilder result = new StringBuilder();
List<String> declarations = match.getDeclarationIds();
for ( int i = 0; i < declarations.size(); i++ ) {
String declaration = declarations.get(i);
Object value = match.getDeclarationValue(declaration);
result.append( declaration );
result.append( "=" );
if ( value == null ) {
// this should never occur
result.append( "null" );
} else {
result.append( value );
}
if ( i < declarations.size() - 1 ) {
result.append( "; " );
}
}
return result.toString();
}
private String extractFactHandleIds(Activation activation) {
InternalFactHandle activatingFact = (InternalFactHandle)activation.getPropagationContext().getFactHandleOrigin();
StringBuilder sb = new StringBuilder();
if (activatingFact != null) {
sb.append(activatingFact.getId());
}
InternalFactHandle[] factHandles = activation.getTuple().toFactHandles();
for (int i = 0; i < factHandles.length; i++) {
if (activatingFact != null) {
if (activatingFact.getId() == factHandles[i].getId()) {
continue;
}
sb.append(",");
} else {
if (i > 0) {
sb.append(",");
}
}
sb.append(factHandles[i].getId());
}
return sb.toString();
}
/**
* Returns a String that can be used as unique identifier for an
* activation. Since the activationId is the same for all assertions
* that are created during a single insert, update or retract, the
* key of the tuple of the activation is added too (which is a set
* of fact handle ids).
*
* @param match The match for which a unique id should be generated
* @return A unique id for the activation
*/
private static String getActivationId(Match match) {
final StringBuilder result = new StringBuilder( match.getRule().getName() );
result.append(" [");
List< ? extends FactHandle> factHandles = match.getFactHandles();
for ( int i = 0; i < factHandles.size(); i++ ) {
result.append( ((InternalFactHandle) factHandles.get(i)).getId() );
if ( i < factHandles.size() - 1 ) {
result.append( ", " );
}
}
return result.append( "]" ).toString();
}
public void agendaGroupPopped(AgendaGroupPoppedEvent event) {
// we don't audit this yet
}
public void agendaGroupPushed(AgendaGroupPushedEvent event) {
// we don't audit this yet
}
public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
filterLogEvent(new RuleFlowGroupLogEvent(
LogEvent.BEFORE_RULEFLOW_GROUP_ACTIVATED,
event.getRuleFlowGroup().getName(),
((org.drools.core.spi.RuleFlowGroup)event.getRuleFlowGroup()).size()));
}
public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
filterLogEvent(new RuleFlowGroupLogEvent(
LogEvent.AFTER_RULEFLOW_GROUP_ACTIVATED,
event.getRuleFlowGroup().getName(),
((org.drools.core.spi.RuleFlowGroup)event.getRuleFlowGroup()).size()));
}
public void beforeRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
filterLogEvent(new RuleFlowGroupLogEvent(
LogEvent.BEFORE_RULEFLOW_GROUP_DEACTIVATED,
event.getRuleFlowGroup().getName(),
((org.drools.core.spi.RuleFlowGroup)event.getRuleFlowGroup()).size()));
}
public void afterRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
filterLogEvent(new RuleFlowGroupLogEvent(
LogEvent.AFTER_RULEFLOW_GROUP_DEACTIVATED,
event.getRuleFlowGroup().getName(),
((org.drools.core.spi.RuleFlowGroup)event.getRuleFlowGroup()).size()));
}
public void beforeProcessStarted(ProcessStartedEvent event) {
filterLogEvent( new RuleFlowLogEvent( LogEvent.BEFORE_RULEFLOW_CREATED,
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void afterProcessStarted(ProcessStartedEvent event) {
filterLogEvent(new RuleFlowLogEvent(LogEvent.AFTER_RULEFLOW_CREATED,
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void beforeProcessCompleted(ProcessCompletedEvent event) {
filterLogEvent( new RuleFlowLogEvent( LogEvent.BEFORE_RULEFLOW_COMPLETED,
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void afterProcessCompleted(ProcessCompletedEvent event) {
filterLogEvent(new RuleFlowLogEvent(LogEvent.AFTER_RULEFLOW_COMPLETED,
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) {
filterLogEvent(new RuleFlowNodeLogEvent(LogEvent.BEFORE_RULEFLOW_NODE_TRIGGERED,
createNodeId(event.getNodeInstance()),
event.getNodeInstance().getNodeName(),
createNodeInstanceId(event.getNodeInstance()),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {
filterLogEvent(new RuleFlowNodeLogEvent(LogEvent.AFTER_RULEFLOW_NODE_TRIGGERED,
createNodeId(event.getNodeInstance()),
event.getNodeInstance().getNodeName(),
createNodeInstanceId(event.getNodeInstance()),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
private String createNodeId(NodeInstance nodeInstance) {
Node node = nodeInstance.getNode();
if (node == null) {
return "";
}
Object uniqueIdObj = node.getMetaData().get("UniqueId");
String nodeId;
if( uniqueIdObj == null ) {
nodeId = "" + node.getId();
} else {
nodeId = (String) uniqueIdObj;
}
NodeContainer nodeContainer = node.getNodeContainer();
while (nodeContainer != null) {
if (nodeContainer instanceof Node) {
node = (Node) nodeContainer;
nodeContainer = node.getNodeContainer();
// TODO fix this filter out hidden compositeNode inside ForEach node
if (!(nodeContainer.getClass().getName().endsWith("ForEachNode"))) {
nodeId = node.getId() + ":" + nodeId;
}
} else {
break;
}
}
return nodeId;
}
private String createNodeInstanceId(NodeInstance nodeInstance) {
String nodeInstanceId = "" + nodeInstance.getId();
NodeInstanceContainer nodeContainer = nodeInstance.getNodeInstanceContainer();
while (nodeContainer != null) {
if (nodeContainer instanceof NodeInstance) {
nodeInstance = (NodeInstance) nodeContainer;
nodeInstanceId = nodeInstance.getId() + ":" + nodeInstanceId;
nodeContainer = nodeInstance.getNodeInstanceContainer();
} else {
break;
}
}
return nodeInstanceId;
}
public void beforeNodeLeft(ProcessNodeLeftEvent event) {
filterLogEvent(new RuleFlowNodeLogEvent(LogEvent.BEFORE_RULEFLOW_NODE_EXITED,
createNodeId(event.getNodeInstance()),
event.getNodeInstance().getNodeName(),
createNodeInstanceId(event.getNodeInstance()),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void afterNodeLeft(ProcessNodeLeftEvent event) {
filterLogEvent(new RuleFlowNodeLogEvent(LogEvent.AFTER_RULEFLOW_NODE_EXITED,
createNodeId(event.getNodeInstance()),
event.getNodeInstance().getNodeName(),
createNodeInstanceId(event.getNodeInstance()),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId()) );
}
public void beforeVariableChanged(ProcessVariableChangedEvent event) {
filterLogEvent(new RuleFlowVariableLogEvent(LogEvent.BEFORE_VARIABLE_INSTANCE_CHANGED,
event.getVariableId(),
event.getVariableInstanceId(),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId(),
event.getNewValue() == null ? "null" : event.getNewValue().toString()) );
}
public void afterVariableChanged(ProcessVariableChangedEvent event) {
filterLogEvent(new RuleFlowVariableLogEvent(LogEvent.AFTER_VARIABLE_INSTANCE_CHANGED,
event.getVariableId(),
event.getVariableInstanceId(),
event.getProcessInstance().getProcessId(),
event.getProcessInstance().getProcessName(),
event.getProcessInstance().getId(),
event.getNewValue() == null ? "null" : event.getNewValue().toString()) );
}
public void afterKiePackageAdded(AfterKiePackageAddedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.AFTER_PACKAGE_ADDED,
event.getKiePackage().getName(),
null ) );
}
public void afterKiePackageRemoved(AfterKiePackageRemovedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.AFTER_PACKAGE_REMOVED,
event.getKiePackage().getName(),
null ) );
}
public void beforeKieBaseLocked(BeforeKieBaseLockedEvent event) {
}
public void afterKieBaseLocked(AfterKieBaseLockedEvent event) {
}
public void beforeKieBaseUnlocked(BeforeKieBaseUnlockedEvent event) {
}
public void afterKieBaseUnlocked(AfterKieBaseUnlockedEvent event) {
}
public void afterRuleAdded(AfterRuleAddedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.AFTER_RULE_ADDED,
event.getRule().getPackageName(),
event.getRule().getName() ) );
}
public void afterRuleRemoved(AfterRuleRemovedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.AFTER_RULE_REMOVED,
event.getRule().getPackageName(),
event.getRule().getName() ) );
}
public void beforeFunctionRemoved(BeforeFunctionRemovedEvent event) {
}
public void beforeKiePackageAdded(BeforeKiePackageAddedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.BEFORE_PACKAGE_ADDED,
event.getKiePackage().getName(),
null ) );
}
public void beforeKiePackageRemoved(BeforeKiePackageRemovedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.BEFORE_PACKAGE_REMOVED,
event.getKiePackage().getName(),
null ) );
}
public void beforeRuleAdded(BeforeRuleAddedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.BEFORE_RULE_ADDED,
event.getRule().getPackageName(),
event.getRule().getName() ) );
}
public void beforeRuleRemoved(BeforeRuleRemovedEvent event) {
filterLogEvent( new RuleBaseLogEvent( LogEvent.BEFORE_RULE_REMOVED,
event.getRule().getPackageName(),
event.getRule().getName() ) );
}
public void afterFunctionRemoved(AfterFunctionRemovedEvent event) {
}
public void beforeProcessAdded(BeforeProcessAddedEvent event) {
}
public void afterProcessAdded(AfterProcessAddedEvent event) {
}
public void beforeProcessRemoved(BeforeProcessRemovedEvent event) {
}
public void afterProcessRemoved(AfterProcessRemovedEvent event) {
}
}