KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jms > remoting > JmsInvokerClientInterceptor


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.jms.remoting;
18
19 import javax.jms.JMSException JavaDoc;
20 import javax.jms.Message JavaDoc;
21 import javax.jms.MessageFormatException JavaDoc;
22 import javax.jms.ObjectMessage JavaDoc;
23 import javax.jms.Queue JavaDoc;
24 import javax.jms.QueueConnection JavaDoc;
25 import javax.jms.QueueConnectionFactory JavaDoc;
26 import javax.jms.QueueRequestor JavaDoc;
27 import javax.jms.QueueSession JavaDoc;
28 import javax.jms.Session JavaDoc;
29
30 import org.aopalliance.intercept.MethodInterceptor;
31 import org.aopalliance.intercept.MethodInvocation;
32
33 import org.springframework.aop.support.AopUtils;
34 import org.springframework.beans.factory.InitializingBean;
35 import org.springframework.jms.connection.ConnectionFactoryUtils;
36 import org.springframework.jms.support.JmsUtils;
37 import org.springframework.jms.support.destination.DestinationResolver;
38 import org.springframework.jms.support.destination.DynamicDestinationResolver;
39 import org.springframework.remoting.RemoteAccessException;
40 import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
41 import org.springframework.remoting.support.RemoteInvocation;
42 import org.springframework.remoting.support.RemoteInvocationFactory;
43 import org.springframework.remoting.support.RemoteInvocationResult;
44 import org.springframework.util.Assert;
45
46 /**
47  * Interceptor for accessing a JMS-based remote service.
48  *
49  * <p>To be configured with a QueueConnectionFactory and a target queue
50  * (either as Queue reference or as queue name).
51  *
52  * @author Juergen Hoeller
53  * @author James Strachan
54  * @since 2.0
55  * @see #setConnectionFactory
56  * @see #setQueue
57  * @see #setQueueName
58  * @see org.springframework.jms.remoting.JmsInvokerServiceExporter
59  * @see org.springframework.jms.remoting.JmsInvokerProxyFactoryBean
60  */

61 public class JmsInvokerClientInterceptor implements MethodInterceptor, InitializingBean {
62
63     private QueueConnectionFactory JavaDoc connectionFactory;
64
65     private Object JavaDoc queue;
66
67     private DestinationResolver destinationResolver = new DynamicDestinationResolver();
68
69     private RemoteInvocationFactory remoteInvocationFactory = new DefaultRemoteInvocationFactory();
70
71
72     /**
73      * Set the QueueConnectionFactory to use for obtaining JMS QueueConnections.
74      */

75     public void setConnectionFactory(QueueConnectionFactory JavaDoc connectionFactory) {
76         this.connectionFactory = connectionFactory;
77     }
78
79     /**
80      * Return the QueueConnectionFactory to use for obtaining JMS QueueConnections.
81      */

82     protected QueueConnectionFactory JavaDoc getConnectionFactory() {
83         return this.connectionFactory;
84     }
85
86     /**
87      * Set the target Queue to send invoker requests to.
88      */

89     public void setQueue(Object JavaDoc queue) {
90         this.queue = queue;
91     }
92
93     /**
94      * Set the name of target queue to send invoker requests to.
95      */

96     public void setQueueName(String JavaDoc queueName) {
97         this.queue = queueName;
98     }
99
100     /**
101      * Set the DestinationResolver that is to be used to resolve Queue
102      * references for this accessor.
103      * <p>The default resolver is a DynamicDestinationResolver. Specify a
104      * JndiDestinationResolver for resolving destination names as JNDI locations.
105      * @param destinationResolver the DestinationResolver that is to be used
106      * @see org.springframework.jms.support.destination.DynamicDestinationResolver
107      * @see org.springframework.jms.support.destination.JndiDestinationResolver
108      */

109     public void setDestinationResolver(DestinationResolver destinationResolver) {
110         Assert.notNull(destinationResolver, "DestinationResolver must not be null");
111         this.destinationResolver = destinationResolver;
112     }
113
114     /**
115      * Set the RemoteInvocationFactory to use for this accessor.
116      * Default is a DefaultRemoteInvocationFactory.
117      * <p>A custom invocation factory can add further context information
118      * to the invocation, for example user credentials.
119      */

120     public void setRemoteInvocationFactory(RemoteInvocationFactory remoteInvocationFactory) {
121         this.remoteInvocationFactory = remoteInvocationFactory;
122     }
123
124
125     public void afterPropertiesSet() {
126         if (getConnectionFactory() == null) {
127             throw new IllegalArgumentException JavaDoc("connectionFactory is required");
128         }
129         if (this.queue == null) {
130             throw new IllegalArgumentException JavaDoc("'queue' or 'queueName' is required");
131         }
132     }
133
134
135     public Object JavaDoc invoke(MethodInvocation methodInvocation) throws Throwable JavaDoc {
136         if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
137             return "JMS invoker proxy for queue [" + this.queue + "]";
138         }
139
140         RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
141         RemoteInvocationResult result = null;
142         try {
143             result = executeRequest(invocation);
144         }
145         catch (JMSException JavaDoc ex) {
146             throw new RemoteAccessException("Cannot access JMS invoker queue [" + this.queue + "]", ex);
147         }
148         return recreateRemoteInvocationResult(result);
149     }
150
151     /**
152      * Create a new RemoteInvocation object for the given AOP method invocation.
153      * The default implementation delegates to the RemoteInvocationFactory.
154      * <p>Can be overridden in subclasses to provide custom RemoteInvocation
155      * subclasses, containing additional invocation parameters like user credentials.
156      * Note that it is preferable to use a custom RemoteInvocationFactory which
157      * is a reusable strategy.
158      * @param methodInvocation the current AOP method invocation
159      * @return the RemoteInvocation object
160      * @see RemoteInvocationFactory#createRemoteInvocation
161      */

