001/*
002 * Copyright 2002-2012 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 *      https://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
017package org.springframework.jms.support;
018
019import javax.jms.Connection;
020import javax.jms.JMSException;
021import javax.jms.MessageConsumer;
022import javax.jms.MessageProducer;
023import javax.jms.QueueBrowser;
024import javax.jms.QueueRequestor;
025import javax.jms.Session;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030import org.springframework.jms.InvalidClientIDException;
031import org.springframework.jms.InvalidDestinationException;
032import org.springframework.jms.InvalidSelectorException;
033import org.springframework.jms.JmsException;
034import org.springframework.jms.JmsSecurityException;
035import org.springframework.jms.MessageEOFException;
036import org.springframework.jms.MessageFormatException;
037import org.springframework.jms.MessageNotReadableException;
038import org.springframework.jms.MessageNotWriteableException;
039import org.springframework.jms.ResourceAllocationException;
040import org.springframework.jms.TransactionInProgressException;
041import org.springframework.jms.TransactionRolledBackException;
042import org.springframework.jms.UncategorizedJmsException;
043import org.springframework.lang.Nullable;
044import org.springframework.util.Assert;
045
046/**
047 * Generic utility methods for working with JMS. Mainly for internal use
048 * within the framework, but also useful for custom JMS access code.
049 *
050 * @author Juergen Hoeller
051 * @since 1.1
052 */
053public abstract class JmsUtils {
054
055        private static final Log logger = LogFactory.getLog(JmsUtils.class);
056
057
058        /**
059         * Close the given JMS Connection and ignore any thrown exception.
060         * This is useful for typical {@code finally} blocks in manual JMS code.
061         * @param con the JMS Connection to close (may be {@code null})
062         */
063        public static void closeConnection(@Nullable Connection con) {
064                closeConnection(con, false);
065        }
066
067        /**
068         * Close the given JMS Connection and ignore any thrown exception.
069         * This is useful for typical {@code finally} blocks in manual JMS code.
070         * @param con the JMS Connection to close (may be {@code null})
071         * @param stop whether to call {@code stop()} before closing
072         */
073        public static void closeConnection(@Nullable Connection con, boolean stop) {
074                if (con != null) {
075                        try {
076                                if (stop) {
077                                        try {
078                                                con.stop();
079                                        }
080                                        finally {
081                                                con.close();
082                                        }
083                                }
084                                else {
085                                        con.close();
086                                }
087                        }
088                        catch (javax.jms.IllegalStateException ex) {
089                                logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
090                        }
091                        catch (JMSException ex) {
092                                logger.debug("Could not close JMS Connection", ex);
093                        }
094                        catch (Throwable ex) {
095                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
096                                logger.debug("Unexpected exception on closing JMS Connection", ex);
097                        }
098                }
099        }
100
101        /**
102         * Close the given JMS Session and ignore any thrown exception.
103         * This is useful for typical {@code finally} blocks in manual JMS code.
104         * @param session the JMS Session to close (may be {@code null})
105         */
106        public static void closeSession(@Nullable Session session) {
107                if (session != null) {
108                        try {
109                                session.close();
110                        }
111                        catch (JMSException ex) {
112                                logger.trace("Could not close JMS Session", ex);
113                        }
114                        catch (Throwable ex) {
115                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
116                                logger.trace("Unexpected exception on closing JMS Session", ex);
117                        }
118                }
119        }
120
121        /**
122         * Close the given JMS MessageProducer and ignore any thrown exception.
123         * This is useful for typical {@code finally} blocks in manual JMS code.
124         * @param producer the JMS MessageProducer to close (may be {@code null})
125         */
126        public static void closeMessageProducer(@Nullable MessageProducer producer) {
127                if (producer != null) {
128                        try {
129                                producer.close();
130                        }
131                        catch (JMSException ex) {
132                                logger.trace("Could not close JMS MessageProducer", ex);
133                        }
134                        catch (Throwable ex) {
135                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
136                                logger.trace("Unexpected exception on closing JMS MessageProducer", ex);
137                        }
138                }
139        }
140
141        /**
142         * Close the given JMS MessageConsumer and ignore any thrown exception.
143         * This is useful for typical {@code finally} blocks in manual JMS code.
144         * @param consumer the JMS MessageConsumer to close (may be {@code null})
145         */
146        public static void closeMessageConsumer(@Nullable MessageConsumer consumer) {
147                if (consumer != null) {
148                        // Clear interruptions to ensure that the consumer closes successfully...
149                        // (working around misbehaving JMS providers such as ActiveMQ)
150                        boolean wasInterrupted = Thread.interrupted();
151                        try {
152                                consumer.close();
153                        }
154                        catch (JMSException ex) {
155                                logger.trace("Could not close JMS MessageConsumer", ex);
156                        }
157                        catch (Throwable ex) {
158                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
159                                logger.trace("Unexpected exception on closing JMS MessageConsumer", ex);
160                        }
161                        finally {
162                                if (wasInterrupted) {
163                                        // Reset the interrupted flag as it was before.
164                                        Thread.currentThread().interrupt();
165                                }
166                        }
167                }
168        }
169
170        /**
171         * Close the given JMS QueueBrowser and ignore any thrown exception.
172         * This is useful for typical {@code finally} blocks in manual JMS code.
173         * @param browser the JMS QueueBrowser to close (may be {@code null})
174         */
175        public static void closeQueueBrowser(@Nullable QueueBrowser browser) {
176                if (browser != null) {
177                        try {
178                                browser.close();
179                        }
180                        catch (JMSException ex) {
181                                logger.trace("Could not close JMS QueueBrowser", ex);
182                        }
183                        catch (Throwable ex) {
184                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
185                                logger.trace("Unexpected exception on closing JMS QueueBrowser", ex);
186                        }
187                }
188        }
189
190        /**
191         * Close the given JMS QueueRequestor and ignore any thrown exception.
192         * This is useful for typical {@code finally} blocks in manual JMS code.
193         * @param requestor the JMS QueueRequestor to close (may be {@code null})
194         */
195        public static void closeQueueRequestor(@Nullable QueueRequestor requestor) {
196                if (requestor != null) {
197                        try {
198                                requestor.close();
199                        }
200                        catch (JMSException ex) {
201                                logger.trace("Could not close JMS QueueRequestor", ex);
202                        }
203                        catch (Throwable ex) {
204                                // We don't trust the JMS provider: It might throw RuntimeException or Error.
205                                logger.trace("Unexpected exception on closing JMS QueueRequestor", ex);
206                        }
207                }
208        }
209
210        /**
211         * Commit the Session if not within a JTA transaction.
212         * @param session the JMS Session to commit
213         * @throws JMSException if committing failed
214         */
215        public static void commitIfNecessary(Session session) throws JMSException {
216                Assert.notNull(session, "Session must not be null");
217                try {
218                        session.commit();
219                }
220                catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException ex) {
221                        // Ignore -> can only happen in case of a JTA transaction.
222                }
223        }
224
225        /**
226         * Rollback the Session if not within a JTA transaction.
227         * @param session the JMS Session to rollback
228         * @throws JMSException if committing failed
229         */
230        public static void rollbackIfNecessary(Session session) throws JMSException {
231                Assert.notNull(session, "Session must not be null");
232                try {
233                        session.rollback();
234                }
235                catch (javax.jms.TransactionInProgressException | javax.jms.IllegalStateException ex) {
236                        // Ignore -> can only happen in case of a JTA transaction.
237                }
238        }
239
240        /**
241         * Build a descriptive exception message for the given JMSException,
242         * incorporating a linked exception's message if appropriate.
243         * @param ex the JMSException to build a message for
244         * @return the descriptive message String
245         * @see javax.jms.JMSException#getLinkedException()
246         */
247        public static String buildExceptionMessage(JMSException ex) {
248                String message = ex.getMessage();
249                Exception linkedEx = ex.getLinkedException();
250                if (linkedEx != null) {
251                        if (message == null) {
252                                message = linkedEx.toString();
253                        }
254                        else {
255                                String linkedMessage = linkedEx.getMessage();
256                                if (linkedMessage != null && !message.contains(linkedMessage)) {
257                                        message = message + "; nested exception is " + linkedEx;
258                                }
259                        }
260                }
261                return message;
262        }
263
264        /**
265         * Convert the specified checked {@link javax.jms.JMSException JMSException} to a
266         * Spring runtime {@link org.springframework.jms.JmsException JmsException} equivalent.
267         * @param ex the original checked JMSException to convert
268         * @return the Spring runtime JmsException wrapping the given exception
269         */
270        public static JmsException convertJmsAccessException(JMSException ex) {
271                Assert.notNull(ex, "JMSException must not be null");
272
273                if (ex instanceof javax.jms.IllegalStateException) {
274                        return new org.springframework.jms.IllegalStateException((javax.jms.IllegalStateException) ex);
275                }
276                if (ex instanceof javax.jms.InvalidClientIDException) {
277                        return new InvalidClientIDException((javax.jms.InvalidClientIDException) ex);
278                }
279                if (ex instanceof javax.jms.InvalidDestinationException) {
280                        return new InvalidDestinationException((javax.jms.InvalidDestinationException) ex);
281                }
282                if (ex instanceof javax.jms.InvalidSelectorException) {
283                        return new InvalidSelectorException((javax.jms.InvalidSelectorException) ex);
284                }
285                if (ex instanceof javax.jms.JMSSecurityException) {
286                        return new JmsSecurityException((javax.jms.JMSSecurityException) ex);
287                }
288                if (ex instanceof javax.jms.MessageEOFException) {
289                        return new MessageEOFException((javax.jms.MessageEOFException) ex);
290                }
291                if (ex instanceof javax.jms.MessageFormatException) {
292                        return new MessageFormatException((javax.jms.MessageFormatException) ex);
293                }
294                if (ex instanceof javax.jms.MessageNotReadableException) {
295                        return new MessageNotReadableException((javax.jms.MessageNotReadableException) ex);
296                }
297                if (ex instanceof javax.jms.MessageNotWriteableException) {
298                        return new MessageNotWriteableException((javax.jms.MessageNotWriteableException) ex);
299                }
300                if (ex instanceof javax.jms.ResourceAllocationException) {
301                        return new ResourceAllocationException((javax.jms.ResourceAllocationException) ex);
302                }
303                if (ex instanceof javax.jms.TransactionInProgressException) {
304                        return new TransactionInProgressException((javax.jms.TransactionInProgressException) ex);
305                }
306                if (ex instanceof javax.jms.TransactionRolledBackException) {
307                        return new TransactionRolledBackException((javax.jms.TransactionRolledBackException) ex);
308                }
309
310                // fallback
311                return new UncategorizedJmsException(ex);
312        }
313
314}