View Javadoc

1   /***
2    * 
3    * Copyright 2005 LogicBlaze, Inc.
4    * 
5    * Licensed under the Apache License, Version 2.0 (the "License"); 
6    * you may not use this file except in compliance with the License. 
7    * You may obtain a copy of the License at 
8    * 
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License. 
16   * 
17   **/
18  package org.logicblaze.lingo.jms;
19  
20  import org.logicblaze.lingo.LingoInvocation;
21  import org.logicblaze.lingo.LingoRemoteInvocationFactory;
22  import org.logicblaze.lingo.MetadataStrategy;
23  import org.logicblaze.lingo.MethodMetadata;
24  import org.logicblaze.lingo.SimpleMetadataStrategy;
25  import org.logicblaze.lingo.jms.marshall.DefaultMarshaller;
26  import org.logicblaze.lingo.jms.marshall.Marshaller;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.remoting.support.RemoteInvocation;
31  import org.springframework.remoting.support.RemoteInvocationBasedExporter;
32  import org.springframework.remoting.support.RemoteInvocationFactory;
33  import org.springframework.remoting.support.RemoteInvocationResult;
34  
35  import javax.jms.JMSException;
36  import javax.jms.Message;
37  import javax.jms.MessageListener;
38  import javax.jms.ObjectMessage;
39  import javax.jms.Session;
40  
41  /***
42   * @version $Revision: 1.2 $
43   */
44  public abstract class JmsServiceExporterSupport extends RemoteInvocationBasedExporter implements MessageListener, InitializingBean {
45      private static final Log log = LogFactory.getLog(JmsServiceExporterSupport.class);
46  
47      protected Object proxy;
48      private boolean ignoreFailures;
49      private Marshaller marshaller;
50      private MetadataStrategy metadataStrategy;
51      private RemoteInvocationFactory invocationFactory;
52      private Requestor responseRequestor;
53  
54      public void afterPropertiesSet() throws Exception {
55          this.proxy = getProxyForService();
56          if (proxy == null) {
57              throw new IllegalArgumentException("proxy is required");
58          }
59          if (responseRequestor == null) {
60              throw new IllegalArgumentException("responseRequestor is required");
61          }
62          if (marshaller == null) {
63              marshaller = new DefaultMarshaller();
64          }
65          if (metadataStrategy == null) {
66              metadataStrategy = new SimpleMetadataStrategy(true);
67          }
68          if (invocationFactory == null) {
69              invocationFactory = new LingoRemoteInvocationFactory(metadataStrategy);
70          }
71      }
72  
73      public void onMessage(Message message) {
74          try {
75              RemoteInvocation invocation = marshaller.readRemoteInvocation(message);
76              if (invocation != null) {
77                  boolean oneway = false;
78                  if (invocation instanceof LingoInvocation) {
79                      LingoInvocation lingoInvocation = (LingoInvocation) invocation;
80                      oneway = lingoInvocation.getMetadata().isOneWay();
81                      introduceRemoteReferences(lingoInvocation, message);
82                  }
83                  RemoteInvocationResult result = invokeAndCreateResult(invocation, this.proxy);
84                  if (!oneway) {
85                      writeRemoteInvocationResult(message, result);
86                  }
87              }
88          }
89          catch (JMSException e) {
90              onException(message, e);
91          }
92      }
93  
94      // Properties
95      //-------------------------------------------------------------------------
96      public Requestor getResponseRequestor() {
97          return responseRequestor;
98      }
99  
100     public void setResponseRequestor(Requestor responseRequestor) {
101         this.responseRequestor = responseRequestor;
102     }
103 
104     public Marshaller getMarshaller() {
105         return marshaller;
106     }
107 
108     public void setMarshaller(Marshaller marshaller) {
109         this.marshaller = marshaller;
110     }
111 
112     public RemoteInvocationFactory getInvocationFactory() {
113         return invocationFactory;
114     }
115 
116     public void setInvocationFactory(RemoteInvocationFactory invocationFactory) {
117         this.invocationFactory = invocationFactory;
118     }
119 
120     public boolean isIgnoreFailures() {
121         return ignoreFailures;
122     }
123 
124     /***
125      * Sets whether or not failures should be ignored (and just logged) or thrown as
126      * runtime exceptions into the JMS provider
127      */
128     public void setIgnoreFailures(boolean ignoreFailures) {
129         this.ignoreFailures = ignoreFailures;
130     }
131 
132 
133     // Implementation methods
134     //-------------------------------------------------------------------------
135 
136     /***
137      * Send the given RemoteInvocationResult as a JMS message to the originator
138      *
139      * @param message current HTTP message
140      * @param result  the RemoteInvocationResult object
141      * @throws javax.jms.JMSException if thrown by trying to send the message
142      */
143     protected abstract void writeRemoteInvocationResult(Message message, RemoteInvocationResult result) throws JMSException;
144 
145     /***
146      * Creates the invocation result response message
147      *
148      * @param session the JMS session to use
149      * @param message the original request message, in case we want to attach any properties etc.
150      * @param result  the invocation result
151      * @return the message response to send
152      * @throws javax.jms.JMSException if creating the messsage failed
153      */
154     protected Message createResponseMessage(Session session, Message message, RemoteInvocationResult result) throws JMSException {
155         // an alternative strategy could be to use XStream and text messages
156         // though some JMS providers, like ActiveMQ, might do this kind of thing for us under the covers
157         if (result == null) {
158             throw new IllegalArgumentException("result cannot be null");
159         }
160         ObjectMessage answer = session.createObjectMessage(result);
161 
162         // lets preserve the correlation ID
163         answer.setJMSCorrelationID(message.getJMSCorrelationID());
164         return answer;
165     }
166 
167     /***
168      * Lets replace any remote object correlation IDs with dynamic proxies
169      *
170      * @param invocation
171      * @param requestMessage
172      */
173     protected void introduceRemoteReferences(LingoInvocation invocation, Message requestMessage) throws JMSException {
174         MethodMetadata metadata = invocation.getMetadata();
175         Object[] arguments = invocation.getArguments();
176         Class[] parameterTypes = invocation.getParameterTypes();
177         for (int i = 0; i < parameterTypes.length; i++) {
178             if (metadata.isRemoteParameter(i)) {
179                 arguments[i] = createRemoteProxy(requestMessage, parameterTypes[i], arguments[i]);
180             }
181         }
182     }
183 
184     protected Object createRemoteProxy(Message message, Class parameterType, Object argument) throws JMSException {
185         JmsProxyFactoryBean factory = new JmsProxyFactoryBean();
186         factory.setDestination(message.getJMSReplyTo());
187         factory.setCorrelationID((String) argument);
188         factory.setRemoteInvocationFactory(invocationFactory);
189         factory.setServiceInterface(parameterType);
190         factory.setRequestor(responseRequestor);
191         factory.afterPropertiesSet();
192         return factory.getObject();
193     }
194 
195 
196     /***
197      * Handle the processing of an exception when processing an inbound messsage
198      */
199     protected void onException(Message message, JMSException e) {
200         String text = "Failed to process inbound message due to: " + e + ". Message will be discarded: " + message;
201         log.info(text, e);
202         if (!ignoreFailures) {
203             throw new RuntimeException(text, e);
204         }
205     }
206 }