/*
* 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.tuscany.sca.implementation.bpel.ode;
import java.util.List;
import java.util.concurrent.Callable;
import javax.wsdl.Part;
import javax.xml.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.iapi.Message;
import org.apache.ode.bpel.iapi.MessageExchange;
import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange;
import org.apache.ode.bpel.iapi.Scheduler;
import org.apache.ode.utils.DOMUtils;
import org.apache.tuscany.sca.assembly.ComponentReference;
import org.apache.tuscany.sca.assembly.EndpointReference;
import org.apache.tuscany.sca.interfacedef.Operation;
import org.apache.tuscany.sca.interfacedef.wsdl.WSDLInterface;
import org.apache.tuscany.sca.runtime.RuntimeComponent;
import org.apache.tuscany.sca.runtime.RuntimeComponentReference;
import org.apache.tuscany.sca.runtime.RuntimeEndpointReference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Helper Class to handle invocation to Tuscany Component References
*
* @version $Rev: 881883 $ $Date: 2009-11-18 19:06:40 +0000 (Wed, 18 Nov 2009) $
*/
public class ODEExternalService {
protected final Log __log = LogFactory.getLog(getClass());
private EmbeddedODEServer _server;
private Scheduler _sched;
public ODEExternalService(EmbeddedODEServer server) {
this._server = server;
this._sched = _server.getScheduler();
}
public void invoke(final PartnerRoleMessageExchange partnerRoleMessageExchange) {
boolean isTwoWay =
partnerRoleMessageExchange.getMessageExchangePattern() == org.apache.ode.bpel.iapi.MessageExchange.MessageExchangePattern.REQUEST_RESPONSE;
if (isTwoWay) {
// Defer the invoke until the transaction commits.
_sched.registerSynchronizer(new Scheduler.Synchronizer() {
public void beforeCompletion() {
}
public void afterCompletion(boolean success) {
// If the TX is rolled back, then we don't send the request.
if (!success) return;
// The invocation must happen in a separate thread, holding on the afterCompletion
// blocks other operations that could have been listed there as well.
_server.getExecutor().submit(new Callable<Object>() {
public Object call() throws Exception {
try {
// do execution
if(! (partnerRoleMessageExchange.getChannel() instanceof TuscanyPRC)) {
throw new IllegalArgumentException("Channel should be an instance of TuscanyPRC");
}
TuscanyPRC channel = (TuscanyPRC) partnerRoleMessageExchange.getChannel();
RuntimeComponent tuscanyRuntimeComponent = _server.getTuscanyRuntimeComponent(channel.getProcessName());
// Fetching the reference based on the data held in the PRC / Endpoint
String refName = channel.getEndpoint().serviceName.getLocalPart();
RuntimeComponentReference runtimeComponentReference = getReferenceByName( tuscanyRuntimeComponent, refName );
RuntimeEndpointReference epr = getEndpointReference( runtimeComponentReference, partnerRoleMessageExchange );
// convert operations
Operation operation =
findOperation(partnerRoleMessageExchange.getOperation().getName(), epr);
/*
This is how a request looks like (payload is wrapped with extra info)
<?xml version="1.0" encoding="UTF-8"?>
<message>
<parameters>
<getGreetings xmlns="http://greetings">
<message xmlns="http://helloworld">Luciano</message>
</getGreetings>
</parameters>
</message>
*/
Element msg = partnerRoleMessageExchange.getRequest().getMessage();
if (msg != null) {
if(__log.isDebugEnabled()) {
String xml = DOMUtils.domToString(msg);
String payload = DOMUtils.domToString(getPayload(partnerRoleMessageExchange.getRequest()));
__log.debug("Starting invocation of SCA Reference");
__log.debug(">>> Original message: " + xml);
__log.debug(">>> Payload: " + payload);
} // end if
Object[] args = new Object[] {getPayload(partnerRoleMessageExchange.getRequest())};
Object result = null;
boolean success = false;
try {
result = epr.invoke(operation, args);
success = true;
} catch (Exception e) {
e.printStackTrace();
partnerRoleMessageExchange.replyWithFailure(MessageExchange.FailureType.OTHER,
e.getMessage(),
null);
} // end try
if(__log.isDebugEnabled()) {
__log.debug("SCA Reference invocation finished");
__log.debug(">>> Result : " + DOMUtils.domToString((Element)result));
} // end if
if (!success) { return null; }
// two way invocation
// process results based on type of message invocation
replyTwoWayInvocation(partnerRoleMessageExchange.getMessageExchangeId(),
operation,
(Element)result);
} // end if
} catch (Throwable t) {
// some error
String errmsg = "Error sending message (mex=" + partnerRoleMessageExchange + "): " + t.getMessage();
__log.error(errmsg, t);
/*replyWithFailure(partnerRoleMessageExchange.getMessageExchangeId(),
MessageExchange.FailureType.COMMUNICATION_ERROR,
errmsg,
null);*/
}
return null;
}
});
}
});
partnerRoleMessageExchange.replyAsync();
} else {
/** one-way case * */
_server.getExecutor().submit(new Callable<Object>() {
public Object call() throws Exception {
// do reply
// operationClient.execute(false);
return null;
}
});
partnerRoleMessageExchange.replyOneWayOk();
}
}
/**
* Gets a RuntimeComponentReference of a supplied RuntimeComponent by name
* @param tuscanyRuntimeComponent - the runtime component
* @param name - the name of the reference
* @return - the RuntimeComponentReference with the supplied name - null if there is no reference with that name
*/
private RuntimeComponentReference getReferenceByName( RuntimeComponent tuscanyRuntimeComponent, String name ) {
if( name == null ) return null;
for( ComponentReference reference : tuscanyRuntimeComponent.getReferences() ) {
if( name.equals(reference.getName()) ) return (RuntimeComponentReference)reference;
} // end for
return null;
} // end method getReferenceByName
/**
* Get the Runtime Wire for the supplied reference
* @param componentReference - the reference
* @return - the RuntimeWire - null if it cannot be found
*/
private RuntimeEndpointReference getEndpointReference( RuntimeComponentReference componentReference,
PartnerRoleMessageExchange mex) {
if( componentReference.isForCallback() ) {
// Where there is a callback, it is necessary to create a specialized wire, based on callback information
// present on the forward call
// Get the callbackEPR for the callback using the BPEL Process ID and the Reference name
// - which is the same name as the service name for a callback
Long processID = _server.getProcessIDFromMex(mex.getMessageExchangeId());
org.apache.tuscany.sca.assembly.EndpointReference callbackEPR =
_server.getCallbackMetadata(processID, componentReference.getName());
RuntimeEndpointReference wire = selectCallbackWire( callbackEPR.getTargetEndpoint(), componentReference );
wire = clone_bind( componentReference, callbackEPR.getCallbackEndpoint() );
return wire;
} else {
// No callback case...
//TODO - fix the x..n multiplicity case, which needs to select the correct ONE of multiple
// EndpointReferences here
return (RuntimeEndpointReference) componentReference.getEndpointReferences().get(0);
} // end if
} // end method getEndpointReference
private RuntimeEndpointReference selectCallbackWire( org.apache.tuscany.sca.assembly.Endpoint endpoint,
RuntimeComponentReference componentReference) {
// Look for callback binding with same name as service binding
if (endpoint == null) {
throw new RuntimeException("Destination for forward call is not available");
}
for (EndpointReference epr : componentReference.getEndpointReferences()) {
if (epr.getBinding().getName().equals(endpoint.getBinding().getName())) {
return (RuntimeEndpointReference) epr;
}
}
// if no match, look for callback binding with same type as service binding
for (EndpointReference epr : componentReference.getEndpointReferences()) {
if (epr.getBinding().getType().equals(endpoint.getBinding().getType())) {
return (RuntimeEndpointReference) epr;
}
}
// no suitable callback wire was found
return null;
} // end method selectCallbackWire
private RuntimeEndpointReference clone_bind(RuntimeComponentReference reference,
org.apache.tuscany.sca.assembly.Endpoint callbackEndpoint) {
try {
// clone the callback reference ready to configure it for this callback endpoint
RuntimeComponentReference ref = (RuntimeComponentReference)reference.clone();
ref.getTargets().clear();
ref.getBindings().clear();
ref.getEndpointReferences().clear();
// clone epr
EndpointReference callbackEndpointReference = (EndpointReference)reference.getEndpointReferences().get(0).clone();
callbackEndpointReference.setReference(ref);
callbackEndpointReference.setTargetEndpoint(callbackEndpoint);
callbackEndpointReference.setUnresolved(true);
// The callback endpoint will be resolved when the wire chains are created
ref.getEndpointReferences().add(callbackEndpointReference);
return (RuntimeEndpointReference) ref.getEndpointReferences().get(0);
} catch ( CloneNotSupportedException e ) {
return null;
} // end try clone_bind
} // end method
/**
* Find the SCA Reference operation
*
* @param operationName
* @param runtimeComponentReference
* @return
*/
private Operation findOperation(String operationName, RuntimeEndpointReference epr) {
Operation reseultOperation = null;
for (Operation operation : epr.getComponentTypeReferenceInterfaceContract().getInterface().getOperations()) {
if (operationName.equalsIgnoreCase(operation.getName())) {
reseultOperation = operation;
break;
}
}
return reseultOperation;
}
/**
* Get payload from a given ODEMessage
* @param odeMessage - the ODE message
* @return the payload of the Message, as a DOM Element
*/
private Element getPayload(Message odeMessage) {
Element payload = null;
// Get the message parts - these correspond to the message parts for the invocation
// as defined in the WSDL for the service operation being invoked
List<String> parts = odeMessage.getParts();
if( parts.size() == 0 ) return null;
// For the present, just deal with the ** FIRST ** part
// TODO Deal with operations that have messages with multiple parts
// - that will require returning an array of Elements, one for each part
Element part = odeMessage.getPart(parts.get(0));
// Get the payload which is the First child
if (part != null && part.hasChildNodes()) {
payload = (Element)part.getFirstChild();
}
return payload;
} // end getPayload
private void replyTwoWayInvocation(final String odeMexId, final Operation operation, final Element result) {
// ODE MEX needs to be invoked in a TX.
try {
_server.getScheduler().execIsolatedTransaction(new Callable<Void>() {
public Void call() throws Exception {
PartnerRoleMessageExchange odeMex = null;
try {
odeMex = (PartnerRoleMessageExchange)_server.getBpelServer().getEngine().getMessageExchange(odeMexId);
if (odeMex != null) {
Message response = createResponseMessage(odeMex, operation, (Element)result);
odeMex.reply(response);
}
} catch (Exception ex) {
String errmsg = "Unable to process response: " + ex.getMessage();
if (odeMex != null) {
odeMex.replyWithFailure(MessageExchange.FailureType.OTHER, errmsg, null);
}
}
return null;
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
private Message createResponseMessage(PartnerRoleMessageExchange partnerRoleMessageExchange,
Operation operation,
Element invocationResult) {
Document dom = DOMUtils.newDocument();
String operationName = operation.getName();
Part bpelOperationOutputPart =
(Part)((WSDLInterface)operation.getInterface()).getPortType().getOperation(operationName, null, null)
.getOutput().getMessage().getParts().values().iterator().next();
Element contentMessage = dom.createElement("message");
Element contentPart = dom.createElement(bpelOperationOutputPart.getName());
contentPart.appendChild(dom.importNode(invocationResult, true));
contentMessage.appendChild(contentPart);
dom.appendChild(contentMessage);
if(__log.isDebugEnabled()) {
__log.debug("Creating result message:");
__log.debug(">>>" + DOMUtils.domToString(dom.getDocumentElement()));
}
QName id = partnerRoleMessageExchange.getOperation().getOutput().getMessage().getQName();
Message response = partnerRoleMessageExchange.createMessage(id);
response.setMessage(dom.getDocumentElement());
return response;
}
}