org.apache.tuscany.sca.implementation.bpel.ode.ODEExternalService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.tuscany.sca.implementation.bpel.ode.ODEExternalService.java

Source

/*
 * 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;
    }

}