/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ode.bpel.engine;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.wsdl.Operation;
import javax.xml.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.common.CorrelationKey;
import org.apache.ode.bpel.dao.BpelDAOConnection;
import org.apache.ode.bpel.dao.CorrelatorDAO;
import org.apache.ode.bpel.dao.MessageDAO;
import org.apache.ode.bpel.dao.MessageExchangeDAO;
import org.apache.ode.bpel.dao.MessageRouteDAO;
import org.apache.ode.bpel.dao.ProcessDAO;
import org.apache.ode.bpel.dao.ProcessInstanceDAO;
import org.apache.ode.bpel.engine.extvar.ExternalVariableConf;
import org.apache.ode.bpel.engine.extvar.ExternalVariableManager;
import org.apache.ode.bpel.evt.ProcessInstanceEvent;
import org.apache.ode.bpel.iapi.BpelEngineException;
import org.apache.ode.bpel.iapi.BpelEventListener;
import org.apache.ode.bpel.iapi.Endpoint;
import org.apache.ode.bpel.iapi.EndpointReference;
import org.apache.ode.bpel.iapi.InvocationStyle;
import org.apache.ode.bpel.iapi.Message;
import org.apache.ode.bpel.iapi.MessageExchange;
import org.apache.ode.bpel.iapi.MyRoleMessageExchange;
import org.apache.ode.bpel.iapi.PartnerRoleChannel;
import org.apache.ode.bpel.iapi.ProcessConf;
import org.apache.ode.bpel.iapi.MessageExchange.AckType;
import org.apache.ode.bpel.iapi.MessageExchange.FailureType;
import org.apache.ode.bpel.iapi.MessageExchange.MessageExchangePattern;
import org.apache.ode.bpel.iapi.MessageExchange.Status;
import org.apache.ode.bpel.iapi.MyRoleMessageExchange.CorrelationStatus;
import org.apache.ode.bpel.iapi.Scheduler.JobInfo;
import org.apache.ode.bpel.iapi.Scheduler.JobProcessorException;
import org.apache.ode.bpel.intercept.FailMessageExchangeException;
import org.apache.ode.bpel.intercept.FaultMessageExchangeException;
import org.apache.ode.bpel.intercept.InterceptorInvoker;
import org.apache.ode.bpel.intercept.MessageExchangeInterceptor;
import org.apache.ode.bpel.memdao.BpelDAOConnectionFactoryImpl;
import org.apache.ode.bpel.memdao.ProcessInstanceDaoImpl;
import org.apache.ode.bpel.rapi.ConstantsModel;
import org.apache.ode.bpel.rapi.FaultInfo;
import org.apache.ode.bpel.rapi.OdeRTInstance;
import org.apache.ode.bpel.rapi.OdeRuntime;
import org.apache.ode.bpel.rapi.PartnerLinkModel;
import org.apache.ode.bpel.rapi.ProcessModel;
import org.apache.ode.bpel.rapi.Serializer;
import org.apache.ode.bpel.runtime.InvalidProcessException;
import org.apache.ode.il.config.OdeConfigProperties;
import org.apache.ode.jacob.soup.ReplacementMap;
import org.apache.ode.jacob.vpu.ExecutionQueueImpl;
import org.apache.ode.utils.DOMUtils;
import org.apache.ode.utils.GUID;
import org.apache.ode.utils.Namespaces;
import org.apache.ode.utils.ObjectPrinter;
import org.apache.ode.utils.msg.MessageBundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Entry point into the runtime of a BPEL process.
*
* @author Maciej Szefler <mszefler at gmail dot com>
* @author Matthieu Riou <mriou at apache dot org>
*/
public class ODEProcess {
static final Log __log = LogFactory.getLog(ODEProcess.class);
private static final Messages __msgs = MessageBundle.getMessages(Messages.class);
private volatile Map<PartnerLinkModel, PartnerLinkPartnerRoleImpl> _partnerRoles;
private volatile Map<PartnerLinkModel, PartnerLinkMyRoleImpl> _myRoles;
/** Mapping from {"Service Name" (QNAME) / port} to a myrole. */
private volatile Map<Endpoint, PartnerLinkMyRoleImpl> _endpointToMyRoleMap;
// Backup hashmaps to keep initial endpoints handy after dehydration
private Map<Endpoint, EndpointReference> _myEprs = new HashMap<Endpoint, EndpointReference>();
private Map<Endpoint, EndpointReference> _partnerEprs = new HashMap<Endpoint, EndpointReference>();
private Map<Endpoint, PartnerRoleChannel> _partnerChannels = new HashMap<Endpoint, PartnerRoleChannel>();
/** Mapping from a potentially shared endpoint to its EPR */
private SharedEndpoints _sharedEps;
final QName _pid;
private volatile ProcessModel _processModel;
// Has the process already been hydrated before?
private boolean _hydratedOnce = false;
/** Last time the process was used. */
private volatile long _lastUsed;
volatile OdeRuntime _runtime;
DebuggerSupport _debugger;
final ProcessConf _pconf;
/** {@link MessageExchangeInterceptor}s registered for this process. */
private final List<MessageExchangeInterceptor> _mexInterceptors = new ArrayList<MessageExchangeInterceptor>();
/** Latch-like thing to control hydration/dehydration. */
HydrationLatch _hydrationLatch;
protected Contexts _contexts;
final BpelInstanceWorkerCache _instanceWorkerCache = new BpelInstanceWorkerCache(this);
private final Set<InvocationStyle> _invocationStyles;
private final BpelDAOConnectionFactoryImpl _inMemDao;
final BpelServerImpl _server;
private MyRoleMessageExchangeCache _myRoleMexCache;
/** Deploy-time configuraton for external variables. */
private ExternalVariableConf _extVarConf;
private ExternalVariableManager _evm;
ODEProcess(BpelServerImpl server, ProcessConf conf, BpelEventListener debugger, MyRoleMessageExchangeCache mexCache) {
_server = server;
_pid = conf.getProcessId();
_pconf = conf;
_hydrationLatch = new HydrationLatch();
_contexts = server._contexts;
_inMemDao = new BpelDAOConnectionFactoryImpl(_contexts.txManager);
_myRoleMexCache = mexCache;
// TODO : do this on a per-partnerlink basis, support transacted styles.
HashSet<InvocationStyle> istyles = new HashSet<InvocationStyle>();
istyles.add(InvocationStyle.UNRELIABLE);
if (!conf.isTransient()) istyles.add(InvocationStyle.RELIABLE);
else istyles.add(InvocationStyle.TRANSACTED);
_invocationStyles = Collections.unmodifiableSet(istyles);
}
/**
* Retrives the base URI to use for local resource resolution.
*
* @return URI - instance representing the absolute file path to the physical location of the process definition folder.
*/
public URI getBaseResourceURI() {
return this._pconf.getBaseURI();
}
/**
* Intiialize the external variable configuration/engine manager. This is called from hydration logic, so it
* is possible to change the external variable configuration at runtime.
*
*/
void initExternalVariables() {
List<Element> conf = _pconf.getExtensionElement(ExternalVariableConf.EXTVARCONF_ELEMENT);
_extVarConf = new ExternalVariableConf(conf);
_evm = new ExternalVariableManager(_pid, _extVarConf, _contexts.externalVariableEngines);
}
public OdeConfigProperties getProperties() {
return _server.getConfigProperties();
}
public String toString() {
return "ODEProcess[" + _pid + "]";
}
public ExternalVariableManager getEVM() {
return _evm;
}
void recoverActivity(ProcessInstanceDAO instanceDAO, final String channel, final long activityId, final String action,
final FaultInfo fault) {
if (__log.isDebugEnabled())
__log.debug("Recovering activity in process " + instanceDAO.getInstanceId() + " with action " + action);
_hydrationLatch.latch(1);
try {
markused();
BpelInstanceWorker iworker = _instanceWorkerCache.get(instanceDAO.getInstanceId());
final OdeRTInstance rti = _runtime.newInstance(getState(iworker, instanceDAO));
final BpelRuntimeContextImpl processInstance = new BpelRuntimeContextImpl(iworker, instanceDAO, rti);
try {
iworker.execInCurrentThread(new Callable<Void>() {
public Void call() throws Exception {
processInstance.recoverActivity(channel, activityId, action, fault);
return null;
}
});
} catch (Exception e) {
throw new BpelEngineException(e);
}
} finally {
_hydrationLatch.release(1);
}
}
/**
* Entry point for message exchanges aimed at the my role.
*
* @param mex
*/
void invokeProcess(final MessageExchangeDAO mexdao) {
InvocationStyle istyle = mexdao.getInvocationStyle();
ConstantsModel constants = null;
_hydrationLatch.latch(1);
try {
// The following check is mostly for sanity purposes. MexImpls should prevent this from
// happening.
PartnerLinkMyRoleImpl target = getMyRoleForService(mexdao.getCallee());
constants = target._process.getProcessModel().getConstantsModel();
Status oldstatus = mexdao.getStatus();
if (target == null) {
String errmsg = __msgs.msgMyRoleRoutingFailure(mexdao.getMessageExchangeId());
__log.error(errmsg);
MexDaoUtil.setFailed(mexdao, MessageExchange.FailureType.UNKNOWN_ENDPOINT, errmsg);
onMyRoleMexAck(mexdao, oldstatus);
return;
}
Operation op = target._plinkDef.getMyRoleOperation(mexdao.getOperation());
if (op == null) {
String errmsg = __msgs.msgMyRoleRoutingFailure(mexdao.getMessageExchangeId());
__log.error(errmsg);
MexDaoUtil.setFailed(mexdao, MessageExchange.FailureType.UNKNOWN_OPERATION, errmsg);
onMyRoleMexAck(mexdao, oldstatus);
return;
}
mexdao.setPattern((op.getOutput() == null) ? MessageExchangePattern.REQUEST_ONLY
: MessageExchangePattern.REQUEST_RESPONSE);
if (!processInterceptors(mexdao, InterceptorInvoker.__onProcessInvoked)) {
__log.debug("Aborting processing of mex " + mexdao.getMessageExchangeId() + " due to interceptors.");
onMyRoleMexAck(mexdao, oldstatus);
return;
}
// "Acknowledge" any one-way invokes
if (op.getOutput() == null) {
mexdao.setStatus(Status.ACK);
mexdao.setAckType(AckType.ONEWAY);
onMyRoleMexAck(mexdao, oldstatus);
}
mexdao.setProcess(getProcessDAO());
markused();
CorrelationStatus cstatus = target.invokeMyRole(mexdao);
if (cstatus == null) {
; // do nothing
} else if (cstatus == CorrelationStatus.CREATE_INSTANCE) {
doInstanceWork(mexdao.getInstance().getInstanceId(), new Callable<Void>() {
public Void call() {
executeCreateInstance(mexdao);
return null;
}
});
} else if (cstatus == CorrelationStatus.MATCHED) {
// This should not occur for in-memory processes, since they are technically not allowed to
// have any <receive>/<pick> elements that are not start activities.
if (isInMemory())
__log.warn("In-memory process " + _pid + " is participating in a non-createinstance exchange!");
// We don't like to do the work in the same TX that did the matching, since this creates fertile
// conditions for deadlock in the correlation tables. However if invocation style is transacted,
// we need to do the work right then and there.
if (istyle == InvocationStyle.TRANSACTED) {
doInstanceWork(mexdao.getInstance().getInstanceId(), new Callable<Void>() {
public Void call() {
executeContinueInstanceMyRoleRequestReceived(mexdao);
return null;
}
});
} else if (istyle == InvocationStyle.P2P_TRANSACTED) /* transact p2p invoke in the same thread */ {
executeContinueInstanceMyRoleRequestReceived(mexdao);
} else /* non-transacted style */{
WorkEvent we = new WorkEvent();
we.setType(WorkEvent.Type.MYROLE_INVOKE);
we.setIID(mexdao.getInstance().getInstanceId());
we.setMexId(mexdao.getMessageExchangeId());
// Could be different to this pid when routing to an older version
we.setProcessId(mexdao.getInstance().getProcess().getProcessId());
scheduleWorkEvent(we, null);
}
} else if (cstatus == CorrelationStatus.QUEUED) {
; // do nothing
}
} catch (InvalidProcessException ipe) {
QName faultQName = null;
if (constants != null) {
Document document = DOMUtils.newDocument();
Element faultElement = document.createElementNS(Namespaces.SOAP_ENV_NS, "Fault");
Element faultDetail = document.createElementNS(Namespaces.ODE_EXTENSION_NS, "fault");
faultElement.appendChild(faultDetail);
switch (ipe.getCauseCode()) {
case InvalidProcessException.DUPLICATE_CAUSE_CODE:
faultQName = constants.getDuplicateInstance();
faultDetail.setTextContent("Found a duplicate instance with the same message key");
break;
case InvalidProcessException.RETIRED_CAUSE_CODE:
faultQName = constants.getRetiredProcess();
faultDetail.setTextContent("The process you're trying to instantiate has been retired");
break;
case InvalidProcessException.DEFAULT_CAUSE_CODE:
default:
faultQName = constants.getUnknownFault();
break;
}
MexDaoUtil.setFaulted(mexdao, faultQName, faultElement);
}
} finally {
_hydrationLatch.release(1);
// If we did not get an ACK during this method, then mark this MEX as needing an ASYNC wake-up
if (mexdao.getStatus() != Status.ACK) mexdao.setStatus(Status.ASYNC);
assert mexdao.getStatus() == Status.ACK || mexdao.getStatus() == Status.ASYNC;
}
}
void executeCreateInstance(MessageExchangeDAO mexdao) {
assert _hydrationLatch.isLatched(1);
BpelInstanceWorker worker = _instanceWorkerCache.get(mexdao.getInstance().getInstanceId());
assert worker.isWorkerThread();
BpelRuntimeContextImpl rtictx = new BpelRuntimeContextImpl(
worker, mexdao.getInstance(), _runtime.newInstance(getState(worker, mexdao.getInstance())));
rtictx.executeCreateInstance(mexdao);
}
void executeContinueInstanceMyRoleRequestReceived(MessageExchangeDAO mexdao) {
assert _hydrationLatch.isLatched(1);
BpelInstanceWorker worker = _instanceWorkerCache.get(mexdao.getInstance().getInstanceId());
assert worker.isWorkerThread();
OdeRTInstance rti = _runtime.newInstance(getState(worker, mexdao.getInstance()));
BpelRuntimeContextImpl instance = new BpelRuntimeContextImpl(worker, mexdao.getInstance(), rti);
int amp = mexdao.getChannel().indexOf('&');
String groupId = mexdao.getChannel().substring(0, amp);
int idx = Integer.valueOf(mexdao.getChannel().substring(amp + 1));
instance.injectMyRoleMessageExchange(groupId, idx, mexdao);
instance.execute();
}
void executeContinueInstanceResume(ProcessInstanceDAO instanceDao, int retryCount) {
BpelInstanceWorker worker = _instanceWorkerCache.get(instanceDao.getInstanceId());
assert worker.isWorkerThread();
OdeRTInstance rti = _runtime.newInstance(getState(worker, instanceDao));
BpelRuntimeContextImpl brc = new BpelRuntimeContextImpl(worker, instanceDao, rti);
brc.setRetryCount(retryCount);
brc.execute();
}
void executeContinueInstanceTimerReceived(ProcessInstanceDAO instanceDao, String timerChannel) {
BpelInstanceWorker worker = _instanceWorkerCache.get(instanceDao.getInstanceId());
assert worker.isWorkerThread();
OdeRTInstance rti = _runtime.newInstance(getState(worker, instanceDao));
BpelRuntimeContextImpl brc = new BpelRuntimeContextImpl(worker, instanceDao, rti);
if (brc.injectTimerEvent(timerChannel)) brc.execute();
}
private void executeContinueInstanceMatcherEvent(ProcessInstanceDAO instanceDao, String correlatorId,
CorrelationKey correlationKey) {
if (__log.isDebugEnabled()) {
__log.debug("MatcherEvent handling: correlatorId=" + correlatorId + ", ckey=" + correlationKey);
}
CorrelatorDAO correlator = instanceDao.getProcess().getCorrelator(correlatorId);
// Find the route first, this is a SELECT FOR UPDATE on the "selector" row,
// So we want to acquire the lock before we do anthing else.
MessageRouteDAO mroute = correlator.findRoute(correlationKey);
if (mroute == null) {
// Ok, this means that a message arrived before we did, so nothing to do.
__log.debug("MatcherEvent handling: nothing to do, route no longer in DB");
return;
}
// Now see if there is a message that matches this selector.
MessageExchangeDAO mexdao = correlator.dequeueMessage(correlationKey);
if (mexdao != null) {
__log.debug("MatcherEvent handling: found matching message in DB (i.e. message arrived before <receive>)");
// We have a match, so we can get rid of the routing entries.
correlator.removeRoutes(mroute.getGroupId(), instanceDao);
mexdao.setInstance(instanceDao);
// Found message matching one of our selectors.
if (__log.isDebugEnabled()) {
__log.debug("SELECT: " + mroute.getGroupId() + ": matched to MESSAGE " + mexdao + " on CKEY " + correlationKey);
}
BpelInstanceWorker worker = _instanceWorkerCache.get(instanceDao.getInstanceId());
assert worker.isWorkerThread();
OdeRTInstance rti = _runtime.newInstance(getState(worker, mexdao.getInstance()));
BpelRuntimeContextImpl brc = new BpelRuntimeContextImpl(worker, instanceDao, rti);
brc.injectMyRoleMessageExchange(mroute.getGroupId(), mroute.getIndex(), mexdao);
brc.execute();
} else {
__log.debug("MatcherEvent handling: nothing to do, no matching message in DB");
}
}
void executeContinueInstancePartnerRoleResponseReceived(MessageExchangeDAO mexdao) {
assert _hydrationLatch.isLatched(1);
ProcessInstanceDAO instanceDao = mexdao.getInstance();
if (instanceDao == null)
throw new BpelEngineException("InternalError: No instance for partner mex " + mexdao);
BpelInstanceWorker worker = _instanceWorkerCache.get(mexdao.getInstance().getInstanceId());
assert worker.isWorkerThread();
OdeRTInstance rti = _runtime.newInstance(getState(worker, mexdao.getInstance()));
BpelRuntimeContextImpl brc = new BpelRuntimeContextImpl(worker, mexdao.getInstance(), rti);
// Canceling invoke check
String jobId = mexdao.getProperty("invokeCheckJobId");
_contexts.scheduler.cancelJob(jobId);
brc.injectPartnerResponse(mexdao.getMessageExchangeId(), mexdao.getChannel());
brc.execute();
}
void enqueueInstanceTransaction(Long instanceId, final Runnable runnable) {
if (instanceId == null)
throw new NullPointerException("instanceId was null!");
BpelInstanceWorker iworker = _instanceWorkerCache.get(instanceId);
iworker.enqueue(_server.new TransactedRunnable(runnable));
}
private <T> T doInstanceWork(Long instanceId, final Callable<T> callable) {
try {
BpelInstanceWorker iworker = _instanceWorkerCache.get(instanceId);
return iworker.execInCurrentThread(new ProcessCallable<T>(callable));
} catch (Exception ex) {
throw new BpelEngineException(ex);
}
}
private PartnerLinkMyRoleImpl getMyRoleForService(QName serviceName) {
assert _hydrationLatch.isLatched(1);
for (Map.Entry<Endpoint, PartnerLinkMyRoleImpl> e : _endpointToMyRoleMap.entrySet()) {
if (e.getKey().serviceName.equals(serviceName))
return e.getValue();
}
return null;
}
/**
* Process the message-exchange interceptors.
* @return <code>true</code> if execution should continue, <code>false</code> otherwise
*/
boolean processInterceptors(MessageExchangeDAO mexdao, InterceptorInvoker invoker) {
InterceptorContextImpl ictx = new InterceptorContextImpl(_contexts.dao.getConnection(), mexdao, getProcessDAO(), _pconf);
try {
for (MessageExchangeInterceptor interceptor : _mexInterceptors)
invoker.invoke(interceptor, ictx);
for (MessageExchangeInterceptor interceptor : _server._contexts.globalIntereceptors)
invoker.invoke(interceptor, ictx);
} catch (FailMessageExchangeException e) {
MexDaoUtil.setFailed(mexdao, FailureType.ABORTED, e.getMessage());
return false;
} catch (FaultMessageExchangeException e) {
MexDaoUtil.setFaulted(mexdao, e.getFaultName(), e.getFaultData());
return false;
}
return true;
}
/**
* Handle a work event; this method is called from the scheduler thread and should be very quick, i.e. any serious work needs to
* be handed off to a separate thread.
* @throws JobProcessorException
*/
void handleWorkEvent(final JobInfo jobInfo) throws JobProcessorException {
assert !_contexts.isTransacted() : "work events must be received outside of a transaction";
markused();
final WorkEvent we = new WorkEvent(jobInfo.jobDetail);
if (__log.isDebugEnabled()) {
__log.debug(ObjectPrinter.stringifyMethodEnter("handleWorkEvent", new Object[] { "jobInfo", jobInfo }));
}
enqueueInstanceTransaction(we.getIID(), new Runnable() {
public void run() {
_contexts.scheduler.jobCompleted(jobInfo.jobName);
execInstanceEvent(we);
}
});
}
/**
* Enqueue a transaction for execution by the engine.
*
* @param tx
* the transaction
*/
<T> Future<T> enqueueTransaction(final Callable<T> tx) {
// We have to wrap our transaction to make sure that we are hydrated when the transaction runs.
return _server.enqueueTransaction(new ProcessCallable<T>(tx));
}
private Object getState(BpelInstanceWorker worker, ProcessInstanceDAO instanceDAO) {
ExecutionQueueImpl state = (ExecutionQueueImpl) worker.getCachedState(instanceDAO.getExecutionStateCounter());
if (state != null) return state;
if (isInMemory()) {
ProcessInstanceDaoImpl inmem = (ProcessInstanceDaoImpl) instanceDAO;
if (inmem.getSoup() != null) {
state = (ExecutionQueueImpl) inmem.getSoup();
}
} else {
byte[] daoState = instanceDAO.getExecutionState();
if (daoState != null) {
state = new ExecutionQueueImpl(getClass().getClassLoader());
state.setReplacementMap((ReplacementMap) _runtime.getReplacementMap(instanceDAO.getProcess().getProcessId()));
ByteArrayInputStream iis = new ByteArrayInputStream(daoState);
try {
state.read(iis);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
return state;
}
private void execInstanceEvent(WorkEvent we) {
BpelInstanceWorker worker = _instanceWorkerCache.get(we.getIID());
assert worker.isWorkerThread();
ProcessInstanceDAO instanceDAO = getProcessDAO().getInstance(we.getIID());
MessageExchangeDAO mexDao = we.getMexId() == null ? null : loadMexDao(we.getMexId());
if (instanceDAO == null) {
if (__log.isDebugEnabled()) {
__log.debug("handleWorkEvent: no ProcessInstance found with iid " + we.getIID() + "; ignoring.");
}
return;
}
if (__log.isDebugEnabled()) {
__log.debug("handleWorkEvent: " + we.getType() + " event for process instance " + we.getIID());
}
switch (we.getType()) {
case MYROLE_INVOKE:
executeContinueInstanceMyRoleRequestReceived(mexDao);
break;
case TIMER:
executeContinueInstanceTimerReceived(instanceDAO, we.getChannel());
break;
case RESUME:
executeContinueInstanceResume(instanceDAO, we.getRetryCount());
break;
case PARTNER_RESPONSE:
executeContinueInstancePartnerRoleResponseReceived(mexDao);
break;
case MATCHER:
executeContinueInstanceMatcherEvent(instanceDAO, we.getCorrelatorId(), we.getCorrelationKey());
break;
}
}
MessageExchangeDAO loadMexDao(String mexId) {
return isInMemory() ? _inMemDao.getConnection().getMessageExchange(mexId) : _contexts.dao.getConnection()
.getMessageExchange(mexId);
}
private void setRoles(ProcessModel oprocess) {
_partnerRoles = new HashMap<PartnerLinkModel, PartnerLinkPartnerRoleImpl>();
_myRoles = new HashMap<PartnerLinkModel, PartnerLinkMyRoleImpl>();
_endpointToMyRoleMap = new HashMap<Endpoint, PartnerLinkMyRoleImpl>();
// Create myRole endpoint name mapping (from deployment descriptor)
HashMap<PartnerLinkModel, Endpoint> myRoleEndpoints = new HashMap<PartnerLinkModel, Endpoint>();
for (Map.Entry<String, Endpoint> provide : _pconf.getProvideEndpoints().entrySet()) {
PartnerLinkModel plink = oprocess.getPartnerLink(provide.getKey());
if (plink == null) {
String errmsg = "Error in deployment descriptor for process " + _pid + "; reference to unknown partner link "
+ provide.getKey();
__log.error(errmsg);
throw new BpelEngineException(errmsg);
}
myRoleEndpoints.put(plink, provide.getValue());
}
// Create partnerRole initial value mapping
for (Map.Entry<String, Endpoint> invoke : _pconf.getInvokeEndpoints().entrySet()) {
PartnerLinkModel plink = oprocess.getPartnerLink(invoke.getKey());
if (plink == null) {
String errmsg = "Error in deployment descriptor for process " + _pid + "; reference to unknown partner link "
+ invoke.getKey();
__log.error(errmsg);
throw new BpelEngineException(errmsg);
}
__log.debug("Processing <invoke> element for process " + _pid + ": partnerlink " + invoke.getKey() + " --> "
+ invoke.getValue());
}
for (PartnerLinkModel pl : oprocess.getAllPartnerLinks()) {
if (pl.hasMyRole()) {
Endpoint endpoint = myRoleEndpoints.get(pl);
if (endpoint == null)
throw new IllegalArgumentException("No service name for myRole plink " + pl.getName());
PartnerLinkMyRoleImpl myRole = new PartnerLinkMyRoleImpl(this, pl, endpoint);
_myRoles.put(pl, myRole);
_endpointToMyRoleMap.put(endpoint, myRole);
}
if (pl.hasPartnerRole()) {
Endpoint endpoint = _pconf.getInvokeEndpoints().get(pl.getName());
if (endpoint == null && pl.isInitializePartnerRoleSet())
throw new IllegalArgumentException(pl.getName() + " must be bound to an endpoint in deloy.xml");
PartnerLinkPartnerRoleImpl partnerRole = new PartnerLinkPartnerRoleImpl(this, pl, endpoint);
_partnerRoles.put(pl, partnerRole);
}
}
}
ProcessDAO getProcessDAO() {
return isInMemory() ? _inMemDao.getConnection().getProcess(_pid) : _contexts.dao.getConnection().getProcess(_pid);
}
static String genCorrelatorId(PartnerLinkModel plink, String opName) {
return plink.getId() + "." + opName;
}
/**
* Get all the services that are implemented by this process.
*
* @return list of qualified names corresponding to the myroles.
*/
public Set<Endpoint> getServiceNames() {
Set<Endpoint> endpoints = new HashSet<Endpoint>();
for (Endpoint provide : _pconf.getProvideEndpoints().values()) {
endpoints.add(provide);
}
return endpoints;
}
void activate(Contexts contexts) {
_contexts = contexts;
_debugger = new DebuggerSupport(this);
__log.debug("Activating " + _pid);
// Activate all the my-role endpoints.
for (Map.Entry<String, Endpoint> entry : _pconf.getProvideEndpoints().entrySet()) {
Endpoint endpoint = entry.getValue();
EndpointReference initialEPR = null;
if (isShareable(endpoint)) {
// Check if the EPR already exists for the given endpoint
initialEPR = _sharedEps.getEndpointReference(endpoint);
if (initialEPR == null) {
// Create an EPR by physically activating the endpoint
initialEPR = _contexts.bindingContext.activateMyRoleEndpoint(_pid, entry.getValue());
_sharedEps.addEndpoint(endpoint, initialEPR);
__log.debug("Activated " + _pid + " myrole " + entry.getKey() + ": EPR is " + initialEPR);
}
// Increment the reference count on the endpoint
_sharedEps.incrementReferenceCount(endpoint);
} else {
// Create an EPR by physically activating the endpoint
initialEPR = _contexts.bindingContext.activateMyRoleEndpoint(_pid, entry.getValue());
__log.debug("Activated " + _pid + " myrole " + entry.getKey() + ": EPR is " + initialEPR);
}
_myEprs.put(endpoint, initialEPR);
}
__log.debug("Activated " + _pid);
markused();
}
void deactivate() {
// Deactivate all the my-role endpoints.
for (Endpoint endpoint : _myEprs.keySet()) {
// Deactivate the EPR only if there are no more references
// to this endpoint from any (active) BPEL process.
if (isShareable(endpoint)) {
__log.debug("deactivating shared endpoint " + endpoint);
if (!_sharedEps.decrementReferenceCount(endpoint)) {
_contexts.bindingContext.deactivateMyRoleEndpoint(endpoint);
_sharedEps.removeEndpoint(endpoint);
}
} else {
__log.debug("deactivating non-shared endpoint " + endpoint);
_contexts.bindingContext.deactivateMyRoleEndpoint(endpoint);
}
}
// TODO Deactivate all the partner-role channels
}
private boolean isShareable(Endpoint endpoint) {
if (!_pconf.isSharedService(endpoint.serviceName)) return false;
// PartnerLinkMyRoleImpl partnerLink = _endpointToMyRoleMap.get(endpoint);
// return partnerLink != null && partnerLink.isOneWayOnly();
return false;
}
EndpointReference getInitialPartnerRoleEPR(PartnerLinkModel link) {
_hydrationLatch.latch(1);
try {
PartnerLinkPartnerRoleImpl prole = _partnerRoles.get(link);
if (prole == null)
throw new IllegalStateException("Unknown partner link " + link);
return prole.getInitialEPR();
} finally {
_hydrationLatch.release(1);
}
}
Endpoint getInitialPartnerRoleEndpoint(PartnerLinkModel link) {
_hydrationLatch.latch(1);
try {
PartnerLinkPartnerRoleImpl prole = _partnerRoles.get(link);
if (prole == null)
throw new IllegalStateException("Unknown partner link " + link);
return prole._initialPartner;
} finally {
_hydrationLatch.release(1);
}
}
EndpointReference getInitialMyRoleEPR(PartnerLinkModel link) {
_hydrationLatch.latch(1);
try {
PartnerLinkMyRoleImpl myRole = _myRoles.get(link);
if (myRole == null) throw new IllegalStateException("Unknown partner link " + link);
return myRole.getInitialEPR();
} finally {
_hydrationLatch.release(1);
}
}
QName getPID() {
return _pid;
}
QName getProcessType() {
return _pconf.getType();
}
PartnerRoleChannel getPartnerRoleChannel(PartnerLinkModel partnerLink) {
_hydrationLatch.latch(1);
try {
PartnerLinkPartnerRoleImpl prole = _partnerRoles.get(partnerLink);
if (prole == null)
throw new IllegalStateException("Unknown partner link " + partnerLink);
return prole._channel;
} finally {
_hydrationLatch.release(1);
}
}
public void saveEvent(ProcessInstanceEvent event, ProcessInstanceDAO instanceDao) {
saveEvent(event, instanceDao, null);
}
public void saveEvent(ProcessInstanceEvent event, ProcessInstanceDAO instanceDao, List<String> scopeNames) {
markused();
if (_pconf.isEventEnabled(scopeNames, event.getType())) {
// notify the listeners
_server.fireEvent(event);
if (instanceDao != null)
instanceDao.insertBpelEvent(event);
else
__log.debug("Couldn't find instance to save event, no event generated!");
}
}
/**
* Ask the process to dehydrate.
*/
void dehydrate() {
_hydrationLatch.latch(0);
try {
// We don't actually need to do anything, the latch will run the doDehydrate method
// when necessary..
} finally {
_hydrationLatch.release(0);
}
}
void hydrate() {
_hydrationLatch.latch(1);
try {
// We don't actually need to do anything, the latch will run the doHydrate method
// when necessary..
} finally {
_hydrationLatch.release(1);
}
}
ProcessModel getProcessModel() {
_hydrationLatch.latch(1);
try {
return _processModel;
} finally {
_hydrationLatch.release(1);
}
}
private MyRoleMessageExchangeImpl newMyRoleMex(InvocationStyle istyle, String mexId, QName target,
PartnerLinkModel mplink, Operation operation) {
MyRoleMessageExchangeImpl mex;
switch (istyle) {
case RELIABLE:
mex = new ReliableMyRoleMessageExchangeImpl(this, mexId, mplink, operation, target);
break;
case TRANSACTED:
mex = new TransactedMyRoleMessageExchangeImpl(this, mexId, mplink, operation, target);
break;
case UNRELIABLE:
mex = new UnreliableMyRoleMessageExchangeImpl(this, mexId, mplink, operation, target);
break;
default:
throw new AssertionError("Unexpected invocation style: " + istyle);
}
_myRoleMexCache.put(mex);
return mex;
}
/**
* Lookup a {@link MyRoleMessageExchangeImpl} object in the cache, re-creating it if not found.
*
* @param mexdao
* DB representation of the mex.
* @return client representation
*/
MyRoleMessageExchangeImpl lookupMyRoleMex(MessageExchangeDAO mexdao) {
return _myRoleMexCache.get(mexdao, this); // this will re-create if necessary
}
/**
* Create (or recreate) a {@link MyRoleMessageExchangeImpl} object from data in the db. This method is used by the
* {@link MyRoleMessageExchangeCache} to re-create objects when they are not found in the cache.
*
* @param mexdao
* @return
*/
MyRoleMessageExchangeImpl recreateMyRoleMex(MessageExchangeDAO mexdao) {
InvocationStyle istyle = mexdao.getInvocationStyle();
_hydrationLatch.latch(1);
try {
PartnerLinkModel plink = _processModel.getPartnerLink(mexdao.getPartnerLinkModelId());
if (plink == null) {
String errmsg = __msgs.msgDbConsistencyError("MexDao #" + mexdao.getMessageExchangeId()
+ " referenced unknown pLinkModelId " + mexdao.getPartnerLinkModelId());
__log.error(errmsg);
throw new BpelEngineException(errmsg);
}
Operation op = plink.getMyRoleOperation(mexdao.getOperation());
if (op == null) {
String errmsg = __msgs.msgDbConsistencyError("MexDao #" + mexdao.getMessageExchangeId()
+ " referenced unknown operation " + mexdao.getOperation());
__log.error(errmsg);
throw new BpelEngineException(errmsg);
}
PartnerLinkMyRoleImpl myRole = _myRoles.get(plink);
if (myRole == null) {
String errmsg = __msgs.msgDbConsistencyError("MexDao #" + mexdao.getMessageExchangeId()
+ " referenced non-existant myrole");
__log.error(errmsg);
throw new BpelEngineException(errmsg);
}
MyRoleMessageExchangeImpl mex = newMyRoleMex(istyle, mexdao.getMessageExchangeId(), myRole._endpoint.serviceName,
plink, op);
mex.load(mexdao);
return mex;
} finally {
_hydrationLatch.release(1);
}
}
PartnerRoleMessageExchangeImpl createPartnerRoleMex(MessageExchangeDAO mexdao) {
_hydrationLatch.latch(1);
try {
PartnerLinkModel plink = _processModel.getPartnerLink(mexdao.getPartnerLinkModelId());
PartnerLinkPartnerRoleImpl prole = _partnerRoles.get(plink);
return prole.createPartnerRoleMex(mexdao);
} finally {
_hydrationLatch.release(1);
}
}
Set<InvocationStyle> getSupportedInvocationStyle(QName serviceId) {
return _invocationStyles;
}
/**
* Find the partner-link-my-role that corresponds to the given service name.
*
* @param serviceName
* name of service
* @return corresponding {@link PartnerLinkMyRoleImpl}
*/
private PartnerLinkMyRoleImpl getPartnerLinkForService(QName serviceName) {
assert _hydrationLatch.isLatched(1);
PartnerLinkMyRoleImpl target = null;
for (Endpoint endpoint : _endpointToMyRoleMap.keySet())
if (endpoint.serviceName.equals(serviceName))
target = _endpointToMyRoleMap.get(endpoint);
return target;
}
public boolean isInMemory() {
return _pconf.isTransient();
}
public long getLastUsed() {
return _lastUsed;
}
/**
* Get a hint as to whether this process is hydrated. Note this is only a hint, since things could change.
*/
public boolean hintIsHydrated() {
return _processModel != null;
}
/** Keep track of the time the process was last used. */
private final void markused() {
_lastUsed = System.currentTimeMillis();
}
/**
* If necessary, create an object in the data store to represent the process. We'll re-use an existing object if it already
* exists and matches the GUID.
*/
private void bounceProcessDAO(BpelDAOConnection conn, final QName pid, final long version, final ProcessModel mprocess) {
deleteProcessDAO(conn, pid, version, mprocess);
createProcessDAO(conn, pid, version, mprocess);
}
private void deleteProcessDAO(BpelDAOConnection conn, final QName pid, final long version, final ProcessModel mprocess) {
__log.debug("Creating process DAO for " + pid + " (guid=" + mprocess.getGuid() + ")");
try {
ProcessDAO old = conn.getProcess(pid);
if (old != null) {
__log.debug("Found ProcessDAO for " + pid + " with GUID " + old.getGuid());
if (mprocess.getGuid() == null) {
// No guid, old version assume its good
} else {
if (old.getGuid().equals(mprocess.getGuid())) {
// Guids match, no need to create
} else {
// GUIDS dont match, delete and create new
String errmsg = "ProcessDAO GUID " + old.getGuid() + " does not match " + mprocess.getGuid() + "; replacing.";
__log.debug(errmsg);
old.delete();
}
}
}
} catch (BpelEngineException ex) {
throw ex;
} catch (Exception dce) {
__log.error("DbError", dce);
throw new BpelEngineException("DbError", dce);
}
}
private void createProcessDAO(BpelDAOConnection conn, final QName pid, final long version, final ProcessModel mprocess) {
__log.debug("Creating process DAO for " + pid + " (guid=" + mprocess.getGuid() + ")");
try {
boolean create = true;
ProcessDAO old = conn.getProcess(pid);
if (old != null) {
__log.debug("Found ProcessDAO for " + pid + " with GUID " + old.getGuid());
if (mprocess.getGuid() == null) {
// No guid, old version assume its good
create = false;
} else {
if (old.getGuid().equals(mprocess.getGuid())) {
// Guids match, no need to create
create = false;
} else {
// GUIDS dont match, delete and create new
String errmsg = "ProcessDAO GUID " + old.getGuid() + " does not match " + mprocess.getGuid() + "; replacing.";
__log.debug(errmsg);
}
}
}
if (create) {
ProcessDAO newDao = conn.createProcess(pid, mprocess.getQName(), mprocess.getGuid(), (int) version);
for (String correlator : mprocess.getCorrelators()) {
newDao.addCorrelator(correlator);
}
}
} catch (BpelEngineException ex) {
throw ex;
} catch (Exception dce) {
__log.error("DbError", dce);
throw new BpelEngineException("DbError", dce);
}
}
MessageExchangeDAO createMessageExchange(String mexId, final char dir) {
if (isInMemory()) {
return _inMemDao.getConnection().createMessageExchange(mexId, dir);
} else {
return _contexts.dao.getConnection().createMessageExchange(mexId, dir);
}
}
MessageExchangeDAO getInMemMexDAO(String mexId) {
return _inMemDao.getConnection().getMessageExchange(mexId);
}
public void releaseMessageExchange(String mexId) {
if (isInMemory()) {
_inMemDao.getConnection().releaseMessageExchange(mexId);
}
}
/**
* Schedule process-level work. This method defers to the server to do the scheduling and wraps the {@link Runnable} in a
* try-finally block that ensures that the process is hydrated.
*
* @param runnable
*/
void scheduleRunnable(final Runnable runnable) {
if (__log.isDebugEnabled()) __log.debug("schedulingRunnable for process " + _pid + ": " + runnable);
_server.scheduleRunnable(new ProcessRunnable(runnable));
}
void enqueueRunnable(BpelInstanceWorker worker) {
if (__log.isDebugEnabled()) __log.debug("enqueuRunnable for process " + _pid + ": " + worker);
_server.enqueueRunnable(new ProcessRunnable(worker));
}
MyRoleMessageExchange createNewMyRoleMex(final InvocationStyle istyle, final QName targetService, final String operation) {
final String mexId = new GUID().toString();
_hydrationLatch.latch(1);
try {
final PartnerLinkMyRoleImpl target = getPartnerLinkForService(targetService);
if (target == null)
throw new BpelEngineException("NoSuchService: " + targetService);
final Operation op = target._plinkDef.getMyRoleOperation(operation);
if (op == null)
throw new BpelEngineException("NoSuchOperation: " + operation);
return newMyRoleMex(istyle, mexId, target._endpoint.serviceName, target._plinkDef, op);
} finally {
_hydrationLatch.release(1);
}
}
void onMyRoleMexAck(MessageExchangeDAO mexdao, Status old) {
if (mexdao.getPipedMessageExchangeId() != null) /* p2p */{
ODEProcess caller = _server.getBpelProcess(mexdao.getPipedPID());
// process no longer deployed....
if (caller == null) return;
MessageExchangeDAO pmex = caller.loadMexDao(mexdao.getPipedMessageExchangeId());
// Mex no longer there.... odd..
if (pmex == null) return;
// Need to copy the response and state from myrolemex --> partnerrolemex
boolean compat = !(caller.isInMemory() ^ isInMemory());
if (compat) {
// both processes are in-mem or both are persisted, can share the message
pmex.setResponse(mexdao.getResponse());
} else /* one process in-mem, other persisted */{
MessageDAO presponse = pmex.createMessage(mexdao.getResponse().getType());
presponse.setData(mexdao.getResponse().getData());
presponse.setHeader(mexdao.getResponse().getHeader());
pmex.setResponse(presponse);
}
pmex.setFault(mexdao.getFault());
pmex.setStatus(mexdao.getStatus());
pmex.setAckType(mexdao.getAckType());
pmex.setFailureType(mexdao.getFailureType());
if (old == Status.ASYNC) caller.p2pWakeup(pmex);
} else /* not p2p */{
// Do an Async wakeup if we are in the ASYNC state. If we're not, we'll pick up the ACK when we unwind
// the stack.
if (old == Status.ASYNC) {
MyRoleMessageExchangeImpl mymex = _myRoleMexCache.get(mexdao, this);
mymex.onAsyncAck(mexdao);
try {
_contexts.mexContext.onMyRoleMessageExchangeStateChanged(mymex);
} catch (Throwable t) {
__log.error("Integration layer threw an unexepcted exception.", t);
}
}
}
}
/**
* Read an {@link org.apache.ode.bpel.rtrep.v2.OProcess} representation from a stream.
*
* @param is input stream
* @return deserialized process representation
* @throws java.io.IOException
* @throws ClassNotFoundException
*/
private ProcessModel deserializeCompiledProcess(InputStream is) throws IOException, ClassNotFoundException {
ProcessModel compiledProcess;
Serializer ofh = new Serializer(is);
compiledProcess = (ProcessModel) ofh.readPModel();
return compiledProcess;
}
class ProcessRunnable implements Runnable {
Runnable _work;
ProcessRunnable(Runnable work) {
_work = work;
}
public void run() {
_hydrationLatch.latch(1);
try {
_work.run();
} finally {
_hydrationLatch.release(1);
}
}
}
class ProcessCallable<T> implements Callable<T> {
Callable<T> _work;
ProcessCallable(Callable<T> work) {
_work = work;
}
public T call() throws Exception {
_hydrationLatch.latch(1);
try {
return _work.call();
} finally {
_hydrationLatch.release(1);
}
}
}
class HydrationLatch extends NStateLatch {
HydrationLatch() {
super(new Runnable[2]);
_transitions[0] = new Runnable() {
public void run() {
doDehydrate();
}
};
_transitions[1] = new Runnable() {
public void run() {
doHydrate();
}
};
}
private void doDehydrate() {
_processModel = null;
_partnerRoles = null;
_myRoles = null;
_endpointToMyRoleMap = null;
}
private void doHydrate() {
markused();
try {
InputStream inputStream = _pconf.getCBPInputStream();
try {
_processModel = deserializeCompiledProcess(inputStream);
} finally {
inputStream.close();
}
} catch (Exception e) {
String errmsg = "Error reloading compiled process " + _pconf.getProcessId() + "; the file appears to be corrupted.";
__log.error(errmsg);
throw new BpelEngineException(errmsg, e);
}
_runtime = buildRuntime(_processModel.getModelVersion());
_runtime.init(_pconf, _processModel);
setRoles(_processModel);
initExternalVariables();
if (!_hydratedOnce) {
for (PartnerLinkPartnerRoleImpl prole : _partnerRoles.values()) {
if (prole._initialPartner != null) {
PartnerRoleChannel channel = _contexts.bindingContext.createPartnerRoleChannel(_pid,
prole._plinkDef.getPartnerRolePortType(), prole._initialPartner);
prole._channel = channel;
_partnerChannels.put(prole._initialPartner, prole._channel);
EndpointReference epr = channel.getInitialEndpointReference();
if (epr != null) {
prole._initialEPR = epr;
_partnerEprs.put(prole._initialPartner, epr);
}
__log.debug("Activated " + _pid + " partnerrole " + prole.getPartnerLinkName() + ": EPR is "
+ prole._initialEPR);
}
}
_hydratedOnce = true;
}
for (PartnerLinkMyRoleImpl myrole : _myRoles.values()) {
myrole._initialEPR = _myEprs.get(myrole._endpoint);
}
for (PartnerLinkPartnerRoleImpl prole : _partnerRoles.values()) {
prole._channel = _partnerChannels.get(prole._initialPartner);
if (_partnerEprs.get(prole._initialPartner) != null) {
prole._initialEPR = _partnerEprs.get(prole._initialPartner);
}
}
if (isInMemory()) {
bounceProcessDAO(_inMemDao.getConnection(), _pid, _pconf.getVersion(), _processModel);
} else if (_contexts.isTransacted()) {
// If we have a transaction, we do this in the current transaction.
bounceProcessDAO(_contexts.dao.getConnection(), _pid, _pconf.getVersion(), _processModel);
} else {
// If we do not have a transaction we need to create one.
try {
_contexts.execTransaction(new Callable<Object>() {
public Object call() throws Exception {
deleteProcessDAO(_contexts.dao.getConnection(), _pid, _pconf.getVersion(), _processModel);
return null;
}
});
_contexts.execTransaction(new Callable<Object>() {
public Object call() throws Exception {
createProcessDAO(_contexts.dao.getConnection(), _pid, _pconf.getVersion(), _processModel);
return null;
}
});
} catch (Exception ex) {
String errmsg = "DbError";
__log.error(errmsg, ex);
throw new BpelEngineException(errmsg, ex);
}
}
}
}
public String scheduleWorkEvent(WorkEvent we, Date timeToFire) {
// if (isInMemory())
// throw new InvalidProcessException("In-mem process execution resulted in event scheduling.");
return _contexts.scheduler.schedulePersistedJob(we.getDetail(), timeToFire);
}
void invokePartner(MessageExchangeDAO mexdao) {
PartnerLinkModel oplink = _processModel.getPartnerLink(mexdao.getPartnerLinkModelId());
PartnerLinkPartnerRoleImpl partnerRole = _partnerRoles.get(oplink);
Endpoint partnerEndpoint = getInitialPartnerRoleEndpoint(oplink);
List<ODEProcess> p2pProcesses = null;
if (partnerEndpoint != null)
p2pProcesses = _server.route(partnerEndpoint.serviceName, new DbBackedMessageImpl(mexdao.getRequest()));
Operation operation = oplink.getPartnerRoleOperation(mexdao.getOperation());
if (!processInterceptors(mexdao, InterceptorInvoker.__onPartnerInvoked)) {
__log.debug("Partner invocation intercepted.");
return;
}
mexdao.setStatus(Status.REQ);
try {
if (p2pProcesses != null && p2pProcesses.size() != 0) {
/* P2P (process-to-process) invocation, special logic */
// First, make a copy of the original request message
MessageDAO request = mexdao.getRequest();
// Then, iterate over each subscribing process
for (ODEProcess p2pProcess : p2pProcesses) {
// Clone the request message for this subscriber
MessageDAO clone = mexdao.createMessage(request.getType());
clone.setData((Element) request.getData().cloneNode(true));
clone.setHeader((Element) request.getHeader().cloneNode(true));
// Set the request on the MEX to the clone
mexdao.setRequest(clone);
// Send the cloned message to the subscribing process
invokeP2P(p2pProcess, partnerEndpoint.serviceName, operation, mexdao);
}
} else {
partnerRole.invokeIL(mexdao);
// Scheduling a verification to see if the invoke has really been processed. Otherwise
// we put it in activity recovery mode (case of a server crash during invocation).
scheduleInvokeCheck(mexdao);
}
} finally {
if (mexdao.getStatus() != Status.ACK)
mexdao.setStatus(Status.ASYNC);
}
assert mexdao.getStatus() == Status.ACK || mexdao.getStatus() == Status.ASYNC;
}
private void scheduleInvokeCheck(MessageExchangeDAO mex) {
boolean isTwoWay = mex.getPattern() ==
org.apache.ode.bpel.iapi.MessageExchange.MessageExchangePattern.REQUEST_RESPONSE;
if (!isInMemory() && isTwoWay) {
if (__log.isDebugEnabled()) __log.debug("Creating invocation check event for mexid " + mex.getMessageExchangeId());
WorkEvent event = new WorkEvent();
event.setMexId(mex.getMessageExchangeId());
event.setProcessId(getPID());
event.setType(WorkEvent.Type.INVOKE_CHECK);
Date future = new Date(System.currentTimeMillis() + (180 * 1000));
String jobId = scheduleWorkEvent(event, future);
mex.setProperty("invokeCheckJobId", jobId);
}
}
/**
* Invoke a partner process directly (via the engine), bypassing the Integration Layer. Obviously this can only be used when an
* process is partners with another process hosted on the same engine.
*
* @param operation
* @param outgoingMessage
* @param partnerRoleMex
*/
private void invokeP2P(ODEProcess target, QName serviceName, Operation operation, MessageExchangeDAO partnerRoleMex) {
if (ODEProcess.__log.isDebugEnabled())
__log.debug("Invoking in a p2p interaction, partnerrole " + partnerRoleMex.getMessageExchangeId()
+ " target=" + target);
partnerRoleMex.setInvocationStyle(
Boolean.parseBoolean(
partnerRoleMex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_TRANSACTED))
? InvocationStyle.P2P_TRANSACTED
: InvocationStyle.P2P);
// Plumbing
MessageExchangeDAO myRoleMex = target.createMessageExchange(new GUID().toString(),
MessageExchangeDAO.DIR_PARTNER_INVOKES_MYROLE);
myRoleMex.setStatus(Status.REQ);
myRoleMex.setCallee(serviceName);
myRoleMex.setOperation(partnerRoleMex.getOperation());
myRoleMex.setPattern(partnerRoleMex.getPattern());
myRoleMex.setTimeout(partnerRoleMex.getTimeout());
myRoleMex.setRequest(partnerRoleMex.getRequest());
myRoleMex.setInvocationStyle(partnerRoleMex.getInvocationStyle());
// Piped cross-references.
myRoleMex.setPipedMessageExchangeId(partnerRoleMex.getMessageExchangeId());
myRoleMex.setPipedPID(getPID());
partnerRoleMex.setPipedPID(target.getPID());
partnerRoleMex.setPipedMessageExchangeId(myRoleMex.getMessageExchangeId());
setStatefulEPRs(partnerRoleMex, myRoleMex);
// A classic P2P interaction is considered reliable. The invocation should take place
// in the local transaction but the invoked process is not supposed to hold our thread
// and the reply should come in a separate transaction.
target.invokeProcess(myRoleMex);
}
private OdeRuntime buildRuntime(int modelVersion) {
// Relying on package naming conventions to find our runtime
String qualifiedName = "org.apache.ode.bpel.rtrep.v" + modelVersion + ".RuntimeImpl";
try {
OdeRuntime runtime = (OdeRuntime) Class.forName(qualifiedName).newInstance();
runtime.setExtensionRegistry(_contexts.extensionRegistry);
return runtime;
} catch (Exception e) {
throw new RuntimeException("Couldn't instantiate ODE runtime version " + modelVersion +
", either your process definition version is outdated or we have a bug.");
}
}
void setStatefulEPRs(MessageExchangeDAO partnerRoleMex) {
setStatefulEPRs(partnerRoleMex, null);
}
private void setStatefulEPRs(MessageExchangeDAO partnerRoleMex, MessageExchangeDAO myRoleMex) {
// Properties used by stateful-exchange protocol.
String mySessionId = partnerRoleMex.getPartnerLink().getMySessionId();
String partnerSessionId = partnerRoleMex.getPartnerLink().getPartnerSessionId();
if (ODEProcess.__log.isDebugEnabled())
__log.debug("Setting myRoleMex session ids for p2p interaction, mySession " + partnerSessionId
+ " - partnerSess " + mySessionId);
if (mySessionId != null) {
partnerRoleMex.setProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID, mySessionId);
if (myRoleMex != null) myRoleMex.setProperty(MessageExchange.PROPERTY_SEP_PARTNERROLE_SESSIONID, mySessionId);
}
if (partnerSessionId != null) {
partnerRoleMex.setProperty(MessageExchange.PROPERTY_SEP_PARTNERROLE_SESSIONID, partnerSessionId);
if (myRoleMex != null) myRoleMex.setProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID, partnerSessionId);
}
if (__log.isDebugEnabled())
__log.debug("INVOKE PARTNER (SEP): sessionId=" + mySessionId + " partnerSessionId=" + partnerSessionId);
}
/**
* Handle in-line P2P responses. Called from the child's transaction.
*
* @param prolemex
*/
private void p2pWakeup(final MessageExchangeDAO prolemex) {
try {
doInstanceWork(prolemex.getInstance().getInstanceId(), new Callable<Void>() {
public Void call() throws Exception {
executeContinueInstancePartnerRoleResponseReceived(prolemex);
return null;
}
});
} catch (Exception ex) {
throw new BpelEngineException(ex);
}
}
public Node getProcessProperty(QName propertyName) {
Map<QName, Node> properties = _pconf.getProcessProperties();
if (properties != null) {
return properties.get(propertyName);
}
return null;
}
}