/*
* 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.runtime;
import java.util.HashSet;
import java.util.Set;
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.evt.VariableModificationEvent;
import org.apache.ode.bpel.obj.OEventHandler;
import org.apache.ode.bpel.obj.OScope;
import org.apache.ode.bpel.runtime.channels.EventHandlerControl;
import org.apache.ode.bpel.runtime.channels.FaultData;
import org.apache.ode.bpel.runtime.channels.ParentScope;
import org.apache.ode.bpel.runtime.channels.PickResponse;
import org.apache.ode.bpel.runtime.channels.Termination;
import org.apache.ode.jacob.CompositeProcess;
import org.apache.ode.jacob.ProcessUtil;
import org.apache.ode.jacob.ReceiveProcess;
import org.apache.ode.jacob.Synch;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Message event handler.
*/
class EH_EVENT extends BpelJacobRunnable {
private static final long serialVersionUID = 1L;
private static final Logger __log = LoggerFactory.getLogger(EH_EVENT.class);
private EventHandlerControl _ehc;
private Termination _tc;
private ParentScope _psc;
private ScopeFrame _scopeFrame;
private OEventHandler.OEvent _oevent;
/** Registered compensation handlers. */
private Set<CompensationHandler> _comps = new HashSet<CompensationHandler>();
private FaultData _fault;
/** Active instances (we can have more than one!) */
private Set<ActivityInfo> _active = new HashSet<ActivityInfo>();
/** Whether a stop has been requested; if so no more new instances. */
private boolean _stopped;
/** Has a termination of this handler been requested. */
private boolean _terminated;
private boolean _childrenTerminated;
EH_EVENT(ParentScope psc,Termination tc, EventHandlerControl ehc, OEventHandler.OEvent o, ScopeFrame scopeFrame) {
_scopeFrame = scopeFrame;
_oevent = o;
_tc = tc;
_psc = psc;
_ehc = ehc;
}
public void run() {
instance(new SELECT());
}
/**
* Terminate all the active activities.
*/
private void terminateActive() {
if (!_childrenTerminated) {
for (ActivityInfo tact : _active) {
replication(tact.self).terminate();
}
_childrenTerminated = true;
}
}
/**
* Template that does the actual selection interaction with the runtime system, and
* then waits on the pick response channel.
*/
class SELECT extends BpelJacobRunnable {
private static final long serialVersionUID = 1L;
/**
* @see org.apache.ode.jacob.JacobRunnable#run()
*/
public void run() {
Selector selector;
try {
PickResponse pickResponseChannel = newChannel(PickResponse.class);
CorrelationKeySet keySet = new CorrelationKeySet();
PartnerLinkInstance pLinkInstance = _scopeFrame.resolve(_oevent.getPartnerLink());
for( OScope.CorrelationSet cset : _oevent.getJoinCorrelations() ) {
if(getBpelRuntimeContext().isCorrelationInitialized(_scopeFrame.resolve(cset))) {
keySet.add(getBpelRuntimeContext().readCorrelation(_scopeFrame.resolve(cset)));
}
}
for( OScope.CorrelationSet cset : _oevent.getMatchCorrelations() ) {
if (!getBpelRuntimeContext().isCorrelationInitialized(_scopeFrame.resolve(cset))) {
throw new FaultException(_oevent.getOwner().getConstants().getQnCorrelationViolation(),"Correlation not initialized.");
}
keySet.add(getBpelRuntimeContext().readCorrelation(_scopeFrame.resolve(cset)));
}
if( keySet.isEmpty() ) {
// Adding a route for opaque correlation. In this case correlation is done on "out-of-band" session id.
String sessionId = getBpelRuntimeContext().fetchMySessionId(pLinkInstance);
keySet.add(new CorrelationKey("-1", new String[] {sessionId}));
}
selector = new Selector(0,pLinkInstance,_oevent.getOperation().getName(), _oevent.getOperation().getOutput() == null, _oevent.getMessageExchangeId(), keySet, _oevent.getRoute());
getBpelRuntimeContext().select(pickResponseChannel, null, false, new Selector[] { selector} );
instance(new WAITING(pickResponseChannel));
} catch(FaultException e){
__log.error("",e);
if (_fault == null) {
_fault = createFault(e.getQName(), _oevent);
}
terminateActive();
instance(new WAITING(null));
}
}
}
/**
* Template that represents the waiting for a pick response.
*/
private class WAITING extends BpelJacobRunnable {
private static final long serialVersionUID = 1L;
private PickResponse _pickResponseChannel;
private WAITING(PickResponse pickResponseChannel) {
_pickResponseChannel = pickResponseChannel;
}
public void run() {
if (!_active.isEmpty() || _pickResponseChannel != null) {
CompositeProcess mlset = ProcessUtil.compose(null);
if (!_terminated) {
mlset.or(new ReceiveProcess() {
private static final long serialVersionUID = 7666910462948788042L;
}.setChannel(_tc).setReceiver(new Termination() {
public void terminate() {
terminateActive();
_terminated = true;
if (_pickResponseChannel != null)
getBpelRuntimeContext().cancel(_pickResponseChannel);
instance(WAITING.this);
}
}));
}
if (!_stopped) {
mlset.or(new ReceiveProcess() {
private static final long serialVersionUID = -1050788954724647970L;
}.setChannel(_ehc).setReceiver(new EventHandlerControl() {
public void stop() {
_stopped = true;
if (_pickResponseChannel != null)
getBpelRuntimeContext().cancel(_pickResponseChannel);
instance(WAITING.this);
}
}));
}
for (final ActivityInfo ai : _active) {
mlset.or(new ReceiveProcess() {
private static final long serialVersionUID = 5341207762415360982L;
}.setChannel(ai.parent).setReceiver(new ParentScope() {
public void compensate(OScope scope, Synch ret) {
_psc.compensate(scope, ret);
instance(WAITING.this);
}
public void completed(FaultData faultData, Set<CompensationHandler> compensations) {
_active.remove(ai);
_comps.addAll(compensations);
if (faultData != null && _fault == null) {
_fault = faultData;
terminateActive();
// ODE-511; needs to clean up the route
if (_pickResponseChannel != null)
getBpelRuntimeContext().cancel(_pickResponseChannel);
_psc.completed(_fault, _comps);
} else
instance(WAITING.this);
}
public void cancelled() { completed(null, CompensationHandler.emptySet()); }
public void failure(String reason, Element data) { completed(null, CompensationHandler.emptySet()); }
}));
}
if (_pickResponseChannel != null)
mlset.or(new ReceiveProcess() {
private static final long serialVersionUID = -4929999153478677288L;
}.setChannel(_pickResponseChannel).setReceiver(new PickResponse() {
public void onRequestRcvd(int selectorIdx, String mexId) {
// The receipt of the message causes a new scope to be created:
ScopeFrame ehScopeFrame = new ScopeFrame(_oevent,
getBpelRuntimeContext().createScopeInstance(_scopeFrame.scopeInstanceId, _oevent),
_scopeFrame,
_comps,
_fault);
if (_oevent.getVariable() != null) {
Element msgEl = getBpelRuntimeContext().getMyRequest(mexId);
if (msgEl != null) {
try {
VariableInstance vinst = ehScopeFrame.resolve(_oevent.getVariable());
getBpelRuntimeContext().writeVariable(vinst, msgEl);
VariableModificationEvent se = new VariableModificationEvent(vinst.declaration.getName());
se.setNewValue(msgEl);
_scopeFrame.fillEventInfo(se);
if (_oevent.getDebugInfo() != null)
se.setLineNo(_oevent.getDebugInfo().getStartLine());
getBpelRuntimeContext().sendEvent(se);
} catch (Exception ex) {
__log.error("",ex);
throw new InvalidProcessException(ex);
}
}
}
try {
for (OScope.CorrelationSet cset : _oevent.getInitCorrelations()) {
initializeCorrelation(ehScopeFrame.resolve(cset), ehScopeFrame.resolve(_oevent.getVariable()));
}
for( OScope.CorrelationSet cset : _oevent.getJoinCorrelations() ) {
// will be ignored if already initialized
initializeCorrelation(ehScopeFrame.resolve(cset), ehScopeFrame.resolve(_oevent.getVariable()));
}
if (_oevent.getPartnerLink().hasPartnerRole()) {
// Trying to initialize partner epr based on a message-provided epr/session.
if (!getBpelRuntimeContext().isPartnerRoleEndpointInitialized(ehScopeFrame
.resolve(_oevent.getPartnerLink())) || !_oevent.getPartnerLink().isInitializePartnerRole()) {
Node fromEpr = getBpelRuntimeContext().getSourceEPR(mexId);
if (fromEpr != null) {
getBpelRuntimeContext().writeEndpointReference(
ehScopeFrame.resolve(_oevent.getPartnerLink()), (Element) fromEpr);
}
}
String partnersSessionId = getBpelRuntimeContext().getSourceSessionId(mexId);
if (partnersSessionId != null)
getBpelRuntimeContext().initializePartnersSessionId(ehScopeFrame.resolve(_oevent.getPartnerLink()),
partnersSessionId);
}
getBpelRuntimeContext().cancelOutstandingRequests(ProcessUtil.exportChannel(_pickResponseChannel));
// this request is now waiting for a reply
getBpelRuntimeContext().processOutstandingRequest(_scopeFrame.resolve(_oevent.getPartnerLink()),
_oevent.getOperation().getName(), _oevent.getMessageExchangeId(), mexId);
} catch (FaultException e) {
__log.error("",e);
if (_fault == null) {
_fault = createFault(e.getQName(), _oevent);
terminateActive();
}
instance(new WAITING(null));
return;
}
// load 'onMessage' activity; we'll do this even if a stop/terminate has been
// requested becasue we cannot undo the receipt of the message at this point.
ActivityInfo child = new ActivityInfo(genMonotonic(),
_oevent.getActivity(),
newChannel(Termination.class), newChannel(ParentScope.class));
_active.add(child);
LinkFrame lf = new LinkFrame(null);
ScopeFrame innerScopeFrame = new ScopeFrame((OScope) _oevent.getActivity(),
getBpelRuntimeContext().createScopeInstance(_scopeFrame.scopeInstanceId, (OScope) _oevent.getActivity()),
ehScopeFrame,
_comps,
_fault);
instance(new SCOPE(child, innerScopeFrame, lf));
// If we previously terminated the other activiites, then we do the same
// here; this is easier then undoing the receive.
if (_childrenTerminated)
replication(child.self).terminate();
if (_terminated || _stopped || _fault != null)
instance(new WAITING(null));
else
instance(new SELECT());
}
public void onTimeout() {
instance(new WAITING(null));
}
public void onCancel() {
instance(new WAITING(null));
}
}));
object(false, mlset);
} else /* Nothing more to do. */ {
_psc.completed(_fault, _comps);
}
}
}
}