org.jbpm.bpel.integration.jms.IntegrationControl.java Source code

Java tutorial

Introduction

Here is the source code for org.jbpm.bpel.integration.jms.IntegrationControl.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the JBPM BPEL PUBLIC LICENSE AGREEMENT as
 * published by JBoss Inc.; either version 1.0 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.jbpm.bpel.integration.jms;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.wsdl.Operation;
import javax.wsdl.Port;
import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.jbpm.JbpmContext;
import org.jbpm.bpel.BpelException;
import org.jbpm.bpel.deploy.DeploymentDescriptor;
import org.jbpm.bpel.deploy.ScopeMatcher;
import org.jbpm.bpel.deploy.PartnerRoleDescriptor.InitiateMode;
import org.jbpm.bpel.endpointref.EndpointReference;
import org.jbpm.bpel.endpointref.EndpointReferenceFactory;
import org.jbpm.bpel.graph.basic.Receive;
import org.jbpm.bpel.graph.def.BpelProcessDefinition;
import org.jbpm.bpel.graph.exe.BpelFaultException;
import org.jbpm.bpel.graph.scope.OnEvent;
import org.jbpm.bpel.graph.scope.Scope;
import org.jbpm.bpel.graph.struct.Pick;
import org.jbpm.bpel.graph.struct.StructuredActivity.Begin;
import org.jbpm.bpel.integration.catalog.DefinitionCatalog;
import org.jbpm.bpel.integration.catalog.ServiceCatalog;
import org.jbpm.bpel.integration.client.Caller;
import org.jbpm.bpel.integration.client.SoapCaller;
import org.jbpm.bpel.integration.def.InvokeAction;
import org.jbpm.bpel.integration.def.PartnerLinkDefinition;
import org.jbpm.bpel.integration.def.ReceiveAction;
import org.jbpm.bpel.integration.def.ReplyAction;
import org.jbpm.bpel.integration.exe.PartnerLinkInstance;
import org.jbpm.bpel.persistence.db.IntegrationSession;
import org.jbpm.bpel.xml.BpelConstants;
import org.jbpm.graph.exe.Token;
import org.jbpm.svc.Services;

/**
 * @author Alejandro Guizar
 * @version $Revision$ $Date: 2008/01/30 07:18:22 $
 */
public class IntegrationControl {

    private final JmsIntegrationServiceFactory integrationServiceFactory;

    private Connection jmsConnection;
    private DeploymentDescriptor deploymentDescriptor;

    private List partnerLinkEntries = Collections.EMPTY_LIST;

    private final List startListeners = new Vector();
    private final Map requestListeners = new Hashtable();
    private final Map outstandingRequests = new Hashtable();
    private final DefinitionCatalog myCatalog = new DefinitionCatalog();

    private final Map callers = new HashMap();

    /**
     * JNDI subcontext that contains JMS objects, relative to the initial context.
     */
    public static final String JMS_CONTEXT = "java:comp/env/jms";

    /** JNDI name bound to the JMS connection factory, relative to {@link #JMS_CONTEXT}. */
    public static final String CONNECTION_FACTORY_NAME = "JbpmConnectionFactory";

    private static final Log log = LogFactory.getLog(IntegrationControl.class);
    private static final long serialVersionUID = 1L;

    IntegrationControl(JmsIntegrationServiceFactory integrationServiceFactory) {
        this.integrationServiceFactory = integrationServiceFactory;
    }

    public JmsIntegrationServiceFactory getIntegrationServiceFactory() {
        return integrationServiceFactory;
    }

    public Connection getJmsConnection() {
        return jmsConnection;
    }

    public Session createJmsSession() throws JMSException {
        return jmsConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
    }

    public DeploymentDescriptor getDeploymentDescriptor() {
        return deploymentDescriptor;
    }

    public void setDeploymentDescriptor(DeploymentDescriptor deploymentDescriptor) {
        this.deploymentDescriptor = deploymentDescriptor;
    }

