Package org.logicblaze.lingo.jms

Source Code of org.logicblaze.lingo.jms.JmsClientInterceptor

/**
*
* Copyright 2005 LogicBlaze, Inc.
*
* Licensed 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.logicblaze.lingo.jms;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.logicblaze.lingo.LingoInvocation;
import org.logicblaze.lingo.LingoRemoteInvocationFactory;
import org.logicblaze.lingo.MetadataStrategy;
import org.logicblaze.lingo.MethodMetadata;
import org.logicblaze.lingo.SimpleMetadataStrategy;
import org.logicblaze.lingo.jms.impl.AsyncReplyHandler;
import org.logicblaze.lingo.jms.impl.MultiplexingRequestor;
import org.logicblaze.lingo.jms.marshall.DefaultMarshaller;
import org.logicblaze.lingo.jms.marshall.Marshaller;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.support.RemoteInvocationBasedAccessor;
import org.springframework.remoting.support.RemoteInvocationFactory;
import org.springframework.remoting.support.RemoteInvocationResult;

import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;

import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;

/**
* Interceptor for accessing a JMS based service which must be configured with a
* {@link org.logicblaze.lingo.LingoRemoteInvocationFactory} instance.
*
* @author James Strachan
* @see #setServiceInterface
* @see #setServiceUrl
* @see JmsServiceExporter
* @see JmsProxyFactoryBean
*/
public class JmsClientInterceptor extends RemoteInvocationBasedAccessor implements MethodInterceptor, InitializingBean, DisposableBean {

    private Map remoteObjects = new WeakHashMap();
    private Requestor requestor;
    private Destination destination;
    private Destination responseDestination;
    private String correlationID;
    private Marshaller marshaller;
    private ConnectionFactory connectionFactory;
    private String jmsType;
    private Map messageProperties;
    private int jmsExpiration = -1;
    private JmsProducerConfig producerConfig = new JmsProducerConfig();

    public JmsClientInterceptor() {
        setRemoteInvocationFactory(createRemoteInvocationFactory());
    }

    public JmsClientInterceptor(Requestor requestor) {
        this.requestor = requestor;
        setRemoteInvocationFactory(createRemoteInvocationFactory());
    }

