001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jms.remoting;
018:
019: import javax.jms.JMSException;
020: import javax.jms.Message;
021: import javax.jms.MessageFormatException;
022: import javax.jms.MessageProducer;
023: import javax.jms.Session;
024:
025: import org.springframework.beans.factory.InitializingBean;
026: import org.springframework.jms.listener.SessionAwareMessageListener;
027: import org.springframework.jms.support.JmsUtils;
028: import org.springframework.jms.support.converter.MessageConverter;
029: import org.springframework.jms.support.converter.SimpleMessageConverter;
030: import org.springframework.remoting.support.RemoteInvocation;
031: import org.springframework.remoting.support.RemoteInvocationBasedExporter;
032: import org.springframework.remoting.support.RemoteInvocationResult;
033:
034: /**
035: * JMS message listener that exports the specified service bean as a
036: * JMS service endpoint, accessible via a JMS invoker proxy.
037: *
038: * <p>Note that this class implements Spring's
039: * {@link org.springframework.jms.listener.SessionAwareMessageListener}
040: * interface, since it requires access to the active JMS Session.
041: * Hence, this class can only be used with message listener containers
042: * which support the SessionAwareMessageListener interface (e.g. Spring's
043: * {@link org.springframework.jms.listener.DefaultMessageListenerContainer}).
044: *
045: * <p>Thanks to James Strachan for the original prototype that this
046: * JMS invoker mechanism was inspired by!
047: *
048: * @author Juergen Hoeller
049: * @author James Strachan
050: * @since 2.0
051: * @see JmsInvokerClientInterceptor
052: * @see JmsInvokerProxyFactoryBean
053: */
054: public class JmsInvokerServiceExporter extends
055: RemoteInvocationBasedExporter implements
056: SessionAwareMessageListener, InitializingBean {
057:
058: private MessageConverter messageConverter = new SimpleMessageConverter();
059:
060: private boolean ignoreInvalidRequests = true;
061:
062: private Object proxy;
063:
064: /**
065: * Specify the MessageConverter to use for turning request messages into
066: * {@link org.springframework.remoting.support.RemoteInvocation} objects,
067: * as well as {@link org.springframework.remoting.support.RemoteInvocationResult}
068: * objects into response messages.
069: * <p>Default is a {@link org.springframework.jms.support.converter.SimpleMessageConverter},
070: * using a standard JMS {@link javax.jms.ObjectMessage} for each invocation /
071: * invocation result object.
072: * <p>Custom implementations may generally adapt Serializables into
073: * special kinds of messages, or might be specifically tailored for
074: * translating RemoteInvocation(Result)s into specific kinds of messages.
075: */
076: public void setMessageConverter(MessageConverter messageConverter) {
077: this .messageConverter = (messageConverter != null ? messageConverter
078: : new SimpleMessageConverter());
079: }
080:
081: /**
082: * Set whether invalidly formatted messages should be discarded.
083: * Default is "true".
084: * <p>Switch this flag to "false" to throw an exception back to the
085: * listener container. This will typically lead to redelivery of
086: * the message, which is usually undesirable - since the message
087: * content will be the same (that is, still invalid).
088: */
089: public void setIgnoreInvalidRequests(boolean ignoreInvalidRequests) {
090: this .ignoreInvalidRequests = ignoreInvalidRequests;
091: }
092:
093: public void afterPropertiesSet() {
094: this .proxy = getProxyForService();
095: }
096:
097: public void onMessage(Message requestMessage, Session session)
098: throws JMSException {
099: RemoteInvocation invocation = readRemoteInvocation(requestMessage);
100: if (invocation != null) {
101: RemoteInvocationResult result = invokeAndCreateResult(
102: invocation, this .proxy);
103: writeRemoteInvocationResult(requestMessage, session, result);
104: }
105: }
106:
107: /**
108: * Read a RemoteInvocation from the given JMS message.
109: * @param requestMessage current request message
110: * @return the RemoteInvocation object (or <code>null</code>
111: * in case of an invalid message that will simply be ignored)
112: * @throws javax.jms.JMSException in case of message access failure
113: */
114: protected RemoteInvocation readRemoteInvocation(
115: Message requestMessage) throws JMSException {
116: Object content = this .messageConverter
117: .fromMessage(requestMessage);
118: if (content instanceof RemoteInvocation) {
119: return (RemoteInvocation) content;
120: }
121: return onInvalidRequest(requestMessage);
122: }
123:
124: /**
125: * Send the given RemoteInvocationResult as a JMS message to the originator.
126: * @param requestMessage current request message
127: * @param session the JMS Session to use
128: * @param result the RemoteInvocationResult object
129: * @throws javax.jms.JMSException if thrown by trying to send the message
130: */
131: protected void writeRemoteInvocationResult(Message requestMessage,
132: Session session, RemoteInvocationResult result)
133: throws JMSException {
134:
135: Message response = createResponseMessage(requestMessage,
136: session, result);
137: MessageProducer producer = session
138: .createProducer(requestMessage.getJMSReplyTo());
139: try {
140: producer.send(response);
141: } finally {
142: JmsUtils.closeMessageProducer(producer);
143: }
144: }
145:
146: /**
147: * Create the invocation result response message.
148: * <p>The default implementation creates a JMS ObjectMessage
149: * for the given RemoteInvocationResult object.
150: * @param requestMessage the original request message
151: * @param session the JMS session to use
152: * @param result the invocation result
153: * @return the message response to send
154: * @throws javax.jms.JMSException if creating the messsage failed
155: */
156: protected Message createResponseMessage(Message requestMessage,
157: Session session, RemoteInvocationResult result)
158: throws JMSException {
159:
160: Message response = this .messageConverter.toMessage(result,
161: session);
162: response.setJMSCorrelationID(requestMessage
163: .getJMSCorrelationID());
164: return response;
165: }
166:
167: /**
168: * Callback that is invoked by {@link #readRemoteInvocation}
169: * when it encounters an invalid request message.
170: * <p>The default implementation either discards the invalid message or
171: * throws a MessageFormatException - according to the "ignoreInvalidRequests"
172: * flag, which is set to "true" (that is, discard invalid messages) by default.
173: * @param requestMessage the invalid request message
174: * @return the RemoteInvocation to expose for the invalid request (typically
175: * <code>null</code> in case of an invalid message that will simply be ignored)
176: * @throws javax.jms.JMSException in case of the invalid request supposed
177: * to lead to an exception (instead of ignoring it)
178: * @see #readRemoteInvocation
179: * @see #setIgnoreInvalidRequests
180: */
181: protected RemoteInvocation onInvalidRequest(Message requestMessage)
182: throws JMSException {
183: if (this .ignoreInvalidRequests) {
184: if (logger.isWarnEnabled()) {
185: logger
186: .warn("Invalid request message will be discarded: "
187: + requestMessage);
188: }
189: return null;
190: } else {
191: throw new MessageFormatException(
192: "Invalid request message: " + requestMessage);
193: }
194: }
195:
196: }
|