org.openbp.server.context.TokenContextImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openbp-server Show documentation
Show all versions of openbp-server Show documentation
The OpenBP process engine (main module)
/*
* 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.openbp.server.context;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.openbp.common.logger.LogUtil;
import org.openbp.common.setting.SettingUtil;
import org.openbp.common.util.ToStringHelper;
import org.openbp.common.util.iterator.EmptyIterator;
import org.openbp.common.util.observer.EventObserver;
import org.openbp.common.util.observer.EventObserverMgr;
import org.openbp.core.CoreConstants;
import org.openbp.core.engine.EngineException;
import org.openbp.core.model.Model;
import org.openbp.core.model.ModelQualifier;
import org.openbp.core.model.item.ItemTypes;
import org.openbp.core.model.item.process.NodeSocket;
import org.openbp.core.model.modelmgr.ModelMgr;
import org.openbp.server.ServerConstants;
import org.openbp.server.context.serializer.ContextObjectSerializerRegistry;
import org.openbp.server.engine.EngineEvent;
import org.openbp.server.engine.EngineTraceEvent;
import org.openbp.server.engine.EngineUtil;
import org.openbp.server.persistence.PersistenceContextProvider;
import org.openbp.server.persistence.PersistentObjectBase;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Standard implementation of the TokenContext interface.
*
* NOTE: If you make changes to this class, be sure to perform the also in CayenneTokenContextImpl, which is an exact copy of this class thanks to Java's inability to support multiple inheritance.
*
* @author Heiko Erhardt
*/
public class TokenContextImpl extends PersistentObjectBase
implements TokenContext, Serializable
{
/** Parent context */
private TokenContext parentContext;
/**
* Set of child contexts (contains {@link TokenContext} objects)
*/
private Set childContextSet;
/**
* Node parameters (maps node-qualified parameter names (Strings) to
* parameter values (Objects)
*/
// Note that this is transient, parameter set will be saved as byte array
private transient Map paramValues;
/**
* Sub process call stack. The call stack contains the position in the
* calling process (the input socket of the calling sub process node,
* {@link NodeSocket} objects).
*/
// Note that this is transient, parameter set will be saved as byte array
private transient CallStack callStack;
/**
* Serialized context data. This byte array represents the context data that
* has been deserialized from persistent storage. In order to update this
* data, call {@link #updateSerialziedContextData} before saving the token context to
* persistent storage. we don't want to deserialize the context data
* right away after loading the token context object, so we just read
* the context data and deserialize it as soon as we need to access the
* content of the context. After deserialization, this member will be
* cleared.
*/
private byte[] contextData;
/** Runtime attribute table */
protected transient Map runtimeAttributes;
/** Current socket qualifier */
private String currentSocketQualifier;
/** Executing model qualifier */
private String executingModelQualifier;
/** Lifecycle state */
private int lifecycleState;
/** Lifecycle request */
private int lifecycleRequest;
/** Priority */
private int priority;
/** Type of queue for the current node */
private String queueType;
/**
* Name of the cluster node that currently processes this context.
* Valid only for token contexts that have the lifecycle state SELECTED or RUNNING.
*/
protected String nodeId;
/** User who owns this token context (optional) */
protected String userId;
/** Id of the debugger that is currently tracing this context (optional) */
protected String debuggerId;
/** Current progress count */
private int progressCount;
/** Progress total count */
private int progressTotal;
/** Progress text */
private String progressText;
/** Current socket (entry or exit) of the node that is executing */
private transient NodeSocket currentSocket;
/**
* Base model under which the processes using this context executes.
* The executing model will be used for item lookups.
*/
protected transient Model executingModel;
/** Engine observer manager that is local to this token */
private transient EventObserverMgr observerMgr;
@Autowired
private transient ModelMgr modelMgr;
@Autowired
private transient PersistenceContextProvider persistenceContextProvider;
@Autowired
private transient ContextObjectSerializerRegistry contextObjectSerializerRegistry;
/** Flag that determines if accessing an undefined process variable should cause an exception */
private static Boolean strictProcessVariableHandling;
/**
* Default constructor.
*/
public TokenContextImpl()
{
setLifecycleState(LifecycleState.CREATED);
setLifecycleRequest(LifecycleRequest.NONE);
childContextSet = new HashSet();
}
/**
* Destroys context, i\.\e. removes it from its parent context.
*/
public void destroy()
{
TokenContext parent = getParentContext();
if (parent != null)
{
parent.removeChildContext(this);
setParentContext(null);
}
}
/**
* Returns a string represenation of this object.
*
* @return Debug string containing the most important properties of this object
*/
public String toString()
{
return ToStringHelper.toString(this, "id", "currentSocketQualifier", "lifecycleStateStr", "lifecycleRequestStr", "parentContext.id", "executingModelQualifier");
}
/*
* @seem TokenContext.getParentContext
*/
public TokenContext getParentContext()
{
return parentContext;
}
/*
* @seem TokenContext.setParentContext
*/
public void setParentContext(final TokenContext parentContext)
{
this.parentContext = parentContext;
}
/*
* @seem TokenContext.addChildContext
*/
public void addChildContext(final TokenContext childContext)
{
childContextSet.add(childContext);
childContext.setParentContext(this);
}
/*
* @seem TokenContext.removeChildContext
*/
public void removeChildContext(final TokenContext childContext)
{
if (childContext != null)
{
childContextSet.remove(childContext);
}
}
/*
* @seem TokenContext.hasChildContext
*/
public boolean hasChildContext()
{
return childContextSet.size() != 0;
}
/*
* @seem TokenContext.getChildContexts
*/
public Iterator getChildContexts()
{
if (childContextSet == null)
return EmptyIterator.getInstance();
return childContextSet.iterator();
}
/*
* @seem TokenContext.getExecutingModel
*/
public Model getExecutingModel()
{
if (executingModel == null)
{
if (executingModelQualifier != null)
{
ModelQualifier mq = new ModelQualifier(executingModelQualifier);
executingModel = getModelMgr().getModelByQualifier(mq);
}
}
return executingModel;
}
/*
* @seem TokenContext.setExecutingModel
*/
public void setExecutingModel(final Model executingModel)
{
this.executingModel = executingModel;
if (executingModel != null)
{
executingModelQualifier = executingModel.getQualifier().toString();
}
else
{
executingModelQualifier = null;
}
}
/*
* @seem TokenContext.getCurrentSocket
*/
public NodeSocket getCurrentSocket()
{
if (currentSocket == null)
{
if (currentSocketQualifier != null)
{
ModelQualifier qualifier = new ModelQualifier(currentSocketQualifier);
qualifier.setItemType(ItemTypes.PROCESS);
currentSocket = EngineUtil.determineNodeSocketFromQualifier(qualifier, getModelMgr());
}
}
return currentSocket;
}
/*
* @seem TokenContext.setCurrentSocket
*/
public void setCurrentSocket(final NodeSocket currentSocket)
{
this.currentSocket = currentSocket;
if (currentSocket != null)
{
currentSocketQualifier = currentSocket.getQualifier().toString();
queueType = currentSocket.getNode().getQueueType();
}
else
{
currentSocketQualifier = null;
queueType = null;
}
}
/**
* Gets the current socket qualifier.
*/
public String getCurrentSocketQualifier()
{
return currentSocketQualifier;
}
/**
* Sets the current socket qualifier.
*/
public void setCurrentSocketQualifier(final String currentSocketQualifier)
{
this.currentSocketQualifier = currentSocketQualifier;
}
/**
* Gets the executing model qualifier.
*/
public String getExecutingModelQualifier()
{
return executingModelQualifier;
}
/**
* Sets the executing model qualifier.
*/
public void setExecutingModelQualifier(String executingModelQualifier)
{
this.executingModelQualifier = executingModelQualifier;
}
/*
* @seem TokenContext.getCallStack
*/
public CallStack getCallStack()
{
if (callStack == null)
{
callStack = new CallStackImpl(this);
}
return callStack;
}
/*
* @seem TokenContext.setCallStack
*/
public void setCallStack(final CallStack callStack)
{
this.callStack = callStack;
}
/*
* @seem TokenContext.getLifecycleState
*/
public int getLifecycleState()
{
return lifecycleState;
}
/*
* @seem TokenContext.setLifecycleState
*/
public void setLifecycleState(final int lifecycleState)
{
this.lifecycleState = lifecycleState;
}
public String getLifecycleStateStr()
{
return LifecycleState.toString(getLifecycleState());
}
/*
* @seem TokenContext.getLifecycleRequest
*/
public int getLifecycleRequest()
{
return lifecycleRequest;
}
/*
* @seem TokenContext.setLifecycleRequest
*/
public void setLifecycleRequest(final int lifecycleRequest)
{
this.lifecycleRequest = lifecycleRequest;
synchronized (this)
{
// Wake up any threads waiting for a lifecycle request change
notifyAll();
}
}
public String getLifecycleRequestStr()
{
return LifecycleRequest.toString(getLifecycleRequest());
}
/*
* @seem TokenContext.getPriority
*/
public int getPriority()
{
return priority;
}
/*
* @seem TokenContext.setPriority
*/
public void setPriority(final int priority)
{
this.priority = priority;
}
/*
* @seem TokenContext.getQueueType
*/
public String getQueueType()
{
return queueType;
}
/*
* @seem TokenContext.setQueueType
*/
public void setQueueType(final String queueType)
{
this.queueType = queueType;
}
/*
* @seem TokenContext.getNodeId
*/
public String getNodeId()
{
return nodeId;
}
/*
* @seem TokenContext.getNodeId
*/
public void setNodeId(final String nodeId)
{
this.nodeId = nodeId;
}
/*
* @seem TokenContext.getUserId
*/
public String getUserId()
{
return userId;
}
/*
* @seem TokenContext.setUserId
*/
public void setUserId(final String userId)
{
this.userId = userId;
}
/*
* @seem TokenContext.getDebuggerId
*/
public String getDebuggerId()
{
return debuggerId;
}
/*
* @seem TokenContext.setDebuggerId
*/
public void setDebuggerId(final String debuggerId)
{
this.debuggerId = debuggerId;
}
/*
* @seem TokenContext.getProgressInfo
*/
public ProgressInfo getProgressInfo()
{
ProgressInfo progressInfo = new ProgressInfo();
progressInfo.setProgressCount(getProgressCount());
progressInfo.setProgressTotal(getProgressTotal());
progressInfo.setProgressText(getProgressText());
return progressInfo;
}
/*
* @seem TokenContext.setProgressInfo
*/
public void setProgressInfo(final ProgressInfo progressInfo)
{
setProgressCount(progressInfo.getProgressCount());
setProgressTotal(progressInfo.getProgressTotal());
setProgressText(progressInfo.getProgressText());
}
/*
* @seem TokenContext.getProgressCount
*/
public int getProgressCount()
{
return progressCount;
}
/*
* @seem TokenContext.setProgressCount
*/
public void setProgressCount(final int progressCount)
{
this.progressCount = progressCount;
}
/*
* @seem TokenContext.getProgressTotal
*/
public int getProgressTotal()
{
return progressTotal;
}
/*
* @seem TokenContext.setProgressTotal
*/
public void setProgressTotal(final int progressTotal)
{
this.progressTotal = progressTotal;
}
/*
* @seem TokenContext.getProgressText
*/
public String getProgressText()
{
return progressText;
}
/*
* @seem TokenContext.setProgressText
*/
public void setProgressText(final String progressText)
{
this.progressText = progressText;
}
/**
* Gets the serialized context data.
*/
public byte[] getContextData()
{
return contextData;
}
/**
* Sets the serialized context data.
*/
public void setContextData(final byte[] contextData)
{
// Prepare for lazy deserialization
this.contextData = contextData;
}
//////////////////////////////////////////////////
// @@ Socket parameter access
//////////////////////////////////////////////////
/*
* @seem TokenContext.hasParamValue
*/
public boolean hasParamValue(final String qualParamName)
{
return getParamValues().containsKey(qualParamName);
}
/*
* @seem TokenContext.getParamValue
*/
public Object getParamValue(final String qualParamName)
{
TokenContextValue tcv = (TokenContextValue) getParamValues().get(qualParamName);
if (tcv != null)
return tcv.getValue();
return null;
}
/*
* @seem TokenContext.setParamValue
*/
public void setParamValue(final String qualParamName, final Object value)
{
TokenContextValue tcv = obtainParamValue(qualParamName, true);
tcv.setValue(value);
}
/*
* @seem TokenContext.removeParamValue
*/
public void removeParamValue(final String qualParamName)
{
getParamValues().remove(qualParamName);
}
/*
* @seem TokenContext.clearParamValues
*/
public void clearParamValues()
{
getParamValues().clear();
}
/*
* @seem TokenContext.getParamValues
*/
public Map getParamValues()
{
if (paramValues == null)
{
paramValues = Collections.synchronizedMap(new HashMap());
}
return paramValues;
}
protected TokenContextValue obtainParamValue(final String variableName, final boolean isPersistent)
{
TokenContextValue tcv = (TokenContextValue) getParamValues().get(variableName);
if (tcv == null)
{
tcv = new TokenContextValue();
tcv.setPersistentVariable(isPersistent);
getParamValues().put(variableName, tcv);
}
return tcv;
}
//////////////////////////////////////////////////
// @@ Process variables
//////////////////////////////////////////////////
/**
* Creates a new persistent process variable.
* Does nothing if the process variable already exists in this token or one of its parent tokens.
* Persistent process variables always have 'context' scope.
*
* @param variableName Name of the new process variable
* @param isPersistent true to create a persistent process variable, false for a transient one
*/
public void createProcessVariable(final String variableName, final boolean isPersistent)
{
obtainParamValue(CoreConstants.PROCESS_VARIABLE_INDICATOR + variableName, isPersistent);
}
/*
* @seem TokenContext.hasProcessVariableValue
*/
public boolean hasProcessVariableValue(final String variableName)
{
if (hasParamValue(CoreConstants.PROCESS_VARIABLE_INDICATOR + variableName))
return true;
TokenContext parentContext = getParentContext();
if (parentContext != null)
return parentContext.hasProcessVariableValue(variableName);
return false;
}
/*
* @seem TokenContext.getProcessVariableValue
*/
public Object getProcessVariableValue(final String variableName)
{
TokenContextValue tcv = getProcessVariable(variableName, false);
if (tcv != null)
{
return tcv.getValue();
}
return null;
}
/*
* @seem TokenContext.setProcessVariableValue
*/
public void setProcessVariableValue(final String variableName, final Object value)
{
TokenContextValue tcv = getProcessVariable(variableName, true);
if (tcv != null)
{
tcv.setValue(value);
}
}
/*
* @seem TokenContext.removeProcessVariableValue
*/
public void removeProcessVariableValue(final String variableName)
{
// Remove it from the token context
removeParamValue(CoreConstants.PROCESS_VARIABLE_INDICATOR + variableName);
}
/**
* Gets the names of all process variables that are not null.
*
* @return An iterator of strings
*/
public Iterator getProcessVariableNames()
{
ArrayList ret = new ArrayList();
for (Iterator it = getParamValues().keySet().iterator(); it.hasNext();)
{
String name = (String) it.next();
if (TokenContextUtil.isProcessVariableIdentifier(name))
{
ret.add(name.substring(1));
}
}
return ret.iterator();
}
protected TokenContextValue getProcessVariable(final String variableName, boolean mustExist)
{
for (TokenContext context = this; context != null; context = context.getParentContext())
{
TokenContextValue tcv = (TokenContextValue) context.getParamValues().get(CoreConstants.PROCESS_VARIABLE_INDICATOR + variableName);
if (tcv != null)
return tcv;
}
// Check if accessing an undefined process variable should cause an exception
if (! mustExist)
{
if (strictProcessVariableHandling == null)
{
// Determine timeout from the settings
boolean b = SettingUtil.getBooleanSetting(ServerConstants.SYSPROP_PROCESSVARIABLEHANDLING_STRICT, false);
strictProcessVariableHandling = new Boolean(b);
}
if (strictProcessVariableHandling.booleanValue())
{
mustExist = true;
}
}
if (mustExist)
{
String msg = LogUtil.error(getClass(), "Trying to access undefined process variable $0. [{1}]", variableName, this);
throw new EngineException("UndefinedProcessVariable", msg);
}
// Unknown process variable will cause result null in relaxed mode
return null;
}
//////////////////////////////////////////////////
// @@ RuntimeAttributeContainer implementation
//////////////////////////////////////////////////
/*
* @seem TokenContext.getRuntimeAttributes
*/
public Map getRuntimeAttributes()
{
return runtimeAttributes;
}
/*
* @seem TokenContext.getRuntimeAttribute
*/
public Object getRuntimeAttribute(final String key)
{
if (runtimeAttributes != null)
return runtimeAttributes.get(key);
return null;
}
/*
* @seem TokenContext.setRuntimeAttribute
*/
public void setRuntimeAttribute(final String key, final Object value)
{
if (runtimeAttributes == null)
{
runtimeAttributes = new Hashtable();
}
runtimeAttributes.put(key, value);
}
/*
* @seem TokenContext.removeRuntimeAttribute
*/
public void removeRuntimeAttribute(final String key)
{
if (runtimeAttributes != null)
{
runtimeAttributes.remove(key);
if (runtimeAttributes.isEmpty())
{
runtimeAttributes = null;
}
}
}
//////////////////////////////////////////////////
// @@ Engine observation
//////////////////////////////////////////////////
/**
* Registers an observer.
*
* @param observer The observer; The observer's observeEvent method will receive events of the type
* {@link EngineEvent} or {@link EngineTraceEvent}
* @param eventTypes Lit of event types the observer wants to be notified of
* or null for all event types
*/
public void registerObserver(final EventObserver observer, final String[] eventTypes)
{
obtainObserverMgr().registerObserver(observer, eventTypes);
}
/**
* Unregisters an observer.
*
* @param observer The observer
*/
public void unregisterObserver(final EventObserver observer)
{
obtainObserverMgr().unregisterObserver(observer);
}
/**
* Suspends broadcasting of engine events.
*
* @return The previous suspend status
*/
public boolean suspendEngineEvents()
{
return obtainObserverMgr().suspendObserverEvents();
}
/**
* Resumes broadcasting of engine events.
*/
public void resumeEngineEvents()
{
obtainObserverMgr().resumeObserverEvents();
}
/**
* Checks if there are active engine event observers registered.
*
* @param eventType Type of event in question
* @return true if there is at least one observer
*/
public boolean hasActiveObservers(final String eventType)
{
return obtainObserverMgr().hasActiveObservers(eventType);
}
/**
* Notifies all registered observers about a engine event (for internal use only).
*
* @param event Engine event to dispatch
*/
public void fireEngineEvent(final EngineEvent event)
{
obtainObserverMgr().fireEvent(event);
}
protected EventObserverMgr obtainObserverMgr()
{
if (observerMgr == null)
{
setObserverMgr(new EventObserverMgr());
}
return observerMgr;
}
/**
* Gets the engine observer manager that is local to this token.
*/
public EventObserverMgr getObserverMgr()
{
return observerMgr;
}
/**
* Sets the engine observer manager that is local to this token.
*/
public void setObserverMgr(EventObserverMgr observerMgr)
{
this.observerMgr = observerMgr;
}
//////////////////////////////////////////////////
// @@ PersistentObject support
//////////////////////////////////////////////////
/*
* @seem PersistentObject.onLoad
*/
public void onLoad()
{
applySerialziedContextData();
}
/*
* @seem PersistentObject.beforeSave
*/
public void beforeSave()
{
updateSerialziedContextData();
}
//////////////////////////////////////////////////
// @@ O/R mapper support
//////////////////////////////////////////////////
/**
* Gets the set of child contexts.
*/
public Set getChildContextSet()
{
return childContextSet;
}
/**
* Sets the set of child contexts.
*/
public void setChildContextSet(final Set childContextSet)
{
this.childContextSet = childContextSet;
}
/**
* Ensures that the context is deserialized after being read from persistent
* storage.
*/
protected void applySerialziedContextData()
{
byte[] data = getContextData();
if (data != null)
{
TokenContextUtil.fromByteArray(this, data, contextObjectSerializerRegistry);
}
}
/**
* Updates the serialzied context data with actual context values.
*/
protected void updateSerialziedContextData()
{
setContextData(TokenContextUtil.toByteArray(this, contextObjectSerializerRegistry));
}
public ModelMgr getModelMgr()
{
return modelMgr;
}
public PersistenceContextProvider getPersistenceContextProvider()
{
return persistenceContextProvider;
}
}