1 /*
2 * Copyright 2005-2012 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.ws.transport.jms;
18
19 import java.io.IOException;
20 import java.net.URI;
21 import javax.jms.BytesMessage;
22 import javax.jms.Connection;
23 import javax.jms.ConnectionFactory;
24 import javax.jms.Destination;
25 import javax.jms.JMSException;
26 import javax.jms.Message;
27 import javax.jms.MessageConsumer;
28 import javax.jms.MessageProducer;
29 import javax.jms.Queue;
30 import javax.jms.Session;
31 import javax.jms.TextMessage;
32 import javax.jms.Topic;
33
34 import org.springframework.jms.connection.ConnectionFactoryUtils;
35 import org.springframework.jms.core.MessagePostProcessor;
36 import org.springframework.jms.support.JmsUtils;
37 import org.springframework.jms.support.destination.JmsDestinationAccessor;
38 import org.springframework.util.StringUtils;
39 import org.springframework.ws.transport.WebServiceConnection;
40 import org.springframework.ws.transport.WebServiceMessageSender;
41 import org.springframework.ws.transport.jms.support.JmsTransportUtils;
42
43 /**
44 * {@link WebServiceMessageSender} implementation that uses JMS {@link Message}s. Requires a JMS {@link
45 * ConnectionFactory} to operate.
46 * <p/>
47 * This message sender supports URI's of the following format: <blockquote> <tt><b>jms:</b></tt><i>destination</i>[<tt><b>?</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>][<tt><b>&</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>]*
48 * </blockquote> where the characters <tt><b>:</b></tt>, <tt><b>?</b></tt>, and <tt><b>&</b></tt> stand for
49 * themselves. The <i>destination</i> represents the name of the {@link Queue} or {@link Topic} that will be resolved by
50 * the {@link #getDestinationResolver() destination resolver}. Valid <i>param-name</i> include:
51 * <p/>
52 * <blockquote>
53 * <table>
54 * <tr><th><i>param-name</i></th><th><i>Description</i></th></tr>
55 * <tr>
56 * <td><tt>deliveryMode</tt></td>
57 * <td>Indicates whether the request message is persistent or not. This may be <tt>PERSISTENT</tt> or
58 * <tt>NON_PERSISTENT</tt>. See {@link MessageProducer#setDeliveryMode(int)}</td>
59 * </tr>
60 * <tr>
61 * <td><tt>messageType</tt></td>
62 * <td>The message type. This may be <tt>BINARY_MESSAGE</tt> (the default) or <tt>TEXT_MESSAGE</tt></td>
63 * </tr>
64 * <tr>
65 * <td><tt>priority</tt></td>
66 * <td>The JMS priority (0-9) associated with the request message. See
67 * {@link MessageProducer#setPriority(int)}</td>
68 * </tr>
69 * <tr>
70 * <td><tt>replyToName</tt></td>
71 * <td>The name of the destination to which the response message must be sent, that will be resolved by
72 * the {@link #getDestinationResolver() destination resolver}.</td>
73 * </tr>
74 * <tr>
75 * <td><tt>timeToLive</tt></td>
76 * <td>The lifetime, in milliseconds, of the request message. See
77 * {@link MessageProducer#setTimeToLive(long)}</td>
78 * </tr>
79 * </table>
80 * </blockquote>
81 * <p/>
82 * If the <tt>replyToName</tt> is not set, a {@link Session#createTemporaryQueue() temporary queue} is used.
83 * <p/>
84 * This class uses {@link BytesMessage} messages by default, but can be configured to send {@link TextMessage} messages
85 * instead. <b>Note</b> that <code>BytesMessages</code> are preferred, since <code>TextMessages</code> do not support
86 * attachments and character encodings reliably.
87 * <p/>
88 * Some examples of JMS URIs are:
89 * <p/>
90 * <blockquote> <tt>jms:SomeQueue</tt><br> <tt>jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT</tt><br>
91 * <tt>jms:RequestQueue?replyToName=ResponseQueueName</tt><br> <tt>jms:Queue?messageType=TEXT_MESSAGE</blockquote>
92 *
93 * @author Arjen Poutsma
94 * @see <a href="http://tools.ietf.org/id/draft-merrick-jms-iri-00.txt">IRI Scheme for Java(tm) Message Service 1.0</a>
95 * @since 1.5.0
96 */
97 public class JmsMessageSender extends JmsDestinationAccessor implements WebServiceMessageSender {
98
99 /** Default timeout for receive operations: -1 indicates a blocking receive without timeout. */
100 public static final long DEFAULT_RECEIVE_TIMEOUT = -1;
101
102 /** Default encoding used to read fromn and write to {@link TextMessage} messages. */
103 public static final String DEFAULT_TEXT_MESSAGE_ENCODING = "UTF-8";
104
105 private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
106
107 private String textMessageEncoding = DEFAULT_TEXT_MESSAGE_ENCODING;
108
109 private MessagePostProcessor postProcessor;
110
111 /**
112 * Create a new <code>JmsMessageSender</code>
113 * <p/>
114 * <b>Note</b>: The ConnectionFactory has to be set before using the instance. This constructor can be used to
115 * prepare a JmsTemplate via a BeanFactory, typically setting the ConnectionFactory via {@link
116 * #setConnectionFactory(ConnectionFactory)}.
117 *
118 * @see #setConnectionFactory(ConnectionFactory)
119 */
120 public JmsMessageSender() {
121 }
122
123 /**
124 * Create a new <code>JmsMessageSender</code>, given a ConnectionFactory.
125 *
126 * @param connectionFactory the ConnectionFactory to obtain Connections from
127 */
128 public JmsMessageSender(ConnectionFactory connectionFactory) {
129 setConnectionFactory(connectionFactory);
130 }
131
132 /**
133 * Set the timeout to use for receive calls. The default is -1, which means no timeout.
134 *
135 * @see MessageConsumer#receive(long)
136 */
137 public void setReceiveTimeout(long receiveTimeout) {
138 this.receiveTimeout = receiveTimeout;
139 }
140
141 /** Sets the encoding used to read from {@link TextMessage} messages. Defaults to <code>UTF-8</code>. */
142 public void setTextMessageEncoding(String textMessageEncoding) {
143 this.textMessageEncoding = textMessageEncoding;
144 }
145
146 /**
147 * Sets the optional {@link MessagePostProcessor} to further modify outgoing messages after the XML contents has
148 * been set.
149 */
150 public void setPostProcessor(MessagePostProcessor postProcessor) {
151 this.postProcessor = postProcessor;
152 }
153
154 public WebServiceConnection createConnection(URI uri) throws IOException {
155 Connection jmsConnection = null;
156 Session jmsSession = null;
157 try {
158 jmsConnection = createConnection();
159 jmsSession = createSession(jmsConnection);
160 Destination requestDestination = resolveRequestDestination(jmsSession, uri);
161 Message requestMessage = createRequestMessage(jmsSession, uri);
162 JmsSenderConnection wsConnection =
163 new JmsSenderConnection(getConnectionFactory(), jmsConnection, jmsSession, requestDestination,
164 requestMessage);
165 wsConnection.setDeliveryMode(JmsTransportUtils.getDeliveryMode(uri));
166 wsConnection.setPriority(JmsTransportUtils.getPriority(uri));
167 wsConnection.setReceiveTimeout(receiveTimeout);
168 wsConnection.setResponseDestination(resolveResponseDestination(jmsSession, uri));
169 wsConnection.setTimeToLive(JmsTransportUtils.getTimeToLive(uri));
170 wsConnection.setTextMessageEncoding(textMessageEncoding);
171 wsConnection.setSessionTransacted(isSessionTransacted());
172 wsConnection.setPostProcessor(postProcessor);
173 return wsConnection;
174 }
175 catch (JMSException ex) {
176 JmsUtils.closeSession(jmsSession);
177 ConnectionFactoryUtils.releaseConnection(jmsConnection, getConnectionFactory(), true);
178 throw new JmsTransportException(ex);
179 }
180 }
181
182 public boolean supports(URI uri) {
183 return uri.getScheme().equals(JmsTransportConstants.JMS_URI_SCHEME);
184 }
185
186 private Destination resolveRequestDestination(Session session, URI uri) throws JMSException {
187 return resolveDestinationName(session, JmsTransportUtils.getDestinationName(uri));
188 }
189
190 private Destination resolveResponseDestination(Session session, URI uri) throws JMSException {
191 String destinationName = JmsTransportUtils.getReplyToName(uri);
192 return StringUtils.hasLength(destinationName) ? resolveDestinationName(session, destinationName) : null;
193 }
194
195 private Message createRequestMessage(Session session, URI uri) throws JMSException {
196 int messageType = JmsTransportUtils.getMessageType(uri);
197 if (messageType == JmsTransportConstants.BYTES_MESSAGE_TYPE) {
198 return session.createBytesMessage();
199 }
200 else if (messageType == JmsTransportConstants.TEXT_MESSAGE_TYPE) {
201 return session.createTextMessage();
202 }
203 else {
204 throw new IllegalArgumentException("Invalid message type [" + messageType + "].");
205 }
206
207 }
208
209
210 }