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
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
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
156
157 if (result == null) {
158 throw new IllegalArgumentException("result cannot be null");
159 }
160 ObjectMessage answer = session.createObjectMessage(result);
161
162
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 }