Java tutorial
/* * 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.common.ProcessState; 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.ProcessConf.CLEANUP_CATEGORY; 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.bpel.runtime.InvalidInstanceException; 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.Properties; 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; public 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; } public 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 mexdao Message Exchange DAO. */ 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) { if (__log.isDebugEnabled()) { __log.debug("Acknowledge one-way invokes...."); } 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 (mexdao.getInstance().getState() == ProcessState.STATE_TERMINATED) { throw new InvalidInstanceException("Trying to invoke terminated process instance", InvalidInstanceException.TERMINATED_CAUSE_CODE); } if (op.getOutput() != null) { // If the invoked operation is request-response type it's not good to store the request in // database until suspended instance is resumed. It's good to throw and exception in this case. // Then the client will know that this process is suspended. if (mexdao.getInstance().getState() == ProcessState.STATE_SUSPENDED) { throw new InvalidInstanceException("Trying to invoke suspended instance", InvalidInstanceException.SUSPENDED_CAUSE_CODE); } } 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); } } catch (InvalidInstanceException iie) { 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 (iie.getCauseCode()) { case InvalidInstanceException.TERMINATED_CAUSE_CODE: faultQname = new QName("ode", "TerminatedInstance"); faultDetail.setTextContent(iie.getMessage()); break; case InvalidInstanceException.SUSPENDED_CAUSE_CODE: faultQname = new QName("ode", "SuspendedInstance"); faultDetail.setTextContent(iie.getMessage()); break; default: faultQname = constants.getUnknownFault(); break; } MexDaoUtil.setFaulted(mexdao, faultQname, faultElement); } } catch (BpelEngineException bee) { QName faultQname = 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); faultQname = new QName("ode", "BpelEngineException"); faultDetail.setTextContent(bee.getMessage()); 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); assert mexdao != null; assert mexdao.getInstance() != null; 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(); mexdao.release(true); } 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"); if (jobId != null) _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; } public 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(final String mexId) { if (isInMemory()) { _inMemDao.getConnection().releaseMessageExchange(mexId); } else { if (_contexts.isTransacted()) { _contexts.dao.getConnection().releaseMessageExchange(mexId); } else { // ATT-MRIOU; what's the right way without creating its own transaction for releasing my role mex? try { _contexts.execTransaction(new Callable<Object>() { public Object call() throws Exception { _contexts.dao.getConnection().releaseMessageExchange(mexId); return null; } }); } catch (Exception e) { throw new RuntimeException(e); } } } } /** * 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 */ { if (mexdao.getAckType() != AckType.ONEWAY) { MessageDAO presponse = pmex.getConnection().createMessage(mexdao.getResponse().getType()); presponse.setData(mexdao.getResponse().getData()); presponse.setHeader(mexdao.getResponse().getHeader()); pmex.setResponse(presponse); } else { pmex.setResponse(null); } } 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 (__log.isDebugEnabled()) { __log.debug("ODEProcess#onMyRoleMexAck not p2p block."); } if (old == Status.ASYNC) { if (__log.isDebugEnabled()) { __log.debug("ODEProcess#onMyRoleMexAck not p2p block, old status is 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.getConnection().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); // use a greater timeout to make sure the check job does not get executed while the service invocation is still waiting for a response PartnerLinkModel model = _processModel.getPartnerLink(mex.getPartnerLinkModelId()); long timeout = (long) (getTimeout(model) * 1.5); Date future = new Date(System.currentTimeMillis() + timeout); 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 serviceName * @param operation * @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); if (myRoleMex.getStatus() != Status.ACK) { MexDaoUtil.setFailed(partnerRoleMex, FailureType.NO_RESPONSE, "No Response"); } else { MexDaoUtil.copyMyRoleMexDAOToPartnerRoleMexDAOInP2PInvoke(myRoleMex, partnerRoleMex); } } 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 boolean isCleanupCategoryEnabled(boolean instanceSucceeded, CLEANUP_CATEGORY category) { return _pconf.isCleanupCategoryEnabled(instanceSucceeded, category); } public Set<CLEANUP_CATEGORY> getCleanupCategories(boolean instanceSucceeded) { return _pconf.getCleanupCategories(instanceSucceeded); } public Node getProcessProperty(QName propertyName) { Map<QName, Node> properties = _pconf.getProcessProperties(); if (properties != null) { return properties.get(propertyName); } return null; } public long getTimeout(PartnerLinkModel partnerLink) { // OPartnerLink, PartnerLinkPartnerRoleImpl final PartnerLinkPartnerRoleImpl linkPartnerRole = _partnerRoles.get(partnerLink); long timeout = Properties.DEFAULT_MEX_TIMEOUT; String timeout_property = _pconf.getEndpointProperties(linkPartnerRole._initialEPR) .get(Properties.PROP_MEX_TIMEOUT); if (timeout_property != null) { try { timeout = Long.parseLong(timeout_property); } catch (NumberFormatException e) { if (__log.isWarnEnabled()) __log.warn("Mal-formatted Property: [" + Properties.PROP_MEX_TIMEOUT + "=" + timeout_property + "] Default value (" + timeout + ") will be used"); } } return timeout; } }