From a72fcfa3adf5be93bcf62e8ed26c08d10939b364 Mon Sep 17 00:00:00 2001 From: lresende Date: Thu, 11 Sep 2008 04:10:13 +0000 Subject: Branch before trunk cleanup git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@694106 13f79535-47bb-0310-9956-ffa450edef68 --- .../jms/provider/AbstractMessageProcessor.java | 117 ++++++ .../binding/jms/provider/JMSBindingInvoker.java | 407 +++++++++++++++++++++ .../binding/jms/provider/JMSBindingListener.java | 232 ++++++++++++ .../jms/provider/JMSBindingProviderFactory.java | 58 +++ .../JMSBindingReferenceBindingProvider.java | 112 ++++++ .../provider/JMSBindingServiceBindingProvider.java | 274 ++++++++++++++ .../binding/jms/provider/JMSMessageProcessor.java | 55 +++ .../jms/provider/JMSMessageProcessorUtil.java | 75 ++++ .../binding/jms/provider/JMSResourceFactory.java | 220 +++++++++++ .../jms/provider/ObjectMessageProcessor.java | 75 ++++ .../binding/jms/provider/TextMessageProcessor.java | 68 ++++ .../jms/provider/XMLTextMessageProcessor.java | 90 +++++ 12 files changed, 1783 insertions(+) create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/AbstractMessageProcessor.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingInvoker.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingListener.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingProviderFactory.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingReferenceBindingProvider.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingServiceBindingProvider.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessor.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessorUtil.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSResourceFactory.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/ObjectMessageProcessor.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/TextMessageProcessor.java create mode 100644 branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/XMLTextMessageProcessor.java (limited to 'branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany') diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/AbstractMessageProcessor.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/AbstractMessageProcessor.java new file mode 100644 index 0000000000..2fcb07a07f --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/AbstractMessageProcessor.java @@ -0,0 +1,117 @@ +/* + * 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.binding.jms.provider; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.Session; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; +import org.osoa.sca.ServiceRuntimeException; + +/** + * Base MessageProcessor for the JMSBinding. + * + * @version $Rev$ $Date$ + */ +public abstract class AbstractMessageProcessor implements JMSMessageProcessor { + + protected String operationPropertyName; + protected boolean xmlFormat = true; + + public AbstractMessageProcessor(JMSBinding jmsBinding) { + this.operationPropertyName = jmsBinding.getOperationSelectorPropertyName(); + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.OperationAndDataBinding#getOperationName(javax.jms.Message) + */ + public String getOperationName(Message message) { + try { + + return message.getStringProperty(operationPropertyName); + + } catch (JMSException e) { + throw new JMSBindingException("Exception retreiving operation name from message", e); + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.OperationAndDataBinding#setOperationName(javax.jms.Message, java.lang.String) + */ + public void setOperationName(String operationName, Message message) { + try { + + message.setStringProperty(operationPropertyName, operationName); + + } catch (JMSException e) { + throw new JMSBindingException("Exception setting the operation name on message", e); + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.OperationAndDataBinding#extractPayload(javax.jms.Session, java.lang.Object) + */ + public Message insertPayloadIntoJMSMessage(Session session, Object o) { + return createJMSMessage(session, o); + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.OperationAndDataBinding#extractPayload(javax.jms.Message) + */ + public Object extractPayloadFromJMSMessage(Message msg) { + try { + if (msg.getBooleanProperty(JMSBindingConstants.FAULT_PROPERTY)) { + throw new ServiceRuntimeException("remote service exception, see nested exception", (Throwable)((ObjectMessage)msg).getObject()); + } + } catch (JMSException e) { + throw new JMSBindingException(e); + } + return extractPayload(msg); + } + + public Message createFaultMessage(Session session, Throwable o) { + try { + + ObjectMessage message = session.createObjectMessage(); + message.setObject(o); + message.setBooleanProperty(JMSBindingConstants.FAULT_PROPERTY, true); + return message; + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + + protected abstract Object[] extractPayload(Message msg); + + protected abstract Message createJMSMessage(Session session, Object o); + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingInvoker.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingInvoker.java new file mode 100644 index 0000000000..a14e2bd018 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingInvoker.java @@ -0,0 +1,407 @@ +/* + * 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.binding.jms.provider; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.naming.NamingException; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.invocation.DataExchangeSemantics; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.runtime.ReferenceParameters; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.osoa.sca.ServiceRuntimeException; + +/** + * Invoker for the JMS binding. + * + * @version $Rev$ $Date$ + */ +public class JMSBindingInvoker implements Invoker, DataExchangeSemantics { + + protected Operation operation; + protected String operationName; + + protected JMSBinding jmsBinding; + protected JMSResourceFactory jmsResourceFactory; + protected JMSMessageProcessor requestMessageProcessor; + protected JMSMessageProcessor responseMessageProcessor; + protected Destination bindingRequestDest; + protected Destination bindingReplyDest; + protected RuntimeComponentReference reference; + + public JMSBindingInvoker(JMSBinding jmsBinding, Operation operation, JMSResourceFactory jmsResourceFactory, RuntimeComponentReference reference) { + + this.operation = operation; + operationName = operation.getName(); + + this.jmsBinding = jmsBinding; + this.jmsResourceFactory = jmsResourceFactory; + this.reference = reference; + this.requestMessageProcessor = JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding); + this.responseMessageProcessor = JMSMessageProcessorUtil.getResponseMessageProcessor(jmsBinding); + + try { + + bindingRequestDest = lookupDestination(); + bindingReplyDest = lookupResponseDestination(); + + } catch (NamingException e) { + throw new JMSBindingException(e); + } + + } + + /** + * Looks up the Destination Queue for the JMS Binding + * + * @return The Destination Queue + * @throws NamingException Failed to lookup Destination Queue + * @throws JMSBindingException Failed to lookup Destination Queue + * @see #lookupDestinationQueue(boolean) + */ + protected Destination lookupDestination() throws NamingException, JMSBindingException { + return lookupDestinationQueue(false); + } + + /** + * Looks up the Destination Response Queue for the JMS Binding + * + * @return The Destination Response Queue + * @throws NamingException Failed to lookup Destination Response Queue + * @throws JMSBindingException Failed to lookup Destination Response Queue + * @see #lookupDestinationQueue(boolean) + */ + protected Destination lookupResponseDestination() throws NamingException, JMSBindingException { + return lookupDestinationQueue(true); + } + + /** + * Looks up the Destination Queue for the JMS Binding. + *

+ * What happens in the look up will depend on the create mode specified for the JMS Binding: + *

+ * See the SCA JMS Binding specification for more information. + *

+ * + * @param isReponseQueue true if we are creating a response queue. + * false if we are creating a request queue + * @return The Destination queue. + * @throws NamingException Failed to lookup JMS queue + * @throws JMSBindingException Failed to lookup JMS Queue. Probable cause is that + * the JMS queue's current existence/non-existence is not compatible with + * the create mode specified on the binding + */ + protected Destination lookupDestinationQueue(boolean isReponseQueue) throws NamingException, JMSBindingException { + String queueName; + String queueType; + String qCreateMode; + + if (isReponseQueue) { + queueName = jmsBinding.getResponseDestinationName(); + queueType = "JMS Response Destination "; + qCreateMode = jmsBinding.getResponseDestinationCreate(); + if (JMSBindingConstants.DEFAULT_RESPONSE_DESTINATION_NAME.equals(queueName)) { + return null; + } + } else { + queueName = jmsBinding.getDestinationName(); + queueType = "JMS Destination "; + qCreateMode = jmsBinding.getDestinationCreate(); + } + + Destination dest = jmsResourceFactory.lookupDestination(queueName); + + if (qCreateMode.equals(JMSBindingConstants.CREATE_ALWAYS)) { + // In this mode, the queue must not already exist as we are creating it + if (dest != null) { + throw new JMSBindingException(queueType + queueName + + " already exists but has create mode of \"" + + qCreateMode + + "\" while registering binding " + + jmsBinding.getName() + + " invoker"); + } + // Create the queue + dest = jmsResourceFactory.createDestination(queueName); + + } else if (qCreateMode.equals(JMSBindingConstants.CREATE_IF_NOT_EXIST)) { + // In this mode, the queue may nor may not exist. It will be created if it does not exist + if (dest == null) { + dest = jmsResourceFactory.createDestination(queueName); + } + + } else if (qCreateMode.equals(JMSBindingConstants.CREATE_NEVER)) { + // In this mode, the queue must have already been created. + if (dest == null) { + throw new JMSBindingException(queueType + queueName + + " not found but create mode of \"" + + qCreateMode + + "\" while registering binding " + + jmsBinding.getName() + + " invoker"); + } + } + + // Make sure we ended up with a queue + if (dest == null) { + throw new JMSBindingException(queueType + queueName + + " not found with create mode of \"" + + qCreateMode + + "\" while registering binding " + + jmsBinding.getName() + + " invoker"); + } + + return dest; + } + + public org.apache.tuscany.sca.invocation.Message invoke(org.apache.tuscany.sca.invocation.Message tuscanyMsg) { + try { + + Object resp = invokeTarget(tuscanyMsg); + tuscanyMsg.setBody(resp); + + } catch (InvocationTargetException e) { + tuscanyMsg.setFaultBody(e.getCause()); + } catch (ServiceRuntimeException e) { + if (e.getCause() instanceof InvocationTargetException) { + if ((e.getCause().getCause() instanceof RuntimeException)) { + tuscanyMsg.setFaultBody(e.getCause()); + } else { + tuscanyMsg.setFaultBody(e.getCause().getCause()); + } + } else { + tuscanyMsg.setFaultBody(e); + } + } catch (Throwable e) { + tuscanyMsg.setFaultBody(e); + } + return tuscanyMsg; + } + + protected Object invokeTarget(org.apache.tuscany.sca.invocation.Message tuscanyMsg) throws InvocationTargetException { + try { + Session session = jmsResourceFactory.createSession(); + try { + + Destination replyToDest = getReplyToDestination(session); + + Message requestMsg = sendRequest(tuscanyMsg, session, replyToDest); + + if (replyToDest == null) { + return null; + } else { + Message replyMsg = receiveReply(session, replyToDest, requestMsg.getJMSMessageID()); + Object[] response = (Object[])responseMessageProcessor.extractPayloadFromJMSMessage(replyMsg); + return (response != null && response.length > 0) ? response[0] : null; + } + + } finally { + session.close(); + } + } catch (JMSException e) { + throw new InvocationTargetException(e); + } catch (NamingException e) { + throw new InvocationTargetException(e); + } + } + + protected Destination getReplyToDestination(Session session) throws JMSException, JMSBindingException, NamingException { + Destination replyToDest; + if (operation.isNonBlocking()) { + replyToDest = null; + } else { + if (bindingReplyDest != null) { + replyToDest = bindingReplyDest; + } else { + replyToDest = session.createTemporaryQueue(); + } + } + return replyToDest; + } + + protected Message sendRequest(org.apache.tuscany.sca.invocation.Message tuscanyMsg, Session session, Destination replyToDest) throws JMSException, JMSBindingException, + NamingException { + + Message requestMsg = requestMessageProcessor.insertPayloadIntoJMSMessage(session, tuscanyMsg.getBody()); + + setHeaders(tuscanyMsg, requestMsg); + + requestMsg.setJMSReplyTo(replyToDest); + + Destination requestDest = getRequestDestination(tuscanyMsg, session); + + MessageProducer producer = session.createProducer(requestDest); + + if (jmsBinding.getOperationJMSTimeToLive(operationName) != null) { + producer.setTimeToLive(jmsBinding.getOperationJMSTimeToLive(operationName)); + } + + try { + producer.send(requestMsg); + } finally { + producer.close(); + } + return requestMsg; + } + + protected Destination getRequestDestination(org.apache.tuscany.sca.invocation.Message tuscanyMsg, Session session) throws JMSBindingException, NamingException, JMSException { + Destination requestDestination; + if (reference.isCallback()) { + String toURI = tuscanyMsg.getTo().getURI(); + if (toURI != null && toURI.startsWith("jms:")) { + // the msg to uri contains the callback destination name + // this is an jms physical name not a jndi name so need to use session.createQueue + requestDestination = session.createQueue(toURI.substring(4)); + } else { + requestDestination = lookupDestination(); + } + } else { + requestDestination = bindingRequestDest; + } + + return requestDestination; + } + + protected void setHeaders(org.apache.tuscany.sca.invocation.Message tuscanyMsg, Message jmsMsg) throws JMSException { + + requestMessageProcessor.setOperationName(jmsBinding.getNativeOperationName(operationName), jmsMsg); + + if (jmsBinding.getOperationJMSDeliveryMode(operationName) != null) { + if (jmsBinding.getOperationJMSDeliveryMode(operationName)) { + jmsMsg.setJMSDeliveryMode(DeliveryMode.PERSISTENT); + } else { + jmsMsg.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + } + + if (jmsBinding.getOperationJMSCorrelationId(operationName) != null) { + jmsMsg.setJMSCorrelationID(jmsBinding.getOperationJMSCorrelationId(operationName)); + } + + if (jmsBinding.getOperationJMSPriority(operationName) != null) { + jmsMsg.setJMSPriority(jmsBinding.getOperationJMSPriority(operationName)); + } + + if (jmsBinding.getOperationJMSType(operationName) != null) { + jmsMsg.setJMSType(jmsBinding.getOperationJMSType(operationName)); + } + + ReferenceParameters parameters = tuscanyMsg.getFrom().getReferenceParameters(); + + Object conversationID = parameters.getConversationID(); + if (conversationID != null) { + jmsMsg.setStringProperty(JMSBindingConstants.CONVERSATION_ID_PROPERTY, conversationID.toString()); + } + + if (hasCallback()) { + + if (parameters.getCallbackID() != null) { + jmsMsg.setStringProperty(JMSBindingConstants.CALLBACK_ID_PROPERTY, parameters.getCallbackID().toString()); + } + + String callbackDestName = getCallbackDestinationName(reference); + if (callbackDestName != null) { + jmsMsg.setStringProperty(JMSBindingConstants.CALLBACK_Q_PROPERTY, callbackDestName); + } + } + + for (String propName : jmsBinding.getPropertyNames()) { + Object value = jmsBinding.getProperty(propName); + jmsMsg.setObjectProperty(propName, value); + } + + Map operationProperties = jmsBinding.getOperationProperties(operationName); + if (operationProperties != null) { + for (String propName : operationProperties.keySet()) { + Object value = operationProperties.get(propName); + jmsMsg.setObjectProperty(propName, value); + } + } + } + + protected boolean hasCallback() { + if (operation.getInterface() instanceof JavaInterface) { + JavaInterface jiface = (JavaInterface)operation.getInterface(); + if (jiface.getCallbackClass() != null) { + return true; + } + } + return false; + } + + protected Message receiveReply(Session session, Destination replyToDest, String requestMsgId) throws JMSException, NamingException { + String msgSelector = "JMSCorrelationID = '" + requestMsgId + "'"; + MessageConsumer consumer = session.createConsumer(replyToDest, msgSelector); + + long receiveWait; + if (jmsBinding.getOperationJMSTimeToLive(operationName) != null) { + receiveWait = jmsBinding.getOperationJMSTimeToLive(operationName) * 2; + } else { + receiveWait = JMSBindingConstants.DEFAULT_TIME_TO_LIVE; + } + + Message replyMsg; + try { + jmsResourceFactory.startConnection(); + replyMsg = consumer.receive(receiveWait); + } finally { + consumer.close(); + } + if (replyMsg == null) { + throw new JMSBindingException("No reply message received on " + replyToDest + " for message id " + requestMsgId); + } + return replyMsg; + } + + protected String getCallbackDestinationName(RuntimeComponentReference reference) { + RuntimeComponentService s = (RuntimeComponentService)reference.getCallbackService(); + JMSBinding b = s.getBinding(JMSBinding.class); + if (b != null) { + JMSBindingServiceBindingProvider bp = (JMSBindingServiceBindingProvider)s.getBindingProvider(b); + return bp.getDestinationName(); + } + return null; + } + + public boolean allowsPassByReference() { + // JMS always pass by value + return true; + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingListener.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingListener.java new file mode 100644 index 0000000000..4701c7b805 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingListener.java @@ -0,0 +1,232 @@ +/* + * 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.binding.jms.provider; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.naming.NamingException; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; +import org.apache.tuscany.sca.core.assembly.EndpointReferenceImpl; +import org.apache.tuscany.sca.core.invocation.MessageImpl; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.runtime.EndpointReference; +import org.apache.tuscany.sca.runtime.ReferenceParameters; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; + +/** + * Listener for the JMSBinding. + * + * @version $Rev$ $Date$ + */ +public class JMSBindingListener implements MessageListener { + + private static final Logger logger = Logger.getLogger(JMSBindingListener.class.getName()); + + private static final String ON_MESSAGE_METHOD_NAME = "onMessage"; + private JMSBinding jmsBinding; + private Binding targetBinding; + private JMSResourceFactory jmsResourceFactory; + private RuntimeComponentService service; + private JMSMessageProcessor requestMessageProcessor; + private JMSMessageProcessor responseMessageProcessor; + private String correlationScheme; + private List serviceOperations; + + public JMSBindingListener(JMSBinding jmsBinding, JMSResourceFactory jmsResourceFactory, RuntimeComponentService service, Binding targetBinding) throws NamingException { + this.jmsBinding = jmsBinding; + this.jmsResourceFactory = jmsResourceFactory; + this.service = service; + this.targetBinding = targetBinding; + requestMessageProcessor = JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding); + responseMessageProcessor = JMSMessageProcessorUtil.getResponseMessageProcessor(jmsBinding); + correlationScheme = jmsBinding.getCorrelationScheme(); + serviceOperations = service.getInterfaceContract().getInterface().getOperations(); + + } + + public void onMessage(Message requestJMSMsg) { + logger.log(Level.FINE, "JMS service '" + service.getName() + "' received message " + requestJMSMsg); + try { + Object responsePayload = invokeService(requestJMSMsg); + sendReply(requestJMSMsg, responsePayload, false); + } catch (Throwable e) { + logger.log(Level.SEVERE, "Exception invoking service '" + service.getName(), e); + sendReply(requestJMSMsg, e, true); + } + } + + /** + * Turn the JMS message back into a Tuscany message and invoke the target component + * + * @param requestJMSMsg + * @return + * @throws JMSException + * @throws InvocationTargetException + */ + protected Object invokeService(Message requestJMSMsg) throws JMSException, InvocationTargetException { + + String operationName = requestMessageProcessor.getOperationName(requestJMSMsg); + Operation operation = getTargetOperation(operationName); + + MessageImpl tuscanyMsg = new MessageImpl(); + tuscanyMsg.setOperation(operation); + if ("onMessage".equals(operation.getName())) { + tuscanyMsg.setBody(requestJMSMsg); + } else { + Object requestPayload = requestMessageProcessor.extractPayloadFromJMSMessage(requestJMSMsg); + tuscanyMsg.setBody(requestPayload); + } + + setHeaderProperties(requestJMSMsg, tuscanyMsg, operation); + + return service.getRuntimeWire(targetBinding).invoke(operation, tuscanyMsg); + } + + protected Operation getTargetOperation(String operationName) { + Operation operation = null; + + if (serviceOperations.size() == 1) { + + // SCA JMS Binding Specification - Rule 1.5.1 line 203 + operation = serviceOperations.get(0); + + } else if (operationName != null) { + + // SCA JMS Binding Specification - Rule 1.5.1 line 205 + for (Operation op : serviceOperations) { + if (op.getName().equals(operationName)) { + operation = op; + break; + } + } + + } else { + + // SCA JMS Binding Specification - Rule 1.5.1 line 207 + for (Operation op : serviceOperations) { + if (op.getName().equals(ON_MESSAGE_METHOD_NAME)) { + operation = op; + break; + } + } + } + + if (operation == null) { + throw new JMSBindingException("Can't find operation " + (operationName != null ? operationName : ON_MESSAGE_METHOD_NAME)); + } + + return operation; + } + + protected void setHeaderProperties(Message requestJMSMsg, MessageImpl tuscanyMsg, Operation operation) throws JMSException { + + EndpointReference from = new EndpointReferenceImpl(null); + tuscanyMsg.setFrom(from); + from.setCallbackEndpoint(new EndpointReferenceImpl("/")); // TODO: whats this for? + ReferenceParameters parameters = from.getReferenceParameters(); + + String conversationID = requestJMSMsg.getStringProperty(JMSBindingConstants.CONVERSATION_ID_PROPERTY); + if (conversationID != null) { + parameters.setConversationID(conversationID); + } + + if (service.getInterfaceContract().getCallbackInterface() != null) { + + String callbackdestName = requestJMSMsg.getStringProperty(JMSBindingConstants.CALLBACK_Q_PROPERTY); + if (callbackdestName == null && operation.isNonBlocking()) { + // if the request has a replyTo but this service operation is oneway but the service uses callbacks + // then use the replyTo as the callback destination + Destination replyTo = requestJMSMsg.getJMSReplyTo(); + if (replyTo != null) { + callbackdestName = (replyTo instanceof Queue) ? ((Queue)replyTo).getQueueName() : ((Topic)replyTo).getTopicName(); + } + } + + if (callbackdestName != null) { + // append "jms:" to make it an absolute uri so the invoker can determine it came in on the request + // as otherwise the invoker should use the uri from the service callback binding + parameters.setCallbackReference(new EndpointReferenceImpl("jms:" + callbackdestName)); + } + + String callbackID = requestJMSMsg.getStringProperty(JMSBindingConstants.CALLBACK_ID_PROPERTY); + if (callbackID != null) { + parameters.setCallbackID(callbackID); + } + } + } + + protected void sendReply(Message requestJMSMsg, Object responsePayload, boolean isFault) { + try { + + if (requestJMSMsg.getJMSReplyTo() == null) { + // assume no reply is expected + if (responsePayload != null) { + logger.log(Level.FINE, "JMS service '" + service.getName() + "' dropped response as request has no replyTo"); + } + return; + } + + Session session = jmsResourceFactory.createSession(); + Message replyJMSMsg; + if (isFault) { + replyJMSMsg = responseMessageProcessor.createFaultMessage(session, (Throwable)responsePayload); + } else { + replyJMSMsg = responseMessageProcessor.insertPayloadIntoJMSMessage(session, responsePayload); + } + + replyJMSMsg.setJMSDeliveryMode(requestJMSMsg.getJMSDeliveryMode()); + replyJMSMsg.setJMSPriority(requestJMSMsg.getJMSPriority()); + + if (correlationScheme == null || JMSBindingConstants.CORRELATE_MSG_ID.equalsIgnoreCase(correlationScheme)) { + replyJMSMsg.setJMSCorrelationID(requestJMSMsg.getJMSMessageID()); + } else if (JMSBindingConstants.CORRELATE_CORRELATION_ID.equalsIgnoreCase(correlationScheme)) { + replyJMSMsg.setJMSCorrelationID(requestJMSMsg.getJMSCorrelationID()); + } + + Destination destination = requestJMSMsg.getJMSReplyTo(); + MessageProducer producer = session.createProducer(destination); + + producer.send(replyJMSMsg); + + producer.close(); + session.close(); + + } catch (JMSException e) { + throw new JMSBindingException(e); + } catch (NamingException e) { + throw new JMSBindingException(e); + } + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingProviderFactory.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingProviderFactory.java new file mode 100644 index 0000000000..00424d32c2 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingProviderFactory.java @@ -0,0 +1,58 @@ +/* + * 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.binding.jms.provider; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.provider.BindingProviderFactory; +import org.apache.tuscany.sca.provider.ReferenceBindingProvider; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.work.WorkScheduler; + +/** + * A factory from creating the JMS binding provider. + * + * @version $Rev$ $Date$ + */ +public class JMSBindingProviderFactory implements BindingProviderFactory { + + private WorkScheduler workScheduler; + + public JMSBindingProviderFactory(ExtensionPointRegistry extensionPoints) { + UtilityExtensionPoint utilities = extensionPoints.getExtensionPoint(UtilityExtensionPoint.class); + workScheduler = utilities.getUtility(WorkScheduler.class); + } + + public ReferenceBindingProvider createReferenceBindingProvider(RuntimeComponent component, RuntimeComponentReference reference, JMSBinding binding) { + return new JMSBindingReferenceBindingProvider(component, reference, binding); + } + + public ServiceBindingProvider createServiceBindingProvider(RuntimeComponent component, RuntimeComponentService service, JMSBinding binding) { + return new JMSBindingServiceBindingProvider(component, service, binding, binding, workScheduler); + } + + public Class getModelType() { + return JMSBinding.class; + } +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingReferenceBindingProvider.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingReferenceBindingProvider.java new file mode 100644 index 0000000000..020829def6 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingReferenceBindingProvider.java @@ -0,0 +1,112 @@ +/* + * 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.binding.jms.provider; + +import java.util.ArrayList; +import java.util.List; + +import javax.jms.JMSException; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.provider.ReferenceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; + +/** + * Implementation of the JMS reference binding provider. + * + * @version $Rev$ $Date$ + */ +public class JMSBindingReferenceBindingProvider implements ReferenceBindingProvider { + + private RuntimeComponentReference reference; + private JMSBinding jmsBinding; + private List jmsBindingInvokers = new ArrayList(); + private JMSResourceFactory jmsResourceFactory; + + public JMSBindingReferenceBindingProvider(RuntimeComponent component, RuntimeComponentReference reference, JMSBinding binding) { + this.reference = reference; + this.jmsBinding = binding; + jmsResourceFactory = new JMSResourceFactory(binding.getConnectionFactoryName(), binding.getInitialContextFactoryName(), binding.getJndiURL()); + + if (XMLTextMessageProcessor.class.isAssignableFrom(JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding).getClass())) { + setXMLDataBinding(reference); + } + + } + + protected void setXMLDataBinding(RuntimeComponentReference reference) { + try { + InterfaceContract ic = (InterfaceContract)reference.getInterfaceContract().clone(); + + Interface ii = (Interface)ic.getInterface().clone(); + ii.resetDataBinding("org.apache.axiom.om.OMElement"); + ic.setInterface(ii); + reference.setInterfaceContract(ic); + + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + public Invoker createInvoker(Operation operation) { + + if (jmsBinding.getDestinationName().equals(JMSBindingConstants.DEFAULT_DESTINATION_NAME)) { + if (!reference.isCallback()) { + throw new JMSBindingException("No destination specified for reference " + reference.getName()); + } + } + + JMSBindingInvoker invoker = new JMSBindingInvoker(jmsBinding, operation, jmsResourceFactory, reference); + jmsBindingInvokers.add(invoker); + return invoker; + } + + public boolean supportsOneWayInvocation() { + return true; + } + + public InterfaceContract getBindingInterfaceContract() { + if (reference.getInterfaceContract() == null) { + return reference.getReference().getInterfaceContract(); + } else { + return reference.getInterfaceContract(); + } + } + + public void start() { + + } + + public void stop() { + try { + jmsResourceFactory.closeConnection(); + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingServiceBindingProvider.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingServiceBindingProvider.java new file mode 100644 index 0000000000..9f5bf68d13 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSBindingServiceBindingProvider.java @@ -0,0 +1,274 @@ +/* + * 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.binding.jms.provider; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.naming.NamingException; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.work.WorkScheduler; + +/** + * Implementation of the JMS service binding provider. + * + * @version $Rev$ $Date$ + */ +public class JMSBindingServiceBindingProvider implements ServiceBindingProvider { + private static final Logger logger = Logger.getLogger(JMSBindingServiceBindingProvider.class.getName()); + + private RuntimeComponentService service; + private Binding targetBinding; + private JMSBinding jmsBinding; + private JMSResourceFactory jmsResourceFactory; + private MessageConsumer consumer; + private WorkScheduler workScheduler; + private boolean running; + + private Destination destination; + + public JMSBindingServiceBindingProvider(RuntimeComponent component, RuntimeComponentService service, Binding targetBinding, JMSBinding binding, WorkScheduler workScheduler) { + this.service = service; + this.jmsBinding = binding; + this.workScheduler = workScheduler; + this.targetBinding = targetBinding; + + jmsResourceFactory = new JMSResourceFactory(binding.getConnectionFactoryName(), binding.getInitialContextFactoryName(), binding.getJndiURL()); + + if (jmsBinding.getDestinationName().equals(JMSBindingConstants.DEFAULT_DESTINATION_NAME)) { + if (!service.isCallback()) { + // use the SCA service name as the default destination name + jmsBinding.setDestinationName(service.getName()); + } + } + + if (XMLTextMessageProcessor.class.isAssignableFrom(JMSMessageProcessorUtil.getRequestMessageProcessor(jmsBinding).getClass())) { + setXMLDataBinding(service); + } + + } + + protected void setXMLDataBinding(RuntimeComponentService service) { + if (service.getInterfaceContract() != null) { + try { + InterfaceContract ic = (InterfaceContract)service.getInterfaceContract().clone(); + Interface ii = ic.getInterface(); + if (ii.getOperations().size() == 1 && "onMessage".equals(ii.getOperations().get(0).getName())) { + return; + } + ii = (Interface)ii.clone(); + ii.resetDataBinding("org.apache.axiom.om.OMElement"); + ic.setInterface(ii); + service.setInterfaceContract(ic); + + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + } + + public InterfaceContract getBindingInterfaceContract() { + return service.getInterfaceContract(); + } + + public boolean supportsOneWayInvocation() { + return true; + } + + public void start() { + this.running = true; + + try { + registerListerner(); + } catch (Exception e) { + throw new JMSBindingException("Error starting JMSServiceBinding", e); + } + } + + public void stop() { + this.running = false; + try { + consumer.close(); + jmsResourceFactory.closeConnection(); + } catch (Exception e) { + // if using an embedded broker then when shutting down Tuscany the broker may get closed + // before this stop method is called. I can't see how to detect that so for now just + // ignore the exception if the message is that the transport is already disposed + if (!"Transport disposed.".equals(e.getMessage())) { + throw new JMSBindingException("Error stopping JMSServiceBinding", e); + } + } + } + + private void registerListerner() throws NamingException, JMSException { + + Session session = jmsResourceFactory.createSession(); + destination = lookupDestinationQueue(); + if (destination == null) { + destination = session.createTemporaryQueue(); + } + + if (jmsBinding.getJMSSelector() != null) { + consumer = session.createConsumer(destination, jmsBinding.getJMSSelector()); + } else { + consumer = session.createConsumer(destination); + } + + final JMSBindingListener listener = new JMSBindingListener(jmsBinding, jmsResourceFactory, service, targetBinding); + try { + + consumer.setMessageListener(listener); + jmsResourceFactory.startConnection(); + + } catch (javax.jms.IllegalStateException e) { + + // setMessageListener not allowed in JEE container so use Tuscany threads + + jmsResourceFactory.startConnection(); + workScheduler.scheduleWork(new Runnable() { + public void run() { + try { + while (running) { + final Message msg = consumer.receive(); + workScheduler.scheduleWork(new Runnable() { + public void run() { + try { + listener.onMessage(msg); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + logger.log(Level.INFO, "JMS " + (service.isCallback() ? "callback service" : "service") + + " '" + + service.getName() + + "' listening on destination " + + ((destination instanceof Queue) ? ((Queue)destination).getQueueName() : ((Topic)destination).getTopicName())); + } + + /** + * Looks up the Destination Queue for the JMS Binding. + *

+ * What happens in the look up will depend on the create mode specified for the JMS Binding: + *

+ * See the SCA JMS Binding specification for more information. + *

+ * + * @return The Destination queue. + * @throws NamingException Failed to lookup JMS queue + * @throws JMSBindingException Failed to lookup JMS Queue. Probable cause is that the JMS queue's current existence/non-existence is not + * compatible with the create mode specified on the binding + */ + private Destination lookupDestinationQueue() throws NamingException, JMSBindingException { + + if (service.isCallback() && JMSBindingConstants.DEFAULT_DESTINATION_NAME.equals(jmsBinding.getDestinationName())) { + // if its a callback service returning null indicates to use a temporary queue + return null; + } + + Destination destination = jmsResourceFactory.lookupDestination(jmsBinding.getDestinationName()); + + String qCreateMode = jmsBinding.getDestinationCreate(); + if (qCreateMode.equals(JMSBindingConstants.CREATE_ALWAYS)) { + // In this mode, the queue must not already exist as we are creating it + if (destination != null) { + throw new JMSBindingException("JMS Destination " + jmsBinding.getDestinationName() + + " already exists but has create mode of \"" + + qCreateMode + + "\" while registering service " + + service.getName() + + " listener"); + } + + // Create the queue + destination = jmsResourceFactory.createDestination(jmsBinding.getDestinationName()); + + } else if (qCreateMode.equals(JMSBindingConstants.CREATE_IF_NOT_EXIST)) { + // In this mode, the queue may nor may not exist. It will be created if it does not exist + if (destination == null) { + destination = jmsResourceFactory.createDestination(jmsBinding.getDestinationName()); + } + + } else if (qCreateMode.equals(JMSBindingConstants.CREATE_NEVER)) { + // In this mode, the queue must have already been created. + if (destination == null) { + throw new JMSBindingException("JMS Destination " + jmsBinding.getDestinationName() + + " not found but create mode of \"" + + qCreateMode + + "\" while registering service " + + service.getName() + + " listener"); + } + } + + // Make sure we ended up with a queue + if (destination == null) { + throw new JMSBindingException("JMS Destination " + jmsBinding.getDestinationName() + + " not found with create mode of \"" + + qCreateMode + + "\" while registering service " + + service.getName() + + " listener"); + } + + return destination; + } + + public String getDestinationName() { + try { + if (destination instanceof Queue) { + return ((Queue)destination).getQueueName(); + } else if (destination instanceof Topic) { + return ((Topic)destination).getTopicName(); + } else { + return null; + } + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessor.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessor.java new file mode 100644 index 0000000000..821b9d7873 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessor.java @@ -0,0 +1,55 @@ +/* + * 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.binding.jms.provider; + +import javax.jms.Message; +import javax.jms.Session; + +/** + * Interface for a component that does operation selection and message payload processing + * + * @version $Rev$ $Date$ + */ +public interface JMSMessageProcessor { + + /** + * Get the operation name from a JMS Message + */ + String getOperationName(Message message); + + /** + * Set the operation name on a JMS Message + */ + void setOperationName(String operationName, Message message); + + /** + * Extracts the payload from a JMS Message + */ + Object extractPayloadFromJMSMessage(Message msg); + + /** + * Create a JMS Message containing the payload + */ + Message insertPayloadIntoJMSMessage(Session session, Object payload); + + /** + * Create a JMS Message for reporting an exception + */ + Message createFaultMessage(Session session, Throwable responsePayload); +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessorUtil.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessorUtil.java new file mode 100644 index 0000000000..699b1cb176 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSMessageProcessorUtil.java @@ -0,0 +1,75 @@ +/* + * 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.binding.jms.provider; + +import java.lang.reflect.Constructor; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; + +/** + * Utility methods to load JMS message processors. + * + * @version $Rev: $ $Date: $ + */ +public class JMSMessageProcessorUtil { + + /** + * Used to create instances of the JMSResourceFactory and RequestMessageProcessor and ResponseMessageProcessor from + * string based class name provided in the configuration + * + * @param cl ClassLoader + * @param className the string based class name to load and instantiate + * @return the new object + */ + private static Object instantiate(ClassLoader cl, String className, JMSBinding binding) { + Object instance; + if (cl == null) { + cl = binding.getClass().getClassLoader(); + } + + try { + Class clazz; + + try { + clazz = cl.loadClass(className); + } catch (ClassNotFoundException e) { + clazz = binding.getClass().getClassLoader().loadClass(className); + } + + Constructor constructor = clazz.getDeclaredConstructor(new Class[] {JMSBinding.class}); + instance = constructor.newInstance(binding); + + } catch (Throwable e) { + throw new JMSBindingException("Exception instantiating OperationAndDataBinding class", e); + } + + return instance; + } + + public static JMSMessageProcessor getRequestMessageProcessor(JMSBinding binding) { + return (JMSMessageProcessor)instantiate(null, binding.getRequestMessageProcessorName(), binding); + } + + public static JMSMessageProcessor getResponseMessageProcessor(JMSBinding binding) { + return (JMSMessageProcessor)instantiate(null, binding.getResponseMessageProcessorName(), binding); + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSResourceFactory.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSResourceFactory.java new file mode 100644 index 0000000000..a1308d98cf --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/JMSResourceFactory.java @@ -0,0 +1,220 @@ +/* + * 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.binding.jms.provider; + +import java.util.Properties; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingConstants; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; + +/** + * Abstracts away any JMS provide specific feature from the JMS binding + * + * @version $Rev$ $Date$ + */ +public class JMSResourceFactory { + + private String initialContextFactoryName; + private String connectionFactoryName = "ConnectionFactory"; + private String jndiURL; + + private Connection connection; + private Context context; + private boolean isConnectionStarted; + + public JMSResourceFactory(String connectionFactoryName, String initialContextFactoryName, String jndiURL) { + if (connectionFactoryName != null && connectionFactoryName.trim().length() > 0) { + this.connectionFactoryName = connectionFactoryName.trim(); + } + if (initialContextFactoryName != null && initialContextFactoryName.trim().length() > 0) { + this.initialContextFactoryName = initialContextFactoryName.trim(); + } + if (jndiURL != null) { + this.jndiURL = jndiURL.trim(); + } + } + + /* + * This is a simple implementation where a connection is created per binding Ideally the resource factory should be + * able to leverage the host environment to provide connection pooling if it can. E.g. if Tuscany is running inside + * an AppServer Then we could leverage the JMS resources it provides + * + * @see org.apache.tuscany.binding.jms.JMSResourceFactory#getConnection() + */ + public Connection getConnection() throws NamingException, JMSException { + if (connection == null) { + createConnection(); + } + return connection; + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.JMSResourceFactory#createSession() + */ + public Session createSession() throws JMSException, NamingException { + return getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.JMSResourceFactory#startConnection() + */ + public void startConnection() throws JMSException, NamingException { + if (!isConnectionStarted) { + getConnection().start(); + isConnectionStarted = true; + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.binding.jms.JMSResourceFactory#closeConnection() + */ + public void closeConnection() throws JMSException { + if (connection != null) { + try { + connection.close(); + } catch (JMSException e) { + // if using an embedded broker then when shutting down Tuscany the broker may get closed + // before this stop method is called. I can't see how to detect that so for now just + // ignore the exception if the message is that the transport is already disposed + if (!e.getMessage().contains("disposed")) { + throw e; + } + } + } + } + + private void createConnection() throws NamingException, JMSException { + ConnectionFactory connectionFactory = (ConnectionFactory)jndiLookUp(connectionFactoryName); + if (connectionFactory == null) { + throw new JMSBindingException("connection factory not found: " + connectionFactoryName); + } + connection = connectionFactory.createConnection(); + } + + private synchronized Context getInitialContext() throws NamingException { + if (context == null) { + Properties props = new Properties(); + + if (initialContextFactoryName != null) { + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName); + } + if (jndiURL != null) { + props.setProperty(Context.PROVIDER_URL, jndiURL); + } + + initJREEnvironment(props); + + context = new InitialContext(props); + } + return context; + } + + /** + * If using the WAS JMS Client with a non-IBM JRE then an additional + * environment property needs to be set to initialize the ORB correctly. + * See: http://www-1.ibm.com/support/docview.wss?uid=swg24012804 + */ + private void initJREEnvironment(Properties props) { + if ("com.ibm.websphere.naming.WsnInitialContextFactory".equals(props.get(Context.INITIAL_CONTEXT_FACTORY))) { + String vendor = System.getProperty("java.vendor"); + if (vendor == null || !vendor.contains("IBM")) { + props.setProperty("com.ibm.CORBA.ORBInit", "com.ibm.ws.sib.client.ORB"); + } + } + } + + public Destination lookupDestination(String destName) throws NamingException { + if (JMSBindingConstants.DEFAULT_DESTINATION_NAME.equals(destName)) { + return null; + } + + Destination dest = (Destination)jndiLookUp(destName); + if (dest == null) { + dest = lookupPhysical(destName); + } + return dest; + } + + protected Destination lookupPhysical(String jndiName) { + + // TODO: the SCA JMS spec says a destination name may be a non-jndi plain destination name + +// Session session = null; +// try { +// +// Destination dest; +// session = createSession(); +// dest = session.createQueue(jndiName); +// return dest; +// +// } catch (JMSException e) { +// throw new JMSBindingException(e); +// } catch (NamingException e) { +// throw new JMSBindingException(e); +// } finally { +// if (session != null) { +// try { +// session.close(); +// } catch (JMSException e) { +// throw new JMSBindingException(e); +// } +// } +// } + return null; + } + + /** + * You can create a destination in ActiveMQ (and have it appear in JNDI) by putting "dynamicQueues/" in front of the queue name being looked up + */ + public Destination createDestination(String jndiName) throws NamingException { + return lookupDestination("dynamicQueues/" + jndiName); + } + + protected Object jndiLookUp(String name) { + Object o = null; + try { + o = getInitialContext().lookup("java:comp/env/" + name); + } catch (NamingException ex) { + // ignore + } + if (o == null) { + try { + o = getInitialContext().lookup(name); + } catch (NamingException ex) { + // ignore + } + } + return o; + } +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/ObjectMessageProcessor.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/ObjectMessageProcessor.java new file mode 100644 index 0000000000..e9ae3cecaa --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/ObjectMessageProcessor.java @@ -0,0 +1,75 @@ +/* + * 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.binding.jms.provider; + +import java.io.Serializable; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.Session; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; + +/** + * MessageProcessor for sending/receiving Serializable objects with the JMSBinding. + * + * @version $Rev$ $Date$ + */ +public class ObjectMessageProcessor extends AbstractMessageProcessor { + + public ObjectMessageProcessor(JMSBinding jmsBinding) { + super(jmsBinding); + } + + @Override + protected Object[] extractPayload(Message msg) { + try { + + Object o = ((ObjectMessage)msg).getObject(); + if (o != null && o.getClass().isArray()) { + return (Object[])o; + } else { + return new Object[] { o}; + } + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + + @Override + protected Message createJMSMessage(Session session, Object o) { + try { + + if (!(o instanceof Serializable)) { + throw new IllegalStateException("JMS ObjectMessage payload not Serializable: " + o); + } + + ObjectMessage message = session.createObjectMessage(); + message.setObject((Serializable)o); + return message; + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/TextMessageProcessor.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/TextMessageProcessor.java new file mode 100644 index 0000000000..d8490c6223 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/TextMessageProcessor.java @@ -0,0 +1,68 @@ +/* + * 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.binding.jms.provider; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; + +/** + * MessageProcessor for sending/receiving javax.jms.TextMessage with the JMSBinding. + * + * @version $Rev$ $Date$ + */ +public class TextMessageProcessor extends AbstractMessageProcessor { + + public TextMessageProcessor(JMSBinding jmsBinding) { + super(jmsBinding); + } + + @Override + protected Object[] extractPayload(Message msg) { + try { + + if (!(msg instanceof TextMessage)) { + throw new IllegalStateException("expecting JMS TextMessage: " + msg); + } + + return new Object[] {((TextMessage)msg).getText()}; + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + + @Override + protected Message createJMSMessage(Session session, Object o) { + try { + + TextMessage message = session.createTextMessage(); + message.setText(String.valueOf(o)); + return message; + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + +} diff --git a/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/XMLTextMessageProcessor.java b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/XMLTextMessageProcessor.java new file mode 100644 index 0000000000..88dd41cc57 --- /dev/null +++ b/branches/trunk-20080910/modules/binding-jms-runtime/src/main/java/org/apache/tuscany/sca/binding/jms/provider/XMLTextMessageProcessor.java @@ -0,0 +1,90 @@ +/* + * 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.binding.jms.provider; + +import java.io.StringReader; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.impl.builder.StAXOMBuilder; +import org.apache.tuscany.sca.binding.jms.impl.JMSBinding; +import org.apache.tuscany.sca.binding.jms.impl.JMSBindingException; + +/** + * MessageProcessor for sending/receiving XML javax.jms.TextMessage with the JMSBinding. + * + * @version $Rev$ $Date$ + */ +public class XMLTextMessageProcessor extends AbstractMessageProcessor { + + public XMLTextMessageProcessor(JMSBinding jmsBinding) { + super(jmsBinding); + } + + @Override + protected Object[] extractPayload(Message msg) { + try { + + String xml = ((TextMessage)msg).getText(); + Object[] os; + if (xml != null) { + XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(xml)); + StAXOMBuilder builder = new StAXOMBuilder(reader); + os = new Object[] {builder.getDocumentElement()}; + } else { + os = new Object[] {}; + } + return os; + + } catch (XMLStreamException e) { + throw new JMSBindingException(e); + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + + @Override + protected Message createJMSMessage(Session session, Object o) { + try { + + TextMessage message = session.createTextMessage(); + + if (o instanceof OMElement) { + message.setText(o.toString()); + } else if ((o instanceof Object[]) && ((Object[])o)[0] instanceof OMElement) { + message.setText(((Object[])o)[0].toString()); + } else if (o != null) { + throw new IllegalStateException("expecting OMElement payload: " + o); + } + + return message; + + } catch (JMSException e) { + throw new JMSBindingException(e); + } + } + +} -- cgit v1.2.3