    public PartnerLinkEntry getPartnerLinkEntry(PartnerLinkDefinition partnerLink) {
        final long partnerLinkId = partnerLink.getId();
        for (int i = 0, n = partnerLinkEntries.size(); i < n; i++) {
            PartnerLinkEntry entry = (PartnerLinkEntry) partnerLinkEntries.get(i);
            if (entry.getId() == partnerLinkId)
                return entry;
        }
        return null;
    }

    public PartnerLinkEntry getPartnerLinkEntry(QName portTypeName, QName serviceName, String portName) {
        PartnerLinkEntry selectedEntry = null;

        for (int i = 0, n = partnerLinkEntries.size(); i < n; i++) {
            PartnerLinkEntry entry = (PartnerLinkEntry) partnerLinkEntries.get(i);
            EndpointReference myReference = entry.getMyReference();

            if (myReference == null)
                continue; // this entry corresponds to a partner link without myRole

            QName myPortTypeName = myReference.getPortTypeName();
            if (!portTypeName.equals(myPortTypeName))
                continue;

            QName myServiceName = myReference.getServiceName();
            if (myServiceName != null && !serviceName.equals(myServiceName))
                continue;

            String myPortName = myReference.getPortName();
            if (myPortName != null && !portName.equals(myPortName))
                continue;

            if (selectedEntry != null) {
                throw new BpelException("multiple partner link entries match the given arguments: service="
                        + serviceName + ", port=" + portName);
            }
            selectedEntry = entry;
        }
        return selectedEntry;
    }

    public Map getRequestListeners() {
        return requestListeners;
    }

    public void addRequestListener(RequestListener requestListener) {
        Object key = createKey(requestListener.getReceiveActionId(), requestListener.getTokenId());
        requestListeners.put(key, requestListener);
    }

    public RequestListener removeRequestListener(ReceiveAction receiveAction, Token token) {
        Object key = createKey(receiveAction.getId(), token.getId());
        return (RequestListener) requestListeners.remove(key);
    }

    public void removeRequestListener(RequestListener requestListener) {
        Object key = createKey(requestListener.getReceiveActionId(), requestListener.getTokenId());
        synchronized (requestListeners) {
            Object currentListener = requestListeners.get(key);
            if (requestListener == currentListener)
                requestListeners.remove(key);
        }
    }

    private static Object createKey(long receiveActionId, long tokenId) {
        return new RequestListener.Key(receiveActionId, tokenId);
    }

    public Map getOutstandingRequests() {
        return outstandingRequests;
    }

    public void addOutstandingRequest(ReceiveAction receiveAction, Token token, OutstandingRequest request) {
        Object key = createKey(receiveAction.getPartnerLink().getInstance(token), receiveAction.getOperation(),
                receiveAction.getMessageExchange());

        if (outstandingRequests.put(key, request) != null)
            throw new BpelFaultException(BpelConstants.FAULT_CONFLICTING_REQUEST);

        log.debug("added outstanding request: receiveAction=" + receiveAction + ", token=" + token + ", request="
                + request);
    }

    public OutstandingRequest removeOutstandingRequest(ReplyAction replyAction, Token token) {
        Object key = createKey(replyAction.getPartnerLink().getInstance(token), replyAction.getOperation(),
                replyAction.getMessageExchange());
        OutstandingRequest request = (OutstandingRequest) outstandingRequests.remove(key);
        if (request == null)
            throw new BpelFaultException(BpelConstants.FAULT_MISSING_REQUEST);

        log.debug("removed outstanding request: replyAction=" + replyAction + ", token=" + token + ", request="
                + request);
        return request;
    }

    private static Object createKey(PartnerLinkInstance partnerLinkInstance, Operation operation,
            String messageExchange) {
        return new OutstandingRequest.Key(getOrAssignId(partnerLinkInstance), operation.getName(), messageExchange);
    }

    private static long getOrAssignId(PartnerLinkInstance partnerLinkInstance) {
        long id = partnerLinkInstance.getId();
        // in case instance is transient, assign an identifier to it
        if (id == 0L) {
            Services.assignId(partnerLinkInstance);
            id = partnerLinkInstance.getId();
        }
        return id;
    }

    public Map getCallers() {
        return callers;
    }