162     protected RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
163         return this.remoteInvocationFactory.createRemoteInvocation(methodInvocation);
164     }
165
166     /**
167      * Execute the given remote invocation, sending an invoker request message
168      * to this accessor's target queue and waiting for a corresponding response.
169      * <p>The default implementation is based on a JMS QueueRequestor,
170      * using a freshly obtained JMS Session.
171      * @param invocation the RemoteInvocation to execute
172      * @return the RemoteInvocationResult object
173      * @throws JMSException in case of JMS failure
174      */

175     protected RemoteInvocationResult executeRequest(RemoteInvocation invocation) throws JMSException JavaDoc {
176         QueueConnection JavaDoc con = getConnectionFactory().createQueueConnection();
177         QueueSession JavaDoc session = null;
178         QueueRequestor JavaDoc requestor = null;
179         try {
180             session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
181             Queue JavaDoc queueToUse = resolveQueue(session);
182             Message JavaDoc requestMessage = createRequestMessage(session, invocation);
183             requestor = new QueueRequestor JavaDoc(session, queueToUse);
184             con.start();
185             Message JavaDoc responseMessage = requestor.request(requestMessage);
186             return extractInvocationResult(responseMessage);
187         }
188         finally {
189             JmsUtils.closeQueueRequestor(requestor);
190             JmsUtils.closeSession(session);
191             ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory(), true);
192         }
193     }
194
195     /**
196      * Resolve this accessor's target queue.
197      * @param session the current JMS Session
198      * @return the resolved target Queue
199      * @throws JMSException if resolution failed
200      */

201     protected Queue JavaDoc resolveQueue(Session JavaDoc session) throws JMSException JavaDoc {
202         if (this.queue instanceof Queue JavaDoc) {
203             return (Queue JavaDoc) queue;
204         }
205         else if (this.queue instanceof String JavaDoc) {
206             return resolveQueueName(session, (String JavaDoc) this.queue);
207         }
208         else {
209             throw new javax.jms.IllegalStateException JavaDoc(
210                     "Queue object [" + this.queue + "] is neither a [javax.jms.Queue] nor a queue name String");
211         }
212     }
213
214     /**
215      * Resolve the given queue name into a JMS {@link javax.jms.Queue},
216      * via this accessor's {@link DestinationResolver}.
217      * @param session the current JMS Session
218      * @param queueName the name of the queue
219      * @return the located Queue
220      * @throws JMSException if resolution failed
221      * @see #setDestinationResolver
222      */

223     protected Queue JavaDoc resolveQueueName(Session JavaDoc session, String JavaDoc queueName) throws JMSException JavaDoc {
224         return (Queue JavaDoc) this.destinationResolver.resolveDestinationName(session, queueName, false);
225     }
226
227     /**
228      * Create the invoker request message.
229      * <p>The default implementation creates a JMS ObjectMessage
230      * for the given RemoteInvocation object.
231      * @param session the current JMS Session
232      * @param invocation the remote invocation to send
233      * @throws JMSException if the message could not be created
234      */

235     protected Message JavaDoc createRequestMessage(Session JavaDoc session, RemoteInvocation invocation) throws JMSException JavaDoc {
236         return session.createObjectMessage(invocation);
237     }
238
239     /**
240      * Extract the invocation result from the response message.
241      * <p>The default implementation expects a JMS ObjectMessage carrying
242      * a RemoteInvocationResult object. If an invalid response message is
243      * encountered, the <code>onInvalidResponse</code> callback gets invoked.
244      * @param responseMessage the response message
245      * @return the invocation result
246      * @throws JMSException is thrown if a JMS exception occurs
247      * @see #onInvalidResponse
248      */

249     protected RemoteInvocationResult extractInvocationResult(Message JavaDoc responseMessage) throws JMSException JavaDoc {
250         if (responseMessage instanceof ObjectMessage JavaDoc) {
251             ObjectMessage JavaDoc objectMessage = (ObjectMessage JavaDoc) responseMessage;
252             Object JavaDoc body = objectMessage.getObject();
253             if (body instanceof RemoteInvocationResult) {
254                 return (RemoteInvocationResult) body;
255             }
256         }
257         return onInvalidResponse(responseMessage);
258     }
259
260     /**
261      * Callback that is invoked by <code>extractInvocationResult</code>
262      * when it encounters an invalid response message.
263      * <p>The default implementation throws a MessageFormatException.
264      * @param responseMessage the invalid response message
265      * @return an alternative invocation result that should be
266      * returned to the caller (if desired)
267      * @throws JMSException if the invalid response should lead
268      * to an infrastructure exception propagated to the caller
269      * @see #extractInvocationResult
270      */

271     protected RemoteInvocationResult onInvalidResponse(Message JavaDoc responseMessage) throws JMSException JavaDoc {
272         throw new MessageFormatException JavaDoc("Invalid response message: " + responseMessage);
273     }
274
275     /**
276      * Recreate the invocation result contained in the given RemoteInvocationResult
277      * object. The default implementation calls the default recreate method.
278      * <p>Can be overridden in subclass to provide custom recreation, potentially
279      * processing the returned result object.
280      * @param result the RemoteInvocationResult to recreate
281      * @return a return value if the invocation result is a successful return
282      * @throws Throwable if the invocation result is an exception
283      * @see org.springframework.remoting.support.RemoteInvocationResult#recreate()
284      */

285     protected Object JavaDoc recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable JavaDoc {
286         return result.recreate();
287     }
288
289 }
290
Popular Tags