/* * 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.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.wsdl.Operation; import javax.wsdl.PortType; import javax.xml.namespace.QName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.ode.bpel.common.CorrelationKey; import org.apache.ode.bpel.common.CorrelationKeySet; import org.apache.ode.bpel.common.FaultException; import org.apache.ode.bpel.common.InvalidMessageException; import org.apache.ode.bpel.common.OptionalCorrelationKey; import org.apache.ode.bpel.dao.CorrelatorDAO; 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.evt.CorrelationMatchEvent; import org.apache.ode.bpel.evt.CorrelationNoMatchEvent; import org.apache.ode.bpel.evt.NewProcessInstanceEvent; import org.apache.ode.bpel.iapi.Endpoint; import org.apache.ode.bpel.iapi.MessageExchange; import org.apache.ode.bpel.iapi.MyRoleMessageExchange; import org.apache.ode.bpel.iapi.OdeGlobalConfig; import org.apache.ode.bpel.iapi.ProcessState; import org.apache.ode.bpel.iapi.Scheduler.JobDetails; import org.apache.ode.bpel.iapi.Scheduler.JobType; import org.apache.ode.bpel.intercept.InterceptorInvoker; import org.apache.ode.bpel.obj.OMessageVarType; import org.apache.ode.bpel.obj.OPartnerLink; import org.apache.ode.bpel.obj.OProcess; import org.apache.ode.bpel.obj.OScope; import org.apache.ode.bpel.runtime.InvalidProcessException; import org.apache.ode.bpel.runtime.PROCESS; import org.apache.ode.utils.ObjectPrinter; import org.apache.ode.utils.msg.MessageBundle; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * @author Matthieu Riou <mriou at apache dot org> */ public class PartnerLinkMyRoleImpl extends PartnerLinkRoleImpl { private static final Logger __log = LoggerFactory.getLogger(BpelProcess.class); private static final Messages __msgs = MessageBundle.getMessages(Messages.class); /** The local endpoint for this "myrole". */ public Endpoint _endpoint; PartnerLinkMyRoleImpl(BpelProcess process, OPartnerLink plink, Endpoint endpoint) { super(process, plink); _endpoint = endpoint; } public String toString() { StringBuffer buf = new StringBuffer("{PartnerLinkRole-"); buf.append(_plinkDef.getName()); buf.append('.'); buf.append(_plinkDef.getMyRoleName()); buf.append(" on "); buf.append(_endpoint); buf.append('}'); return buf.toString(); } public boolean isCreateInstance(MyRoleMessageExchangeImpl mex) { Operation operation = getMyRoleOperation(mex.getOperationName()); if(operation == null) { return false; } return _plinkDef.isCreateInstanceOperation(operation); } public List<RoutingInfo> findRoute(MyRoleMessageExchangeImpl mex) { List<RoutingInfo> routingInfos = new ArrayList<RoutingInfo>(); if (__log.isTraceEnabled()) { __log.trace(ObjectPrinter.stringifyMethodEnter(this + ":inputMsgRcvd", new Object[] { "messageExchange", mex })); } Operation operation = getMyRoleOperation(mex.getOperationName()); if (operation == null) { // __log.error(__msgs.msgUnknownOperation(mex.getOperationName(), _plinkDef.myRolePortType.getQName())); // mex.setFailure(MessageExchange.FailureType.UNKNOWN_OPERATION, mex.getOperationName(), null); return null; } setMexRole(mex); // now, the tricks begin: when a message arrives we have to see if there // is anyone waiting for it. Get the correlator, a persisted communication-reduction // data structure supporting correlation correlationKey matching! String correlatorId = BpelProcess.genCorrelatorId(_plinkDef, operation.getName()); CorrelatorDAO correlator = _process.getProcessDAO().getCorrelator(correlatorId); CorrelationKeySet keySet; // We need to compute the correlation keys (based on the operation // we can infer which correlation keys to compute - this is merely a set // consisting of each correlationKey used in each correlation sets // that is ever referenced in an <receive>/<onMessage> on this // partnerlink/operation. try { keySet = computeCorrelationKeys(mex); } catch (InvalidMessageException ime) { // We'd like to do a graceful exit here, no sense in rolling back due to a // a message format problem. __log.debug("Unable to evaluate correlation keys, invalid message format. ",ime); mex.setFailure(MessageExchange.FailureType.FORMAT_ERROR, ime.getMessage(), null); return null; } String mySessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID); String partnerSessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_PARTNERROLE_SESSIONID); if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + correlatorId + ": MSG RCVD keys=" + keySet + " mySessionId=" + mySessionId + " partnerSessionId=" + partnerSessionId); } // Try to find a route for one of our keys. List<MessageRouteDAO> messageRoutes = correlator.findRoute(keySet); if (messageRoutes != null && messageRoutes.size() > 0) { for (MessageRouteDAO messageRoute : messageRoutes) { if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + correlatorId + ": ckeySet " + messageRoute.getCorrelationKeySet() + " route is to " + messageRoute); } routingInfos.add(new RoutingInfo(messageRoute, messageRoute.getCorrelationKeySet(), correlator, keySet)); } } if (routingInfos.size() == 0) { routingInfos.add(new RoutingInfo(null, null, correlator, keySet)); } return routingInfos; } public static class RoutingInfo { public MessageRouteDAO messageRoute; public CorrelationKeySet matchedKeySet; public CorrelatorDAO correlator; // CorrelationKey[] keys; public CorrelationKeySet wholeKeySet; public RoutingInfo(MessageRouteDAO messageRoute, CorrelationKeySet matchedKeySet, CorrelatorDAO correlator, CorrelationKeySet wholeKeySet) { this.messageRoute = messageRoute; this.matchedKeySet = matchedKeySet; this.correlator = correlator; this.wholeKeySet = wholeKeySet; } } public void invokeNewInstance(MyRoleMessageExchangeImpl mex, RoutingInfo routing) { Operation operation = getMyRoleOperation(mex.getOperationName()); if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + routing.correlator.getCorrelatorId() + ": routing failed, CREATING NEW INSTANCE"); } ProcessDAO processDAO = _process.getProcessDAO(); if (_process._pconf.getState() == ProcessState.RETIRED) { throw new InvalidProcessException("Process is retired.", InvalidProcessException.RETIRED_CAUSE_CODE); } if (!_process.processInterceptors(mex, InterceptorInvoker.__onNewInstanceInvoked)) { if (__log.isDebugEnabled()) { __log.debug("Not creating a new instance for mex " + mex + "; interceptor prevented!"); } throw new InvalidProcessException("Cannot instantiate process '" + _process.getPID() + "' any more.", InvalidProcessException.TOO_MANY_INSTANCES_CAUSE_CODE); } ProcessInstanceDAO newInstance = processDAO.createInstance(routing.correlator); BpelRuntimeContextImpl instance = _process .createRuntimeContext(newInstance, new PROCESS(_process.getOProcess()), mex); // send process instance event NewProcessInstanceEvent evt = new NewProcessInstanceEvent(new QName(_process.getOProcess().getTargetNamespace(), _process.getOProcess().getName()), _process.getProcessDAO().getProcessId(), newInstance.getInstanceId()); evt.setPortType(mex.getPortType().getQName()); evt.setOperation(operation.getName()); evt.setMexId(mex.getMessageExchangeId()); _process._debugger.onEvent(evt); _process.saveEvent(evt, newInstance); mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.CREATE_INSTANCE); mex.getDAO().setInstance(newInstance); if (mex.getDAO().getCreateTime() == null) mex.getDAO().setCreateTime(instance.getCurrentEventDateTime()); _process._engine.acquireInstanceLock(newInstance.getInstanceId()); instance.execute(); } public void invokeInstance(MyRoleMessageExchangeImpl mex, RoutingInfo routing) { Operation operation = getMyRoleOperation(mex.getOperationName()); if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + routing.correlator.getCorrelatorId() + ": ROUTING to existing instance " + routing.messageRoute.getTargetInstance().getInstanceId()); } ProcessInstanceDAO instanceDao = routing.messageRoute.getTargetInstance(); BpelProcess process2 = _process._engine._activeProcesses.get(instanceDao.getProcess().getProcessId()); // Reload process instance for DAO. BpelRuntimeContextImpl instance = process2.createRuntimeContext(instanceDao, null, null); instance.inputMsgMatch(routing.messageRoute.getGroupId(), routing.messageRoute.getIndex(), mex); // Kill the route so some new message does not get routed to // same process instance. routing.correlator.removeRoutes(routing.messageRoute.getGroupId(), instanceDao); // send process instance event CorrelationMatchEvent evt = new CorrelationMatchEvent(new QName(process2.getOProcess().getTargetNamespace(), process2.getOProcess().getName()), process2.getProcessDAO().getProcessId(), instanceDao.getInstanceId(), routing.matchedKeySet); evt.setPortType(mex.getPortType().getQName()); evt.setOperation(operation.getName()); evt.setMexId(mex.getMessageExchangeId()); process2._debugger.onEvent(evt); // store event process2.saveEvent(evt, instanceDao); mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.MATCHED); mex.getDAO().setInstance(routing.messageRoute.getTargetInstance()); if (mex.getDAO().getCreateTime() == null) mex.getDAO().setCreateTime(instance.getCurrentEventDateTime()); instance.execute(); } public void noRoutingMatch(MyRoleMessageExchangeImpl mex, List<RoutingInfo> routings) { if (!mex.isAsynchronous()) { mex.setFailure(MessageExchange.FailureType.NOMATCH, "No process instance matching correlation keys.", null); if (!OdeGlobalConfig.queueInOutMessages()) { _process.doAsyncReply(mex, null); } } else { // enqueue message with the last message route, as per the comments in caller (@see BpelProcess.invokeProcess()) RoutingInfo routing = (routings != null && routings.size() > 0) ? routings.get(routings.size() - 1) : null; if (routing != null) { if (__log.isDebugEnabled()) { __log.debug("INPUTMSG: " + routing.correlator.getCorrelatorId() + ": SAVING to DB (no match) "); } // send event CorrelationNoMatchEvent evt = new CorrelationNoMatchEvent(mex.getPortType().getQName(), mex .getOperation().getName(), mex.getMessageExchangeId(), routing.wholeKeySet); evt.setProcessId(_process.getProcessDAO().getProcessId()); evt.setProcessName(new QName(_process.getOProcess().getTargetNamespace(), _process.getOProcess().getName())); _process._debugger.onEvent(evt); mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.QUEUED); // No match, means we add message exchange to the queue. routing.correlator.enqueueMessage(mex.getDAO(), routing.wholeKeySet); // Second matcher needs to be registered here JobDetails we = new JobDetails(); we.setType(JobType.MEX_MATCHER); we.setProcessId(_process.getPID()); we.setMexId(mex.getMessageExchangeId()); we.setInMem(_process.isInMemory()); if(_process.isInMemory()){ _process._engine._contexts.scheduler.scheduleVolatileJob(true, we); }else{ _process._engine._contexts.scheduler.schedulePersistedJob(we, null); } } } } private void setMexRole(MyRoleMessageExchangeImpl mex) { Operation operation = getMyRoleOperation(mex.getOperationName()); mex.getDAO().setPartnerLinkModelId(_plinkDef.getId()); mex.setPortOp(_plinkDef.getMyRolePortType(), operation); mex.setPattern(operation.getOutput() == null ? MessageExchange.MessageExchangePattern.REQUEST_ONLY : MessageExchange.MessageExchangePattern.REQUEST_RESPONSE); } private Operation getMyRoleOperation(String operationName) { return _plinkDef.getMyRoleOperation(operationName); } private CorrelationKeySet computeCorrelationKeys(MyRoleMessageExchangeImpl mex) { CorrelationKeySet keySet = new CorrelationKeySet(); Operation operation = mex.getOperation(); Element msg = mex.getRequest().getMessage(); Map<String, Node> headerParts = mex.getRequest().getHeaderParts(); javax.wsdl.Message msgDescription = operation.getInput().getMessage(); Set<OScope.CorrelationSet> csets = _plinkDef.getNonInitiatingCorrelationSetsForOperation(operation); for (OScope.CorrelationSet cset : csets) { CorrelationKey key = computeCorrelationKey(cset, _process.getOProcess().getMessageTypes().get(msgDescription.getQName()), msg, headerParts); keySet.add(key); } csets = _plinkDef.getJoinningCorrelationSetsForOperation(operation); for (OScope.CorrelationSet cset : csets) { CorrelationKey key = computeCorrelationKey(cset, _process.getOProcess().getMessageTypes().get(msgDescription.getQName()), msg, headerParts); keySet.add(key); } // Let's creata a key based on the sessionId String mySessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID); if (mySessionId != null) keySet.add(new CorrelationKey("-1", new String[] { mySessionId })); return keySet; } @SuppressWarnings("unchecked") private CorrelationKey computeCorrelationKey(OScope.CorrelationSet cset, OMessageVarType messagetype, Element msg, Map<String, Node> headerParts) { CorrelationKey key = null; String[] values = new String[cset.getProperties().size()]; int jIdx = 0; for (Iterator j = cset.getProperties().iterator(); j.hasNext(); ++jIdx) { OProcess.OProperty property = (OProcess.OProperty) j.next(); OProcess.OPropertyAlias alias = property.getAlias(messagetype); if (alias == null) { // TODO: Throw a real exception! And catch this at compile // time. throw new IllegalArgumentException("No alias matching property '" + property.getName() + "' with message type '" + messagetype + "'"); } String value; try { value = _process.extractProperty(msg, headerParts, alias, msg.toString()); } catch (FaultException fe) { String emsg = __msgs.msgPropertyAliasDerefFailedOnMessage(alias.getDescription(), fe.getMessage()); __log.error(emsg, fe); throw new InvalidMessageException(emsg, fe); } values[jIdx] = value; } if( cset.isHasJoinUseCases() ) { key = new OptionalCorrelationKey(cset.getName(), values); } else { key = new CorrelationKey(cset.getName(), values); } return key; } @SuppressWarnings("unchecked") public boolean isOneWayOnly() { PortType portType = _plinkDef.getMyRolePortType(); if (portType == null) { return false; } for (Operation operation : (List<Operation>) portType.getOperations()) { if (operation.getOutput() != null) { return false; } } return true; } }