    public Caller createCaller(InvokeAction invokeAction, Token token) {
        PartnerLinkDefinition partnerLinkDefinition = invokeAction.getPartnerLink();
        PartnerLinkInstance partnerLinkInstance = partnerLinkDefinition.getInstance(token);

        // retrieve partner reference
        EndpointReference partnerReference = partnerLinkInstance.getPartnerReference();
        if (partnerReference == null) {
            // create reference whose sole selection criterion is the port type
            partnerReference = createPartnerReference(partnerLinkDefinition);
            partnerLinkInstance.setPartnerReference(partnerReference);
        }

        // select a port from the service catalog with the criteria known at this point
        Port port = partnerReference.selectPort(getPartnerCatalog());
        log.debug("selected port " + port.getName() + " on " + invokeAction + " for " + token);

        // create a client for that port
        Caller caller = new SoapCaller(port);
        callers.put(new Caller.Key(invokeAction.getId(), token.getId()), caller);
        return caller;
    }

    EndpointReference createPartnerReference(PartnerLinkDefinition partnerLink) {
        PartnerLinkEntry entry = getPartnerLinkEntry(partnerLink);
        InitiateMode initiateMode = entry.getInitiateMode();

        EndpointReference partnerReference;
        if (InitiateMode.PULL.equals(initiateMode)) {
            EndpointReferenceFactory referenceFactory = EndpointReferenceFactory
                    .getInstance(IntegrationConstants.DEFAULT_REFERENCE_NAME, null);
            partnerReference = referenceFactory.createEndpointReference();
        } else if (InitiateMode.STATIC.equals(initiateMode))
            partnerReference = entry.getPartnerReference();
        else
            throw new BpelFaultException(BpelConstants.FAULT_UNINITIALIZED_PARTNER_ROLE);

        return partnerReference;
    }

    public Caller removeCaller(InvokeAction invokeAction, Token token) {
        return (Caller) callers.remove(new Caller.Key(invokeAction.getId(), token.getId()));
    }

    public ServiceCatalog getPartnerCatalog() {
        return getDeploymentDescriptor().getServiceCatalog();
    }

    public List getStartListeners() {
        return startListeners;
    }

    public void addStartListener(StartListener startListener) {
        startListeners.add(startListener);
    }

    public void removeStartListener(StartListener startListener) {
        startListeners.remove(startListener);
    }

    public DefinitionCatalog getMyCatalog() {
        return myCatalog;
    }

    /**
     * Prepares inbound message activities annotated to create a process instance for receiving
     * requests.
     */
    public void enableInboundMessageActivities(JbpmContext jbpmContext) throws NamingException, JMSException {
        InitialContext initialContext = new InitialContext();
        try {
            // publish partner link information to JNDI
            BpelProcessDefinition processDefinition = getDeploymentDescriptor().findProcessDefinition(jbpmContext);
            buildPartnerLinkEntries(initialContext, processDefinition);

            // open a jms connection
            openJmsConnection(initialContext);

            try {
                // enable start IMAs
                StartListenersBuilder builder = new StartListenersBuilder(this);
                builder.visit(processDefinition);

                // note: upon creation, start listeners add themselves to this control
                if (startListeners.isEmpty())
                    throw new BpelException(processDefinition + " has no start activities");

                // enable outstanding IMAs
                IntegrationSession integrationSession = IntegrationSession.getContextInstance(jbpmContext);
                JmsIntegrationService integrationService = JmsIntegrationService.get(jbpmContext);

                // receive
                for (Iterator i = integrationSession.findReceiveTokens(processDefinition).iterator(); i
                        .hasNext();) {
                    Token token = (Token) i.next();
                    Receive receive = (Receive) token.getNode();
                    integrationService.jmsReceive(receive.getReceiveAction(), token, this, true);
                }

                // pick
                for (Iterator i = integrationSession.findPickTokens(processDefinition).iterator(); i.hasNext();) {
                    Token token = (Token) i.next();
                    // pick points activity token to begin mark
                    Begin begin = (Begin) token.getNode();
                    Pick pick = (Pick) begin.getCompositeActivity();
                    integrationService.jmsReceive(pick.getOnMessages(), token, this);
                }

                // event
                for (Iterator t = integrationSession.findEventTokens(processDefinition).iterator(); t.hasNext();) {
                    Token token = (Token) t.next();
                    // scope points events token to itself
                    Scope scope = (Scope) token.getNode();
                    List onEvents = scope.getOnEvents();
                    for (int i = 0, n = onEvents.size(); i < n; i++) {
                        OnEvent onEvent = (OnEvent) onEvents.get(i);
                        integrationService.jmsReceive(onEvent.getReceiveAction(), token, this, false);
                    }
                }

                // start message delivery
                jmsConnection.start();
            } catch (JMSException e) {
                jmsConnection.close();
                throw e;
            }
        } finally {
            initialContext.close();
        }
    }

