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    }