View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: JmsServerConnectionManager.java,v 1.10 2003/08/17 01:32:26 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 04/07/2000   jima    Created
47   */
48  package org.exolab.jms.server;
49  
50  
51  import java.util.Collections;
52  import java.util.HashMap;
53  import java.util.Iterator;
54  import java.util.Map;
55  
56  import javax.jms.JMSException;
57  import javax.jms.JMSSecurityException;
58  
59  import org.apache.commons.logging.Log;
60  import org.apache.commons.logging.LogFactory;
61  
62  import org.exolab.core.foundation.HandleIfc;
63  import org.exolab.jms.authentication.AuthenticationMgr;
64  import org.exolab.jms.events.BasicEventManager;
65  import org.exolab.jms.events.Event;
66  import org.exolab.jms.events.EventHandler;
67  import org.exolab.jms.events.IllegalEventDefinedException;
68  
69  
70  /***
71   * The connection manager is responsible for managing all connections to the
72   * JmsServer. The connection manager is a singleton (at this point anyway)
73   * that is accessible through the instance class method. It is also responsible
74   * for holding a list of connections.
75   * <p>
76   * A client uses a client identifier to create a connection and a connection
77   * type to identify the connection type- queue or topic.
78   * <p>
79   * TODO:
80   * Look at leasing connections and timing them out.
81   *
82   * @version     $Revision: 1.10 $ $Date: 2003/08/17 01:32:26 $
83   * @author      <a href="mailto:jima@exoffice.com">Jim Alateras</a>
84   * @see         JmsServerConnection
85   */
86  public class JmsServerConnectionManager
87      implements EventHandler {
88  
89      /***
90       * The interval that garbage collection on the connection manager is
91       * executed to clean up abnormally terminated connections. This defaults
92       * to every minute.
93       */
94      private long _gcInterval = 60 * 1000;
95  
96      /***
97       * holds a list of active connections
98       */
99      private Map _connections = null;
100 
101     /***
102      * Holds the singleton instance of the class
103      */
104     private static JmsServerConnectionManager _instance = null;
105 
106     /***
107      * Used to stop multiple threads trying to init the singleton
108      */
109     private static final Object _initializer = new Object();
110 
111     /***
112      * This is the event that is fired to initiate garbage collection
113      * in the database
114      */
115     private static final int CONNECTION_GC_EVENT = 1;
116 
117     /***
118      * The logger
119      */
120     private static final Log _log =
121         LogFactory.getLog(JmsServerConnectionManager.class);
122 
123     /***
124      * The private constructor initialises the manager.
125      */
126     private JmsServerConnectionManager() {
127         _connections = Collections.synchronizedMap(new HashMap());
128 
129         // check to see whether the gcInterval has been overriden
130         if (System.getProperty("org.exolab.jms.connection.gcInterval") != null) {
131             try {
132                 _gcInterval = Long.parseLong(System.getProperty(
133                     "org.exolab.jms.connection.gcInterval"));
134             } catch (Exception ignore) {
135                 // if the gcinterval is incorrectly specified then use
136                 // the default
137             }
138         }
139 
140         if (_gcInterval > 0) {
141             registerEvent();
142         }
143     }
144 
145     /***
146      * The static method returns the singleton instance of the
147      * JmsConnectionManager. If one does not exist it is created prior to
148      * returning.
149      *
150      * @return      JmsConnectionManager
151      */
152     public static JmsServerConnectionManager instance() {
153 
154         if (_instance == null) {
155             synchronized (_initializer) {
156                 if (_instance == null) {
157                     _instance = new JmsServerConnectionManager();
158                 }
159             }
160         }
161 
162         return _instance;
163     }
164 
165     /***
166      * Create a connection with the specified client id. If the client already
167      * has an open connection then simply return a reference to this connection
168      * If a connection already exists for the specified client identity then
169      * simply return the exisiting connection
170      *
171      * @param id the client identity
172      * @param username the client's username
173      * @param password the client's password
174      * @return a new connection
175      * @throws JMSSecurityException if the client cannot be authenticated
176      * @throws JMSException if the connection cannot be created
177      */
178     public JmsServerConnection createConnection(String id, String username,
179                                                 String password)
180         throws JMSSecurityException, JMSException {
181 
182         if (!AuthenticationMgr.instance().validateUser(username, password)) {
183             throw new JMSSecurityException("Failed to authenticate user " +
184                 username);
185         }
186 
187         JmsServerConnection result = (JmsServerConnection) _connections.get(id);
188         if (result == null) {
189             result = new JmsServerConnection(id);
190 
191             // the connection is created in the stopped mode and is the
192             // added the list of connections
193             _connections.put(id, result);
194         }
195 
196         return result;
197     }
198 
199     /***
200      * Return an Enumeration of all the connections currently registered with
201      * the connection manager
202      *
203      * @return Iterator
204      */
205     public Iterator getConnections() {
206         return _connections.values().iterator();
207     }
208 
209     /***
210      * Return the connection associated with a particular client identity. If
211      * such a connection does not exist then return null
212      *
213      * @param       id              identity of the client
214      * @return      JmsConnection   associated connection or null
215      */
216     public JmsServerConnection getConnection(String id) {
217         return (JmsServerConnection) _connections.get(id);
218     }
219 
220     /***
221      * Close the connection associated with a particular client identity. If
222      * a connection for the client does not exist then do nothing.
223      *
224      * @param       id              identity of the client
225      */
226     public void closeConnection(String id) {
227         JmsServerConnection connection =
228             (JmsServerConnection) _connections.remove(id);
229 
230         if (connection != null) {
231             connection.close();
232         }
233     }
234 
235     // implementation of EventHandler.handleEvent
236     public void handleEvent(int event, Object callback, long time) {
237         try {
238             Object[] ids = _connections.keySet().toArray();
239             for (int index = 0; index < ids.length; index++) {
240                 JmsServerConnection connection =
241                     (JmsServerConnection) _connections.get(ids[index]);
242                 if ((connection != null) &&
243                     (!connection.isClientEndpointActive())) {
244                     _log.info("Cleaning up connection " + ids[index]);
245                     closeConnection((String) ids[index]);
246                 }
247             }
248         } finally {
249             registerEvent();
250         }
251     }
252 
253     // implementation of EventHandler.getHandle
254     public HandleIfc getHandle() {
255         // not used
256         return null;
257     }
258 
259     /***
260      * Register an event with the event service. The event will tell the
261      * connection manager to initiate garbage collection
262      */
263     private void registerEvent() {
264         try {
265             BasicEventManager.instance().registerEventRelative(
266                 new Event(CONNECTION_GC_EVENT, this, null),
267                 _gcInterval);
268         } catch (IllegalEventDefinedException exception) {
269             _log.error("JmsServerConnectionManager.registerEvent failed",
270                 exception);
271         }
272     }
273 
274 } //-- JmsServerConnectionManager
275