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-2001,2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * Date         Author  Changes
44   * 04/07/2000   jima    Created
45   */
46  package org.exolab.jms.server;
47  
48  import java.sql.Connection;
49  import java.util.Enumeration;
50  import java.util.Vector;
51  
52  import javax.jms.JMSException;
53  import javax.transaction.TransactionManager;
54  
55  import org.apache.commons.logging.Log;
56  import org.apache.commons.logging.LogFactory;
57  
58  import org.exolab.core.service.ServiceManager;
59  import org.exolab.jms.authentication.AuthenticationMgr;
60  import org.exolab.jms.authentication.User;
61  import org.exolab.jms.client.JmsDestination;
62  import org.exolab.jms.client.JmsQueue;
63  import org.exolab.jms.client.JmsTopic;
64  import org.exolab.jms.messagemgr.ConsumerEndpoint;
65  import org.exolab.jms.messagemgr.ConsumerManager;
66  import org.exolab.jms.messagemgr.DestinationCache;
67  import org.exolab.jms.messagemgr.DestinationManager;
68  import org.exolab.jms.persistence.DatabaseService;
69  import org.exolab.jms.persistence.SQLHelper;
70  import org.exolab.jms.util.UUID;
71  
72  
73  /***
74   * A connection is created for every adminclient connecting to the JmsServer.
75   *
76   * @version     $Revision: 1.2.2.1 $ $Date: 2004/05/04 01:26:49 $
77   * @author      <a href="mailto:knut@lerpold.no">Knut Lerpold</a>
78   * @see         org.exolab.jms.server.AdminConnectionManager
79   */
80  public class AdminConnection {
81  
82      /***
83       * This is the client that is responsible for this connection. It should
84       * map back to a client-side entity
85       */
86      private String _clientId = null;
87  
88      /***
89       * The connection id is unique within the context of a JMS server
90       */
91      private String _identifierId = null;
92  
93      /***
94       * The logger
95       */
96      private static final Log _log =
97          LogFactory.getLog(AdminConnection.class);
98  
99      /***
100      * Instantiate an instance of this class for the specified client identity.
101      * Generates a unique identifier for this connection.
102      *
103      * @param       id              client identity
104      */
105     AdminConnection(String id) {
106         _clientId = id;
107         _identifierId = UUID.next();
108     }
109 
110     /***
111      * Returns the client identifier
112      *
113      * @return String  the client identifier
114      */
115     public String getClientId() {
116         return _clientId;
117     }
118 
119     /***
120      * Returns the identifier
121      *
122      * @return String   the identifierId
123      */
124     public String getIdentifierId() {
125         return _identifierId;
126     }
127 
128     /***
129      * Close the admin connection
130      */
131     public void close() {
132     }
133 
134     /***
135      * Determine whether the client is active
136      * Could e.g. disconnect the client if it has been inactive for a
137      * period of time.
138      *
139      * for now always return true
140      *
141      * @return boolean true if active; false otherwise
142      */
143     boolean isClientActive() {
144         boolean active = true;
145 
146         return active;
147     }
148 
149     /***
150      * Return the number of messages for a durable consumer.
151      *
152      * @param       topic               name of the topic
153      * @param       name                consumer name
154      * @return      int                 number of unsent or unacked messages
155      */
156     public int getDurableConsumerMessageCount(String topic, String name) {
157         int count = -1;
158         Connection connection = null;
159 
160         try {
161             // first see if the cache is loaded in memory
162             DestinationManager dmgr = DestinationManager.instance();
163             ConsumerManager cmgr = ConsumerManager.instance();
164             JmsDestination dest = dmgr.destinationFromString(topic);
165             ConsumerEndpoint endpoint = null;
166             if ((dest != null)
167                 && ((name != null)
168                 || (name.length() > 0))) {
169 
170                 endpoint = cmgr.getConsumerEndpoint(name);
171                 if ((endpoint != null)
172                     && (endpoint.getDestination().equals(dest))) {
173                     // retrieve the number of handles for the endpoint, which
174                     // reflects the number of messages
175                     count = endpoint.getMessageCount();
176                 } else {
177                     // there is no cache with this name stored in memory. If
178                     // this is an administered destination then read the count
179                     //  directly from the database.
180                     if (dmgr.isAdministeredDestination(topic)) {
181                         connection = DatabaseService.getConnection();
182                         count = DatabaseService.getAdapter().
183                             getDurableConsumerMessageCount(connection, topic, name);
184 
185                         connection.commit();
186                     }
187                 }
188             }
189         } catch (Exception exception) {
190             _log.error("Failed to get message count for topic=" + topic,
191                 exception);
192             SQLHelper.rollback(connection);
193         } finally {
194             SQLHelper.close(connection);
195         }
196 
197         return count;
198     }
199 
200     /***
201      * First use the destination manager to return the number of persistent and
202      * non-persistent messages in a queue
203      *
204      * @param queue name of the queue
205      * @return int - the number of messages for that destination or -1 if the
206      *               destination is invalid
207      */
208     public int getQueueMessageCount(String queue) {
209         int count = -1;
210         Connection connection = null;
211 
212         try {
213             // first see if the cache is loaded in memory
214             DestinationManager mgr = DestinationManager.instance();
215             JmsDestination dest = mgr.destinationFromString(queue);
216             DestinationCache cache = null;
217             if (dest != null) {
218                 cache = mgr.getDestinationCache(dest);
219 
220                 if (cache != null) {
221                     // retrieve the number of handles for the cache, which
222                     // reflects the number of messages
223                     count = cache.getMessageCount();
224                 } else {
225                     // there is no cache with this name stored in memory. If
226                     // this is an administered destination then read the count
227                     //  directly from the database.
228                     if (mgr.isAdministeredDestination(queue)) {
229                         connection = DatabaseService.getConnection();
230                         count = DatabaseService.getAdapter().
231                             getQueueMessageCount(connection, queue);
232                         connection.commit();
233                     }
234                 }
235             }
236         } catch (Exception exception) {
237             _log.error("Failed to get message count for queue=" + queue,
238                 exception);
239             SQLHelper.rollback(connection);
240         } finally {
241             SQLHelper.close(connection);
242         }
243 
244         return count;
245     }
246 
247     /***
248      * Add the specified durable consumer to the database
249      *
250      * @param       topic               name of the destination
251      * @param       name                name of the consumer
252      * @return      boolean             true if successful
253      */
254     public boolean addDurableConsumer(String topic, String name) {
255         boolean result = false;
256         try {
257             ConsumerManager.instance().createDurableConsumer(
258                 new JmsTopic(topic), name);
259             result = true;
260         } catch (JMSException exception) {
261             _log.error("Failed to add durable consumer=" + name
262                 + " for topic=" + topic, exception);
263         }
264 
265         return result;
266     }
267 
268     /***
269      * Remove the consumer attached to the specified destination and with
270      * the passed in name
271      *
272      * @param       name                name of the consumer
273      * @return      boolean             true if successful
274      */
275     public boolean removeDurableConsumer(String name) {
276         boolean result = false;
277         try {
278             ConsumerManager.instance().removeDurableConsumer(name);
279             result = true;
280         } catch (JMSException exception) {
281             _log.error("Failed to remove durable consumer=" + name, exception);
282         }
283 
284         return result;
285     }
286 
287     /***
288      * Check if the durable consumer exists.
289      *
290      * @param       name                name of the durable conusmer
291      * @return      boolean             true if it exists and false otherwise
292      */
293     public boolean durableConsumerExists(String name) {
294         return ConsumerManager.instance().durableConsumerExists(name);
295     }
296 
297     /***
298      * Return the collection of durable consumer names for a particular
299      * topic destination.
300      *
301      * @param       topic               the topic name
302      * @return      Vector              collection of strings
303      */
304     public Vector getDurableConsumers(String topic) {
305         Enumeration iter = null;
306         Vector result = new Vector();
307         Connection connection = null;
308 
309         try {
310             connection = DatabaseService.getConnection();
311 
312             iter = DatabaseService.getAdapter().getDurableConsumers(
313                 connection, topic);
314             // copy the elements into the vector
315             while (iter.hasMoreElements()) {
316                 result.addElement(iter.nextElement());
317             }
318             connection.commit();
319         } catch (Exception exception) {
320             _log.error("Failed on get durable consumers for topic=" + topic,
321                 exception);
322             SQLHelper.rollback(connection);
323         } finally {
324             SQLHelper.close(connection);
325         }
326 
327         return result;
328     }
329 
330     /***
331      * De-Activate an active persistent consumer.
332      *
333      * @param       name                name of the consumer
334      * @return      boolean             true if successful
335      */
336     public boolean unregisterConsumer(String name) {
337         boolean success = false;
338 
339         try {
340             ConsumerManager.instance().deleteDurableConsumerEndpoint(name);
341             success = true;
342         } catch (JMSException exception) {
343             //
344         }
345 
346         return success;
347     }
348 
349     /***
350      * Check to see if the given consumer is currently connected to the
351      * OpenJMSServer. This is only valid when in online mode.
352      *
353      * @param name The name of the onsumer.
354      * @return boolean True if the consumer is connected.
355      */
356     public boolean isConnected(String name) {
357         return ConsumerManager.instance().isDurableConsumerActive(name);
358     }
359 
360     /***
361      * Return a list of all registered destinations.
362      *
363      * @return      Vector     collection of strings
364      */
365     public Vector getAllDestinations() {
366         Enumeration iter = null;
367         Vector result = new Vector();
368         Connection connection = null;
369         TransactionManager tm = null;
370 
371         try {
372             connection = DatabaseService.getConnection();
373 
374             iter = DatabaseService.getAdapter().getAllDestinations(connection);
375             // copy the elements into the vector
376             while (iter.hasMoreElements()) {
377                 result.addElement(iter.nextElement());
378             }
379             connection.commit();
380         } catch (Exception exception) {
381             _log.error("Failed to get all destinations", exception);
382             SQLHelper.rollback(connection);
383         } finally {
384             SQLHelper.close(connection);
385         }
386 
387         return result;
388     }
389 
390     /***
391      * Add an administered destination with the specified name
392      *
393      * @param       name                destination name
394      * @param       queue               whether it is queue or a topic
395      * @return      boolean             true if successful
396      */
397     public boolean addDestination(String name, Boolean queue) {
398 
399         boolean success = false;
400 
401         // create the appropriate destination object
402         JmsDestination destination = (queue.booleanValue())
403             ? (JmsDestination) new JmsQueue(name)
404             : (JmsDestination) new JmsTopic(name);
405         destination.setPersistent(true);
406 
407         // create the administered destination
408         try {
409             success = DestinationManager.instance().
410                 createAdministeredDestination(destination);
411         } catch (JMSException exception) {
412             _log.error("Failed to add destination=" + name, exception);
413         }
414 
415         return success;
416     }
417 
418     /***
419      * Destroy the specified destination and all associated messsages and
420      * consumers. This is a very dangerous operation to execute while there
421      * are clients online
422      *
423      * @param       name         destination to destroy
424      * @return      boolean             true if successful
425      */
426     public boolean removeDestination(String name) {
427 
428         boolean success = false;
429         JmsDestination dest =
430             DestinationManager.instance().destinationFromString(name);
431 
432         // ensure that the destination actually translates to a valid
433         // object.
434         if (dest != null) {
435             try {
436                 DestinationManager.instance().deleteAdministeredDestination(dest);
437                 success = true;
438             } catch (JMSException exception) {
439                 _log.error("Failed to remove destination=" + name, exception);
440             }
441         }
442 
443         return success;
444     }
445 
446     /***
447      * Check whether the specified destination exists
448      *
449      * @param  name - the name of the destination to check
450      * @return boolean - true if it does and false otherwise
451      */
452     public boolean destinationExists(String name) {
453 
454         boolean exists = false;
455         DestinationManager mgr = DestinationManager.instance();
456         JmsDestination dest = mgr.destinationFromString(name);
457 
458         if (dest != null) {
459             exists = mgr.destinationExists(dest);
460         }
461 
462         return exists;
463     }
464 
465     /***
466      * Terminate the JMS Server. If it is running as a standalone application
467      * then exit the application. It is running as an embedded application then
468      * just terminate the thread
469      */
470     public void stopServer() {
471         // todo perform neccesary clean up for a much nicer shutdown.
472         // probably should inform all clients.
473 
474         _log.info("Stopping all services");
475         ServiceManager.instance().removeAll();
476         _log.info("Server Shutdown scheduled for 5 secs");
477         Runnable r = new Runnable() {
478 
479             public void run() {
480                 try {
481                     Thread.sleep(5000);
482                     System.exit(0);
483                 } catch (Exception err) {
484                     //
485                 }
486             }
487         };
488         Thread t = new Thread(r);
489         t.start();
490     }
491 
492     /***
493      * Purge all processed messages from the database
494      *
495      * @return      int         number of messages purged
496      * @deprecated  no replacement
497      */
498     public int purgeMessages() {
499         return 0;
500     }
501 
502     /***
503      * Add a user with the specified name
504      *
505      * @param username    the users name
506      * @param password    the users password
507      * @return <code>true</code> if the user is added
508      * otherwise <code>false</code>
509      */
510     public boolean addUser(String username, String password) {
511         return AuthenticationMgr.instance().addUser(
512             new User(username, password));
513     }
514 
515     /***
516      * Change password for the specified user
517      *
518      * @param username    the users name
519      * @param password    the users password
520      * @return <code>true</code> if the password is changed
521      * otherwise <code>false</code>
522      */
523     public boolean changePassword(String username, String password) {
524         return AuthenticationMgr.instance().updateUser(
525             new User(username, password));
526     }
527 
528     /***
529      * Remove the specified user
530      *
531      * @param username    the users name
532      * @return <code>true</code> if the user is removed
533      * otherwise <code>false</code>
534      */
535     public boolean removeUser(String username) {
536         return AuthenticationMgr.instance().removeUser(
537             new User(username, null));
538     }
539 
540     /***
541      * Return a list of all registered users.
542      *
543      * @return Vector of users
544      */
545     public Vector getAllUsers() {
546         Enumeration iter = null;
547         Vector result = new Vector();
548         Connection connection = null;
549 
550         try {
551             connection = DatabaseService.getConnection();
552 
553             iter = DatabaseService.getAdapter().getAllUsers(
554                 connection);
555             // copy the elements into the vector
556             while (iter.hasMoreElements()) {
557                 result.addElement(iter.nextElement());
558             }
559             connection.commit();
560         } catch (Exception exception) {
561             _log.error("Failed on get all users", exception);
562             SQLHelper.rollback(connection);
563         } finally {
564             SQLHelper.close(connection);
565         }
566 
567         return result;
568     }
569 
570 }