    public JmsClientInterceptor(Requestor requestor, LingoRemoteInvocationFactory factory) {
        this.requestor = requestor;
        setRemoteInvocationFactory(factory);
    }

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
            return "JMS invoker proxy for service URL [" + getServiceUrl() + "]";
        }
        LingoInvocation invocation = (LingoInvocation) createRemoteInvocation(methodInvocation);
        MethodMetadata metadata = invocation.getMetadata();
        replaceRemoteReferences(invocation, metadata);
        try {
            Message requestMessage = marshaller.createRequestMessage(requestor, invocation);
            populateHeaders(requestMessage);
            if (metadata.isOneWay()) {
                requestor.oneWay(destination, requestMessage);
                return null;
            }
            else {
                Message response = requestor.request(destination, requestMessage);
                RemoteInvocationResult result = marshaller.extractInvocationResult(response);
                return recreateRemoteInvocationResult(result);
            }
        }
        catch (JMSException ex) {
            throw new RemoteAccessException("Cannot access JMS invoker remote service at [" + getServiceUrl() + "]", ex);
        }
    }

    public void afterPropertiesSet() throws JMSException {
        RemoteInvocationFactory factory = getRemoteInvocationFactory();
        if (!(factory instanceof LingoRemoteInvocationFactory)) {
            throw new IllegalArgumentException("remoteInvocationFactory must be an instance of LingoRemoteInvocationFactory but was: " + factory);

        }
        if (requestor == null) {
            if (connectionFactory == null) {
                throw new IllegalArgumentException("requestor or connectionFactory is required");
            }
            else {
                requestor = createRequestor();
            }
        }
        if (marshaller == null) {
            // default to standard JMS marshaling
            marshaller = new DefaultMarshaller();
        }
    }

    public void destroy() throws Exception {
        requestor.close();
    }

    // Properties
    // -------------------------------------------------------------------------
    public Requestor getRequestor() {
        return requestor;
    }

    public void setRequestor(Requestor requestor) {
        this.requestor = requestor;
    }

    public Destination getDestination() {
        return destination;
    }

    /**
     * Sets the destination used to make requests
     *
     * @param destination
     */
    public void setDestination(Destination destination) {
        this.destination = destination;
    }

    public Destination getResponseDestination() {
        return responseDestination;
    }

    /**
     * Sets the destination used to consume responses on - or null and a
     * temporary queue will be created.
     *
     * @param responseDestination
     */
    public void setResponseDestination(Destination responseDestination) {
        this.responseDestination = responseDestination;
    }

    public void setCorrelationID(String correlationID) {
        this.correlationID = correlationID;
    }

    public String getJmsType() {
        return jmsType;
    }

    /**
     * Sets the JMS message type string which is appended to messages if set
     */
    public void setJmsType(String jmsType) {
        this.jmsType = jmsType;
    }

    public Map getMessageProperties() {
        return messageProperties;
    }

    public int getJmsExpiration() {
        return jmsExpiration;
    }

    /**
     * Sets the JMS expiration timeout (in milliseconds) of the request message
     */
    public void setJmsExpiration(int jmsExpiration) {
        this.jmsExpiration = jmsExpiration;
    }

    public int getJmsPriority() {
        return producerConfig.getPriority();
    }

    /**
     * Sets the JMS priority of the request message
     */
    public void setJmsPriority(int jmsPriority) {
        producerConfig.setPriority(jmsPriority);
    }



    public int getTimeToLive() {
        return producerConfig.getTimeToLive();
    }


    /**
     * Sets the time to live on each message request
     */
    public void setTimeToLive(int timeToLive) {
        producerConfig.setTimeToLive(timeToLive);
    }



    /**
     * Sets the message properties to be added to each message. Note that the
     * keys should be Strings and the values should be primitive types.
     */
    public void setMessageProperties(Map messageProperties) {
        this.messageProperties = messageProperties;
    }

    public Marshaller getMarshaller() {
        return marshaller;
    }

    public void setMarshaller(Marshaller marshaller) {
        this.marshaller = marshaller;
    }

    public ConnectionFactory getConnectionFactory() {
        return connectionFactory;
    }

    /**
     * Used to create a default {@link Requestor} if no requestor is explicitly
     * configured.
     */
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public JmsProducerConfig getProducerConfig() {
        return producerConfig;
    }

    /**
     * Sets the configuration of the producer used to send back responses
     */
    public void setProducerConfig(JmsProducerConfig producerConfig) {
        this.producerConfig = producerConfig;
    }

    public boolean isPersistentDelivery() {
        return producerConfig.getDeliveryMode() == DeliveryMode.PERSISTENT;
    }

    /**
     * Sets the delivery mode to be persistent or non-persistent.
     */
    public void setPersistentDelivery(boolean persistent) {
        producerConfig.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
    }

    public String getClientID() {
        return producerConfig.getClientID();
    }

    /**
     * Sets the JMS connections unique clientID. This is optional unless you
     * wish to use durable topic subscriptions. Only one connection can have a
     * given clientID at any time.
     */
    public void setClientID(String clientID) {
        producerConfig.setClientID(clientID);
    }

    // Implementation methods
    // -------------------------------------------------------------------------

    protected void populateHeaders(Message requestMessage) throws JMSException {
        if (correlationID != null) {
            requestMessage.setJMSCorrelationID(correlationID);
        }
        if (jmsType != null) {
            requestMessage.setJMSType(jmsType);
        }
        if (jmsExpiration >= 0) {
            requestMessage.setJMSExpiration(jmsExpiration);
        }
        int jmsPriority = getJmsPriority();
        if (jmsPriority >= 0) {
            requestMessage.setJMSPriority(jmsPriority);
        }
        if (messageProperties != null) {
            for (Iterator iter = messageProperties.entrySet().iterator(); iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                String name = entry.getKey().toString();
                Object value = entry.getValue();
                requestMessage.setObjectProperty(name, value);
            }
        }
    }

    /**
     * Recreate the invocation result contained in the given
     * RemoteInvocationResult object. The default implementation calls the
     * default recreate method.
     * <p>
     * Can be overridden in subclass to provide custom recreation, potentially
     * processing the returned result object.
     *
     * @param result
     *            the RemoteInvocationResult to recreate
     * @return a return value if the invocation result is a successful return
     * @throws Throwable
     *             if the invocation result is an exception
     * @see org.springframework.remoting.support.RemoteInvocationResult#recreate
     */
    protected Object recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable {
        return result.recreate();
    }

    protected void replaceRemoteReferences(LingoInvocation invocation, MethodMetadata metadata) {
        Object[] arguments = invocation.getArguments();
        Class[] parameterTypes = invocation.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            if (metadata.isRemoteParameter(i)) {
                arguments[i] = remoteReference(parameterTypes[i], arguments[i]);
            }
        }
    }

    protected Object remoteReference(Class type, Object value) {
        if (value == null) {
            return null;
        }
        String correlationID = (String) remoteObjects.get(value);
        if (correlationID == null) {
            correlationID = requestor.createCorrelationID();
            remoteObjects.put(value, correlationID);
        }
        if (requestor instanceof MultiplexingRequestor) {
            MultiplexingRequestor multiplexingRequestor = (MultiplexingRequestor) requestor;
            multiplexingRequestor.registerHandler(correlationID, new AsyncReplyHandler(value, marshaller));
        }
        else {
            throw new IllegalArgumentException("You can only pass remote references with a MultiplexingRequestor");
        }
        return correlationID;
    }

    protected Requestor createRequestor() throws JMSException {
        return MultiplexingRequestor.newInstance(connectionFactory, getProducerConfig(), destination, responseDestination);
    }

    /**
     * Factory method to create a default lingo based invocation factory if none
     * is configured
     */
    protected LingoRemoteInvocationFactory createRemoteInvocationFactory() {
        return new LingoRemoteInvocationFactory(createMetadataStrategy());
    }

    /**
     * Factory method to create a default metadata strategy if none is
     * configured
     *
     * @return
     */
    protected MetadataStrategy createMetadataStrategy() {
        return new SimpleMetadataStrategy();
    }

}
TOP

Related Classes of org.logicblaze.lingo.jms.JmsClientInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.