    /**
     * Prevents inbound message activities annotated to create a process instance from further
     * receiving requests.
     */
    public void disableInboundMessageActivities() throws JMSException {
        // disable start IMAs
        synchronized (startListeners) {
            for (int i = 0, n = startListeners.size(); i < n; i++) {
                StartListener startListener = (StartListener) startListeners.get(i);
                startListener.close();
            }
            startListeners.clear();
        }

        // disable outstanding IMAs
        synchronized (requestListeners) {
            for (Iterator i = requestListeners.values().iterator(); i.hasNext();) {
                RequestListener requestListener = (RequestListener) i.next();
                requestListener.close();
            }
            requestListeners.clear();
        }

        // release jms connection
        closeJmsConnection();
    }

    void buildPartnerLinkEntries(InitialContext initialContext, BpelProcessDefinition process)
            throws NamingException {
        // match scopes with their descriptors
        ScopeMatcher scopeMatcher = new ScopeMatcher(process);
        scopeMatcher.visit(getDeploymentDescriptor());
        Map scopeDescriptors = scopeMatcher.getScopeDescriptors();
        // lookup destinations & bind port entries
        PartnerLinkEntriesBuilder builder = new PartnerLinkEntriesBuilder(scopeDescriptors,
                getJmsContext(initialContext), integrationServiceFactory.getRequestDestination());
        builder.visit(process);
        partnerLinkEntries = builder.getPartnerLinkEntries();
    }

    private static Context getJmsContext(InitialContext initialContext) {
        Context jmsContext;
        try {
            jmsContext = (Context) initialContext.lookup(JMS_CONTEXT);
            log.debug("retrieved jms context: " + JMS_CONTEXT);
        } catch (NamingException e) {
            log.debug("jms context not found: " + JMS_CONTEXT);
            jmsContext = initialContext;
            log.debug("fell back on initial context");
        }
        return jmsContext;
    }

    void openJmsConnection(InitialContext initialContext) throws NamingException, JMSException {
        ConnectionFactory jmsConnectionFactory = getConnectionFactory(initialContext);
        jmsConnection = jmsConnectionFactory.createConnection();
    }

    private ConnectionFactory getConnectionFactory(InitialContext initialContext) throws NamingException {
        Context jmsContext = getJmsContext(initialContext);
        ConnectionFactory jmsConnectionFactory;
        try {
            jmsConnectionFactory = (ConnectionFactory) jmsContext.lookup(CONNECTION_FACTORY_NAME);
            log.debug("retrieved jms connection factory: " + CONNECTION_FACTORY_NAME);
        } catch (NamingException e) {
            log.debug("jms connection factory not found: " + CONNECTION_FACTORY_NAME);
            jmsConnectionFactory = integrationServiceFactory.getConnectionFactory();
            if (jmsConnectionFactory == null)
                throw e;
            log.debug("fell back on default connection factory");
        }
        return jmsConnectionFactory;
    }

    void closeJmsConnection() throws JMSException {
        if (jmsConnection != null) {
            jmsConnection.close();
            jmsConnection = null;
        }
    }

    void reset() {
        deploymentDescriptor = null;

        partnerLinkEntries = Collections.EMPTY_LIST;

        startListeners.clear();
        requestListeners.clear();
        outstandingRequests.clear();

        callers.clear();
    }
}