001 /*
002 * Copyright (C) The Apache Software Foundation. All rights reserved.
003 *
004 * This software is published under the terms of the Apache Software License
005 * version 1.1, a copy of which has been included with this distribution in
006 * the LICENSE file.
007 *
008 * $Id: SessionFactory.java 155459 2005-02-26 13:24:44Z dirkv $
009 */
010 package org.apache.commons.messenger;
011
012 import java.util.Properties;
013
014 import javax.jms.Connection;
015 import javax.jms.ConnectionFactory;
016 import javax.jms.JMSException;
017 import javax.jms.MessageListener;
018 import javax.jms.QueueConnection;
019 import javax.jms.QueueConnectionFactory;
020 import javax.jms.ServerSessionPool;
021 import javax.jms.Session;
022 import javax.jms.TopicConnection;
023 import javax.jms.TopicConnectionFactory;
024
025 import org.apache.commons.logging.Log;
026 import org.apache.commons.logging.LogFactory;
027
028 /** <p><code>SessionFactory</code> is a Factory of JMS Session objects.
029 * It can be configured with a JMS Connection object to use or can use
030 * a JMS ConnectionFactory instance to create the JMS Connection lazily</p>
031 *
032 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
033 * @version $Revision: 155459 $
034 */
035 public class SessionFactory {
036
037 /** Logger */
038 private static final Log log = LogFactory.getLog(SessionFactory.class);
039
040 /** The JMS connection used to create JMS sessions */
041 private Connection connection;
042
043 /** The JMS ConnectionFactory used to create JMS Connection instances */
044 private ConnectionFactory connectionFactory;
045
046 /** JMS acknowlege mode used on each session */
047 private int acknowlegeMode = Session.AUTO_ACKNOWLEDGE;
048
049 /** whether JMS sessions should be transacted */
050 private boolean transacted;
051
052 /** the optional username used when creating a new JMS connection via a JMS ConnectionFactory */
053 private String username;
054
055 /** the optional password used when creating a new JMS connection via a JMS ConnectionFactory */
056 private String password;
057
058 /** the properties used to create the connection */
059 protected Properties properties;
060
061 /** Whether to use a Topic or Queue connection/session */
062 private boolean topic = true;
063
064 /** The client ID for the connection */
065 private String clientID;
066
067 /** Creates a new Session instance */
068 public Session createSession(Connection connection) throws JMSException {
069 if ( log.isDebugEnabled() ) {
070 log.debug(
071 "Creating a JMS session in transacted mode: "
072 + isTransacted() + " with ack mode: " + getAcknowledgeMode()
073 );
074 }
075
076 if (topic) {
077 TopicConnection topicConnection = (TopicConnection) connection;
078 return topicConnection.createTopicSession(isTransacted(), getAcknowledgeMode());
079 }
080 else {
081 QueueConnection queueConnection = (QueueConnection) connection;
082 return queueConnection.createQueueSession(isTransacted(), getAcknowledgeMode());
083 }
084 }
085
086 /** Creates a new Session instance */
087 public Session createSession() throws JMSException {
088 Connection connection = getConnection();
089 if (topic) {
090 TopicConnection topicConnection = (TopicConnection) connection;
091 return topicConnection.createTopicSession(isTransacted(), getAcknowledgeMode());
092 }
093 else {
094 QueueConnection queueConnection = (QueueConnection) connection;
095 return queueConnection.createQueueSession(isTransacted(), getAcknowledgeMode());
096 }
097 }
098
099 public ServerSessionPool createServerSessionPool(
100 MessageListener messageListener,
101 int maxThreads)
102 throws JMSException {
103 return new DefaultServerSessionPool(this, messageListener, maxThreads);
104 }
105
106 /** Factory method used to create a connection */
107 public Connection createConnection() throws JMSException {
108 ConnectionFactory factory = getConnectionFactory();
109 if (factory == null) {
110 throw new JMSException("No ConnectionFactory configured. Cannot create a JMS Session");
111 }
112 if (log.isDebugEnabled()) {
113 log.debug("About to create a connection from: " + factory);
114 }
115 if (topic) {
116 return createTopicConnection((TopicConnectionFactory) factory);
117 }
118 else {
119 return createQueueConnection((QueueConnectionFactory) factory);
120 }
121 }
122
123 /** Closes the JMS Connection that this object is using, if any */
124 public synchronized void close() throws JMSException {
125 if (connection != null) {
126 Connection tmp = connection;
127 connection = null;
128 tmp.close();
129 }
130 }
131
132
133 // Properties
134 //-------------------------------------------------------------------------
135 /** Returns the JMS connection used to create new sessions */
136 public Connection getConnection() throws JMSException {
137 if (connection == null) {
138 setConnection(createConnection());
139 connection.start();
140 }
141 return connection;
142 }
143
144 public void setConnection(Connection connection) throws JMSException {
145 this.connection = connection;
146 // change the topic flag if the wrong topic/queue type
147 if (topic) {
148 if (!(connection instanceof TopicConnection)) {
149 setTopic(false);
150 }
151 }
152 else {
153 if (!(connection instanceof QueueConnection)) {
154 setTopic(true);
155 }
156 }
157
158 // assign a clientID if one is set
159 if (clientID != null) {
160 connection.setClientID(clientID);
161 }
162 }
163
164 /** Returns the JMS ConnectionFactory used to create a new connection */
165 public ConnectionFactory getConnectionFactory() throws JMSException {
166 if (connectionFactory == null) {
167 setConnectionFactory(createConnectionFactory());
168 }
169 return connectionFactory;
170 }
171
172 public void setConnectionFactory(ConnectionFactory connectionFactory) {
173 this.connectionFactory = connectionFactory;
174 // change the topic flag if the wrong topic/queue type
175 if (topic) {
176 if (!(connectionFactory instanceof TopicConnectionFactory)) {
177 setTopic(false);
178 }
179 }
180 else {
181 if (!(connectionFactory instanceof QueueConnectionFactory)) {
182 setTopic(true);
183 }
184 }
185 }
186
187 /** Returns true if sessions created by this factory should be transacted */
188 public boolean isTransacted() {
189 return transacted;
190 }
191
192 public void setTransacted(boolean transacted) {
193 this.transacted = transacted;
194 }
195
196 /** Returns the JMS acknowledge mode used by the JMS sessions created by this session */
197 public int getAcknowledgeMode() {
198 return acknowlegeMode;
199 }
200
201 public void setAcknowledgeMode(int acknowlegeMode) {
202 this.acknowlegeMode = acknowlegeMode;
203 }
204
205 /**
206 * A String based setter method to allow this property to be defined
207 * easily from within the digester XML file.
208 *
209 * @param value is either "auto", "client" or "dups_ok"
210 * @throws IllegalArgumentException if the value is not one of the correct values
211 */
212 public void setAcknowledge(String value) {
213 if (value != null) {
214 if (value.equalsIgnoreCase("auto")) {
215 setAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
216 }
217 else if (value.equalsIgnoreCase("client")) {
218 setAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
219 }
220 else if (value.equalsIgnoreCase("dups_ok")) {
221 setAcknowledgeMode(Session.DUPS_OK_ACKNOWLEDGE);
222 }
223 else {
224 throw new IllegalArgumentException(
225 "Value: " + value
226 + " is invalid. Must be 'auto', 'client' or 'dups_ok'"
227 );
228 }
229 }
230 }
231
232 /** Returns the optional username used when creating a new JMS connection via a JMS ConnectionFactory */
233 public String getUsername() {
234 return username;
235 }
236
237 public void setUsername(String username) {
238 this.username = username;
239 }
240
241 /** Returns the optional password used when creating a new JMS connection via a JMS ConnectionFactory */
242 public String getPassword() {
243 return password;
244 }
245
246 public void setPassword(String password) {
247 this.password = password;
248 }
249
250 /** Returns the Properties that can be used to configure the connection creation */
251 public Properties getProperties() {
252 if (properties == null) {
253 properties = createProperties();
254 }
255 return properties;
256 }
257
258 public void setProperties(Properties properties) {
259 this.properties = properties;
260 }
261
262 public void addProperty(String name, String value) {
263 getProperties().setProperty(name, value);
264 }
265
266 /** @return whether to use a Topic or Queue connection/session */
267 public boolean isTopic() {
268 return topic;
269 }
270
271 /** Sets whether to use a Topic or Queue connection/session */
272 public void setTopic(boolean topic) {
273 this.topic = topic;
274 }
275
276 /**
277 * Returns the clientID used on the current connection.
278 * @return String
279 */
280 public String getClientID() {
281 return clientID;
282 }
283
284 /**
285 * Sets the clientID used on the current connection.
286 * @param clientID The clientID to set
287 */
288 public void setClientID(String clientID) {
289 this.clientID = clientID;
290 }
291
292
293 // Implementation methods
294 //-------------------------------------------------------------------------
295 protected QueueConnection createQueueConnection(QueueConnectionFactory queueConnectionFactory)
296 throws JMSException {
297 if (username != null || password != null) {
298 return queueConnectionFactory.createQueueConnection(username, password);
299 }
300 else {
301 return queueConnectionFactory.createQueueConnection();
302 }
303 }
304
305 protected TopicConnection createTopicConnection(TopicConnectionFactory topicConnectionFactory)
306 throws JMSException {
307 if (username != null || password != null) {
308 return topicConnectionFactory.createTopicConnection(username, password);
309 }
310 else {
311 return topicConnectionFactory.createTopicConnection();
312 }
313 }
314
315 /** Factory method used to create a connection factory.
316 * Derived classes may wish to use JNDI to load the ConnectionFactory
317 */
318 protected ConnectionFactory createConnectionFactory() throws JMSException {
319 return null;
320 }
321
322 /** Factory method used to create the initial JNDI context properties.
323 * Derived classes may wish to overload this method to provide different properties
324 */
325 protected Properties createProperties() {
326 try {
327 return new Properties(System.getProperties());
328 }
329 catch (Throwable e) {
330 // security exceptoin
331 return new Properties();
332 }
333 }
334 }