001/*
002 * Copyright 2002-2018 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.ConnectionFactory;
021import javax.jms.JMSException;
022import javax.jms.Session;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027import org.springframework.beans.factory.InitializingBean;
028import org.springframework.core.Constants;
029import org.springframework.jms.JmsException;
030import org.springframework.lang.Nullable;
031import org.springframework.util.Assert;
032
033/**
034 * Base class for {@link org.springframework.jms.core.JmsTemplate} and other
035 * JMS-accessing gateway helpers, defining common properties such as the
036 * JMS {@link ConnectionFactory} to operate on. The subclass
037 * {@link org.springframework.jms.support.destination.JmsDestinationAccessor}
038 * adds further, destination-related properties.
039 *
040 * <p>Not intended to be used directly.
041 * See {@link org.springframework.jms.core.JmsTemplate}.
042 *
043 * @author Juergen Hoeller
044 * @since 1.2
045 * @see org.springframework.jms.support.destination.JmsDestinationAccessor
046 * @see org.springframework.jms.core.JmsTemplate
047 */
048public abstract class JmsAccessor implements InitializingBean {
049
050        /** Constants instance for {@code javax.jms.Session}. */
051        private static final Constants sessionConstants = new Constants(Session.class);
052
053
054        /** Logger available to subclasses. */
055        protected final Log logger = LogFactory.getLog(getClass());
056
057        @Nullable
058        private ConnectionFactory connectionFactory;
059
060        private boolean sessionTransacted = false;
061
062        private int sessionAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
063
064
065        /**
066         * Set the ConnectionFactory to use for obtaining JMS {@link Connection Connections}.
067         */
068        public void setConnectionFactory(@Nullable ConnectionFactory connectionFactory) {
069                this.connectionFactory = connectionFactory;
070        }
071
072        /**
073         * Return the ConnectionFactory that this accessor uses for obtaining
074         * JMS {@link Connection Connections}.
075         */
076        @Nullable
077        public ConnectionFactory getConnectionFactory() {
078                return this.connectionFactory;
079        }
080
081        /**
082         * Obtain the ConnectionFactory for actual use.
083         * @return the ConnectionFactory (never {@code null})
084         * @throws IllegalStateException in case of no ConnectionFactory set
085         * @since 5.0
086         */
087        protected final ConnectionFactory obtainConnectionFactory() {
088                ConnectionFactory connectionFactory = getConnectionFactory();
089                Assert.state(connectionFactory != null, "No ConnectionFactory set");
090                return connectionFactory;
091        }
092
093        /**
094         * Set the transaction mode that is used when creating a JMS {@link Session}.
095         * Default is "false".
096         * <p>Note that within a JTA transaction, the parameters passed to
097         * {@code create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)}
098         * method are not taken into account. Depending on the Java EE transaction context,
099         * the container makes its own decisions on these values. Analogously, these
100         * parameters are not taken into account within a locally managed transaction
101         * either, since the accessor operates on an existing JMS Session in this case.
102         * <p>Setting this flag to "true" will use a short local JMS transaction
103         * when running outside of a managed transaction, and a synchronized local
104         * JMS transaction in case of a managed transaction (other than an XA
105         * transaction) being present. This has the effect of a local JMS
106         * transaction being managed alongside the main transaction (which might
107         * be a native JDBC transaction), with the JMS transaction committing
108         * right after the main transaction.
109         * @see javax.jms.Connection#createSession(boolean, int)
110         */
111        public void setSessionTransacted(boolean sessionTransacted) {
112                this.sessionTransacted = sessionTransacted;
113        }
114
115        /**
116         * Return whether the JMS {@link Session sessions} used by this
117         * accessor are supposed to be transacted.
118         * @see #setSessionTransacted(boolean)
119         */
120        public boolean isSessionTransacted() {
121                return this.sessionTransacted;
122        }
123
124        /**
125         * Set the JMS acknowledgement mode by the name of the corresponding constant
126         * in the JMS {@link Session} interface, e.g. "CLIENT_ACKNOWLEDGE".
127         * <p>If you want to use vendor-specific extensions to the acknowledgment mode,
128         * use {@link #setSessionAcknowledgeMode(int)} instead.
129         * @param constantName the name of the {@link Session} acknowledge mode constant
130         * @see javax.jms.Session#AUTO_ACKNOWLEDGE
131         * @see javax.jms.Session#CLIENT_ACKNOWLEDGE
132         * @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
133         * @see javax.jms.Connection#createSession(boolean, int)
134         */
135        public void setSessionAcknowledgeModeName(String constantName) {
136                setSessionAcknowledgeMode(sessionConstants.asNumber(constantName).intValue());
137        }
138
139        /**
140         * Set the JMS acknowledgement mode that is used when creating a JMS
141         * {@link Session} to send a message.
142         * <p>Default is {@link Session#AUTO_ACKNOWLEDGE}.
143         * <p>Vendor-specific extensions to the acknowledgment mode can be set here as well.
144         * <p>Note that inside an EJB, the parameters to the
145         * {@code create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)} method
146         * are not taken into account. Depending on the transaction context in the EJB,
147         * the container makes its own decisions on these values. See section 17.3.5
148         * of the EJB spec.
149         * @param sessionAcknowledgeMode the acknowledgement mode constant
150         * @see javax.jms.Session#AUTO_ACKNOWLEDGE
151         * @see javax.jms.Session#CLIENT_ACKNOWLEDGE
152         * @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
153         * @see javax.jms.Connection#createSession(boolean, int)
154         */
155        public void setSessionAcknowledgeMode(int sessionAcknowledgeMode) {
156                this.sessionAcknowledgeMode = sessionAcknowledgeMode;
157        }
158
159        /**
160         * Return the acknowledgement mode for JMS {@link Session sessions}.
161         */
162        public int getSessionAcknowledgeMode() {
163                return this.sessionAcknowledgeMode;
164        }
165
166        @Override
167        public void afterPropertiesSet() {
168                if (getConnectionFactory() == null) {
169                        throw new IllegalArgumentException("Property 'connectionFactory' is required");
170                }
171        }
172
173
174        /**
175         * Convert the specified checked {@link javax.jms.JMSException JMSException} to
176         * a Spring runtime {@link org.springframework.jms.JmsException JmsException}
177         * equivalent.
178         * <p>The default implementation delegates to the
179         * {@link JmsUtils#convertJmsAccessException} method.
180         * @param ex the original checked {@link JMSException} to convert
181         * @return the Spring runtime {@link JmsException} wrapping {@code ex}
182         * @see JmsUtils#convertJmsAccessException
183         */
184        protected JmsException convertJmsAccessException(JMSException ex) {
185                return JmsUtils.convertJmsAccessException(ex);
186        }
187
188        /**
189         * Create a JMS Connection via this template's ConnectionFactory.
190         * <p>This implementation uses JMS 1.1 API.
191         * @return the new JMS Connection
192         * @throws JMSException if thrown by JMS API methods
193         * @see javax.jms.ConnectionFactory#createConnection()
194         */
195        protected Connection createConnection() throws JMSException {
196                return obtainConnectionFactory().createConnection();
197        }
198
199        /**
200         * Create a JMS Session for the given Connection.
201         * <p>This implementation uses JMS 1.1 API.
202         * @param con the JMS Connection to create a Session for
203         * @return the new JMS Session
204         * @throws JMSException if thrown by JMS API methods
205         * @see javax.jms.Connection#createSession(boolean, int)
206         */
207        protected Session createSession(Connection con) throws JMSException {
208                return con.createSession(isSessionTransacted(), getSessionAcknowledgeMode());
209        }
210
211        /**
212         * Determine whether the given Session is in client acknowledge mode.
213         * <p>This implementation uses JMS 1.1 API.
214         * @param session the JMS Session to check
215         * @return whether the given Session is in client acknowledge mode
216         * @throws javax.jms.JMSException if thrown by JMS API methods
217         * @see javax.jms.Session#getAcknowledgeMode()
218         * @see javax.jms.Session#CLIENT_ACKNOWLEDGE
219         */
220        protected boolean isClientAcknowledge(Session session) throws JMSException {
221                return (session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
222        }
223
224}