KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > SessionManager


1 /**
2  * $RCSfile: SessionManager.java,v $
3  * $Revision: 1.64 $
4  * $Date: 2005/07/26 05:11:33 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger;
13
14 import org.jivesoftware.messenger.audit.AuditStreamIDFactory;
15 import org.jivesoftware.messenger.auth.UnauthorizedException;
16 import org.jivesoftware.messenger.container.BasicModule;
17 import org.jivesoftware.messenger.handler.PresenceUpdateHandler;
18 import org.jivesoftware.messenger.server.IncomingServerSession;
19 import org.jivesoftware.messenger.server.OutgoingServerSession;
20 import org.jivesoftware.messenger.spi.BasicStreamIDFactory;
21 import org.jivesoftware.messenger.user.UserManager;
22 import org.jivesoftware.messenger.user.UserNotFoundException;
23 import org.jivesoftware.messenger.component.ComponentSession;
24 import org.jivesoftware.messenger.component.InternalComponentManager;
25 import org.jivesoftware.util.JiveGlobals;
26 import org.jivesoftware.util.LocaleUtils;
27 import org.jivesoftware.util.Log;
28 import org.xmpp.packet.JID;
29 import org.xmpp.packet.Message;
30 import org.xmpp.packet.Packet;
31 import org.xmpp.packet.Presence;
32
33 import java.util.*;
34 import java.util.concurrent.ConcurrentHashMap JavaDoc;
35 import java.util.concurrent.CopyOnWriteArrayList JavaDoc;
36
37 /**
38  * Manages the sessions associated with an account. The information
39  * maintained by the Session manager is entirely transient and does
40  * not need to be preserved between server restarts.
41  *
42  * @author Derek DeMoro
43  */

44 public class SessionManager extends BasicModule {
45
46     private int sessionCount = 0;
47     public static final int NEVER_KICK = -1;
48
49     private PresenceUpdateHandler presenceHandler;
50     private PacketRouter router;
51     private String JavaDoc serverName;
52     private JID serverAddress;
53     private UserManager userManager;
54     private int conflictLimit;
55
56     private ClientSessionListener clientSessionListener = new ClientSessionListener();
57     private ComponentSessionListener componentSessionListener = new ComponentSessionListener();
58     private IncomingServerSessionListener incomingServerListener = new IncomingServerSessionListener();
59     private OutgoingServerSessionListener outgoingServerListener = new OutgoingServerSessionListener();
60
61     /**
62      * Map that holds sessions that has been created but haven't been authenticated yet. The Map
63      * will hold client sessions.
64      */

65     private Map JavaDoc<String JavaDoc, ClientSession> preAuthenticatedSessions = new ConcurrentHashMap JavaDoc<String JavaDoc, ClientSession>();
66
67     /**
68      * Map of priority ordered SessionMap objects with username (toLowerCase) as key. The sessions
69      * contained in this Map are client sessions. For each username a SessionMap is kept which
70      * tracks the session for each user resource.
71      */

72     private Map JavaDoc<String JavaDoc, SessionMap> sessions = new ConcurrentHashMap JavaDoc<String JavaDoc, SessionMap>();
73
74     /**
75      * Map of anonymous server sessions. They need to be treated separately as they
76      * have no associated user, and don't follow the normal routing rules for
77      * priority based fall over. The sessions contained in this Map are client sessions.
78      */

79     private Map JavaDoc<String JavaDoc, ClientSession> anonymousSessions = new ConcurrentHashMap JavaDoc<String JavaDoc, ClientSession>();
80
81     /**
82      * The sessions contained in this List are component sessions. For each connected component
83      * this Map will keep the component's session.
84      */

85     private List JavaDoc<ComponentSession> componentsSessions = new CopyOnWriteArrayList JavaDoc<ComponentSession>();
86
87     /**
88      * The sessions contained in this Map are server sessions originated by a remote server. These
89      * sessions can only receive packets from the remote server but are not capable of sending
90      * packets to the remote server. Sessions will be added to this collecion only after they were
91      * authenticated. The key of the Map is the hostname of the remote server.
92      */

93     private Map JavaDoc<String JavaDoc, IncomingServerSession> incomingServerSessions = new ConcurrentHashMap JavaDoc<String JavaDoc, IncomingServerSession>();
94
95     /**
96      * The sessions contained in this Map are server sessions originated from this server to remote
97      * servers. These sessions can only send packets to the remote server but are not capable of
98      * receiving packets from the remote server. Sessions will be added to this collecion only
99      * after they were authenticated. The key of the Map is the hostname of the remote server.
100      */

101     private Map JavaDoc<String JavaDoc, OutgoingServerSession> outgoingServerSessions = new ConcurrentHashMap JavaDoc<String JavaDoc, OutgoingServerSession>();
102
103     /**
104      * <p>Session manager must maintain the routing table as sessions are added and
105      * removed.</p>
106      */

107     private RoutingTable routingTable;
108
109     private StreamIDFactory streamIDFactory;
110
111     /**
112      * Timer that will clean up dead or inactive sessions. Currently only outgoing server sessions
113      * will be analyzed.
114      */

115     private Timer timer = new Timer("Sessions cleanup");
116
117     /**
118      * Task that closes idle server sessions.
119      */

120     private ServerCleanupTask serverCleanupTask;
121
122     /**
123      * Returns the instance of <CODE>SessionManagerImpl</CODE> being used by the XMPPServer.
124      *
125      * @return the instance of <CODE>SessionManagerImpl</CODE> being used by the XMPPServer.
126      */

127     public static SessionManager getInstance() {
128         return XMPPServer.getInstance().getSessionManager();
129     }
130
131     public SessionManager() {
132         super("Session Manager");
133         if (JiveGlobals.getBooleanProperty("xmpp.audit.active")) {
134             streamIDFactory = new AuditStreamIDFactory();
135         }
136         else {
137             streamIDFactory = new BasicStreamIDFactory();
138         }
139
140         String JavaDoc conflictLimitProp = JiveGlobals.getProperty("xmpp.session.conflict-limit");
141         if (conflictLimitProp == null) {
142             conflictLimit = 0;
143             JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
144         }
145         else {
146             try {
147                 conflictLimit = Integer.parseInt(conflictLimitProp);
148             }
149             catch (NumberFormatException JavaDoc e) {
150                 conflictLimit = 0;
151                 JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
152             }
153         }
154     }
155
156     /**
157      * Simple data structure to track sessions for a single user (tracked by resource
158      * and priority).
159      */

160     private class SessionMap {
161         private Map JavaDoc<String JavaDoc,ClientSession> resources = new ConcurrentHashMap JavaDoc<String JavaDoc,ClientSession>();
162         private LinkedList priorityList = new LinkedList();
163
164         /**
165          * Add a session to the manager.
166          *
167          * @param session
168          */

169         void addSession(ClientSession session) {
170             String JavaDoc resource = session.getAddress().getResource();
171             Presence presence = session.getPresence();
172             int priority = presence == null ? 0 : presence.getPriority();
173             resources.put(resource, session);
174             sortSession(resource, priority);
175         }
176
177         /**
178          * Sorts the session into the list based on priority
179          *
180          * @param resource The resource corresponding to the session to sort
181          * @param priority The priority to use for sorting
182          */

183         private void sortSession(String JavaDoc resource, int priority) {
184             synchronized (priorityList) {
185                 if (priorityList.size() > 0) {
186                     Iterator iter = priorityList.iterator();
187                     for (int i = 0; iter.hasNext(); i++) {
188                         ClientSession sess = resources.get(iter.next());
189                         if (sess != null && sess.getPresence().getPriority() <= priority) {
190                             priorityList.add(i, resource);
191                             break;
192                         }
193                     }
194                 }
195                 if (!priorityList.contains(resource)) {
196                     priorityList.addLast(resource);
197                 }
198             }
199         }
200
201         /**
202          * Change the priority of a session associated with the sender.
203          *
204          * @param sender The sender who's session just changed priority
205          * @param priority The new priority for the session
206          */

207         public void changePriority(JID sender, int priority) {
208             String JavaDoc resource = sender.getResource();
209             if (resources.containsKey(resource)) {
210                 synchronized (priorityList) {
211                     priorityList.remove(resource);
212                     sortSession(resource, priority);
213                 }
214             }
215         }
216
217         /**
218          * Remove a session from the manager.
219          *
220          * @param session The session to remove
221          */

222         void removeSession(Session session) {
223             String JavaDoc resource = session.getAddress().getResource();
224             resources.remove(resource);
225             synchronized (priorityList) {
226                 priorityList.remove(resource);
227             }
228         }
229
230         /**
231          * Gets the session for the given resource.
232          *
233          * @param resource The resource describing the particular session
234          * @return The session for that resource or null if none found (use getDefaultSession() to obtain default)
235          */

236         ClientSession getSession(String JavaDoc resource) {
237             return resources.get(resource);
238         }
239
240         /**
241          * Checks to see if a session for the given resource exists.
242          *
243          * @param resource The resource of the session we're checking
244          * @return True if we have a session corresponding to that resource
245          */

246         boolean hasSession(String JavaDoc resource) {
247             return resources.containsKey(resource);
248         }
249
250         /**
251          * Returns the default session for the user based on presence priority. It's possible to
252          * indicate if only available sessions (i.e. with an available presence) should be
253          * included in the search.
254          *
255          * @param filterAvailable flag that indicates if only available sessions should be
256          * considered.
257          * @return The default session for the user.
258          */

259         ClientSession getDefaultSession(boolean filterAvailable) {
260             if (priorityList.isEmpty()) {
261                 return null;
262             }
263
264             if (!filterAvailable) {
265                 return resources.get(priorityList.getFirst());
266             }
267             else {
268                 synchronized (priorityList) {
269                     for (int i=0; i < priorityList.size(); i++) {
270                         ClientSession s = resources.get(priorityList.get(i));
271                         if (s != null && s.getPresence().isAvailable()) {
272                             return s;
273                         }
274                     }
275                 }
276                 return null;
277             }
278         }
279
280         /**
281          * Determines if this map is empty or not.
282          *
283          * @return True if the map contains no entries
284          */

285         boolean isEmpty() {
286             return resources.isEmpty();
287         }
288
289         /**
290          * Broadcast to all resources for the given user
291          *
292          * @param packet
293          */

294         private void broadcast(Packet packet) throws UnauthorizedException, PacketException {
295             for (Session session : resources.values()) {
296                 packet.setTo(session.getAddress());
297                 session.process(packet);
298             }
299         }
300
301         /**
302          * Create an iterator over all sessions for the user.
303          * We create a new list to generate the iterator so other threads
304          * may safely alter the session map without affecting the iterator.
305          *
306          * @return An iterator of all sessions
307          */

308         public Iterator getSessions() {
309             LinkedList<Session> list = new LinkedList<Session>();
310             for (Session session : resources.values()) {
311                 list.add(session);
312             }
313             return list.iterator();
314         }
315
316         /**
317          * Returns a collection of all the sessions whose presence is available.
318          *
319          * @return a collection of all the sessions whose presence is available.
320          */

321         public Collection<ClientSession> getAvailableSessions() {
322             LinkedList<ClientSession> list = new LinkedList<ClientSession>();
323             for (ClientSession session : resources.values()) {
324                 if (session.getPresence().isAvailable()) {
325                     list.add(session);
326                 }
327             }
328             return list;
329         }
330
331         /**
332          * This specified session has received an available presence so we need to recalculate the
333          * order of the sessions so we can have update the default session.
334          *
335          * @param session the session that received an available presence.
336          */

337         public void sessionAvailable(ClientSession session) {
338             changePriority(session.getAddress(), session.getPresence().getPriority());
339         }
340
341         /**
342          * This specified session has received an unavailable presence so we need to recalculate the
343          * order of the sessions so we can have update the default session.
344          *
345          * @param session the session that received an unavailable presence.
346          */

347         public void sessionUnavailable(ClientSession session) {
348             changePriority(session.getAddress(), 0);
349         }
350     }
351
352     /**
353      * Returns a randomly created ID to be used in a stream element.
354      *
355      * @return a randomly created ID to be used in a stream element.
356      */

357     public StreamID nextStreamID() {
358         return streamIDFactory.createStreamID();
359     }
360
361     /**
362      * Creates a new <tt>ClientSession</tt>.
363      *
364      * @param conn the connection to create the session from.
365      * @return a newly created session.
366      * @throws UnauthorizedException
367      */

368     public Session createClientSession(Connection conn) throws UnauthorizedException {
369         if (serverName == null) {
370             throw new UnauthorizedException("Server not initialized");
371         }
372         StreamID id = nextStreamID();
373         ClientSession session = new ClientSession(serverName, conn, id);
374         conn.init(session);
375         // Register to receive close notification on this session so we can
376
// remove and also send an unavailable presence if it wasn't
377
// sent before
378
conn.registerCloseListener(clientSessionListener, session);
379
380         // Add to pre-authenticated sessions.
381
preAuthenticatedSessions.put(session.getAddress().toString(), session);
382         return session;
383     }
384
385     public Session createComponentSession(Connection conn) throws UnauthorizedException {
386         if (serverName == null) {
387             throw new UnauthorizedException("Server not initialized");
388         }
389         StreamID id = nextStreamID();
390         ComponentSession session = new ComponentSession(serverName, conn, id);
391         conn.init(session);
392         // Register to receive close notification on this session so we can
393
// remove the external component from the list of components
394
conn.registerCloseListener(componentSessionListener, session);
395
396         // Add to component session.
397
componentsSessions.add(session);
398         return session;
399     }
400
401     /**
402      * Creates a session for a remote server. The session should be created only after the
403      * remote server has been authenticated either using "server dialback" or SASL.
404      *
405      * @param conn the connection to the remote server.
406      * @param id the stream ID used in the stream element when authenticating the server.
407      * @return the newly created {@link IncomingServerSession}.
408      * @throws UnauthorizedException if the local server has not been initialized yet.
409      */

410     public IncomingServerSession createIncomingServerSession(Connection conn, StreamID id)
411             throws UnauthorizedException {
412         if (serverName == null) {
413             throw new UnauthorizedException("Server not initialized");
414         }
415         IncomingServerSession session = new IncomingServerSession(serverName, conn, id);
416         conn.init(session);
417         // Register to receive close notification on this session so we can
418
// remove its route from the sessions set
419
conn.registerCloseListener(incomingServerListener, session);
420
421         return session;
422     }
423
424     /**
425      * Notification message that a new OutgoingServerSession has been created. Register a listener
426      * that will react when the connection gets closed.
427      *
428      * @param session the newly created OutgoingServerSession.
429      */

430     public void outgoingServerSessionCreated(OutgoingServerSession session) {
431         // Register to receive close notification on this session so we can
432
// remove its route from the sessions set
433
session.getConnection().registerCloseListener(outgoingServerListener, session);
434     }
435
436     /**
437      * Registers that a server session originated by a remote server is hosting a given hostname.
438      * Notice that the remote server may be hosting several subdomains as well as virtual hosts so
439      * the same IncomingServerSession may be associated with many keys.
440      *
441      * @param hostname the hostname that is being served by the remote server.
442      * @param session the incoming server session to the remote server.
443      */

444     public void registerIncomingServerSession(String JavaDoc hostname, IncomingServerSession session) {
445         incomingServerSessions.put(hostname, session);
446     }
447
448     /**
449      * Unregisters that a server session originated by a remote server is hosting a given hostname.
450      * Notice that the remote server may be hosting several subdomains as well as virtual hosts so
451      * the same IncomingServerSession may be associated with many keys.
452      *
453      * @param hostname the hostname that is being served by the remote server.
454      */

455     public void unregisterIncomingServerSession(String JavaDoc hostname) {
456         incomingServerSessions.remove(hostname);
457     }
458
459     /**
460      * Registers that a server session originated by this server has been established to
461      * a remote server named hostname. This session will only be used for sending packets
462      * to the remote server and cannot receive packets. The {@link OutgoingServerSession}
463      * may have one or more domains, subdomains or virtual hosts authenticated with the
464      * remote server.
465      *
466      * @param hostname the hostname that is being served by the remote server.
467      * @param session the outgoing server session to the remote server.
468      */

469     public void registerOutgoingServerSession(String JavaDoc hostname, OutgoingServerSession session) {
470         outgoingServerSessions.put(hostname, session);
471     }
472
473     /**
474      * Unregisters the server session that was originated by this server to a remote server
475      * named hostname. This session was only being used for sending packets
476      * to the remote server and not for receiving packets. The {@link OutgoingServerSession}
477      * may have one or more domains, subdomains or virtual hosts authenticated with the
478      * remote server.
479      *
480      * @param hostname the hostname that the session was connected with.
481      */

482     public void unregisterOutgoingServerSession(String JavaDoc hostname) {
483         outgoingServerSessions.remove(hostname);
484     }
485
486     /**
487      * Add a new session to be managed.
488      */

489     public boolean addSession(ClientSession session) {
490         boolean success = false;
491         String JavaDoc username = session.getAddress().getNode().toLowerCase();
492         SessionMap resources = null;
493
494         synchronized(username.intern()) {
495             resources = sessions.get(username);
496             if (resources == null) {
497                 resources = new SessionMap();
498                 sessions.put(username, resources);
499             }
500             resources.addSession(session);
501             // Remove the pre-Authenticated session but remember to use the temporary JID as the key
502
preAuthenticatedSessions.remove(new JID(null, session.getAddress().getDomain(),
503                     session.getStreamID().toString()).toString());
504             success = true;
505         }
506         return success;
507     }
508
509     /**
510      * Notification message sent when a client sent an available presence for the session. Making
511      * the session available means that the session is now eligible for receiving messages from
512      * other clients. Sessions whose presence is not available may only receive packets (IQ packets)
513      * from the server. Therefore, an unavailable session remains invisible to other clients.
514      *
515      * @param session the session that receieved an available presence.
516      */

517     public void sessionAvailable(ClientSession session) {
518         if (anonymousSessions.containsValue(session)) {
519             // Anonymous session always have resources so we only need to add one route. That is
520
// the route to the anonymous session
521
routingTable.addRoute(session.getAddress(), session);
522         }
523         else {
524             // A non-anonymous session is now available
525
Session defaultSession = null;
526             try {
527                 SessionMap sessionMap = sessions.get(session.getUsername());
528                 if (sessionMap == null) {
529                     Log.warn("No SessionMap found for session" + "\n" + session);
530                 }
531                 // Update the order of the sessions based on the new presence of this session
532
sessionMap.sessionAvailable(session);
533                 defaultSession = sessionMap.getDefaultSession(true);
534                 JID node = new JID(defaultSession.getAddress().getNode(),
535                         defaultSession.getAddress().getDomain(), null);
536                 // Add route to default session (used when no resource is specified)
537
routingTable.addRoute(node, defaultSession);
538                 // Add route to the new session
539
routingTable.addRoute(session.getAddress(), session);
540                 // Broadcast presence between the user's resources
541
broadcastPresenceToOtherResource(session);
542             }
543             catch (UserNotFoundException e) {
544                 // Do nothing since the session is anonymous (? - shouldn't happen)
545
}
546         }
547     }
548
549     /**
550      * Broadcast initial presence from the user's new available resource to any of the user's
551      * existing available resources (if any).
552      *
553      * @param session the session that received the new presence and therefore will not receive
554      * the notification.
555      */

556     private void broadcastPresenceToOtherResource(ClientSession session)
557             throws UserNotFoundException {
558         Presence presence = null;
559         Collection<ClientSession> availableSession;
560         SessionMap sessionMap = sessions.get(session.getUsername());
561         if (sessionMap != null) {
562             availableSession = new ArrayList JavaDoc<ClientSession>(sessionMap.getAvailableSessions());
563             for (ClientSession userSession : availableSession) {
564                 if (userSession != session) {
565                     // Send the presence of an existing session to the session that has just changed
566
// the presence
567
if (session.getPresence().isAvailable()) {
568                         presence = userSession.getPresence().createCopy();
569                         presence.setTo(session.getAddress());
570                         session.process(presence);
571                     }
572                     // Send the presence of the session whose presence has changed to this other
573
// user's session
574
presence = session.getPresence().createCopy();
575                     presence.setTo(userSession.getAddress());
576                     userSession.process(presence);
577                 }
578             }
579         }
580     }
581
582     /**
583      * Notification message sent when a client sent an unavailable presence for the session. Making
584      * the session unavailable means that the session is not eligible for receiving messages from
585      * other clients.
586      *
587      * @param session the session that receieved an unavailable presence.
588      */

589     public void sessionUnavailable(ClientSession session) {
590         if (session.getAddress() != null && routingTable != null &&
591                 session.getAddress().toBareJID().trim().length() != 0) {
592             // Remove route to the removed session (anonymous or not)
593
routingTable.removeRoute(session.getAddress());
594             try {
595                 if (session.getUsername() == null) {
596                     // Do nothing since this is an anonymous session
597
return;
598                 }
599                 SessionMap sessionMap = sessions.get(session.getUsername());
600                 // If sessionMap is null, which is an irregular case, try to clean up the routes to
601
// the user from the routing table
602
if (sessionMap == null) {
603                     JID userJID = new JID(session.getUsername(), serverName, "");
604                     try {
605                         routingTable.getRoute(userJID);
606                         // Remove the route for the session's BARE address
607
routingTable.removeRoute(new JID(session.getAddress().getNode(),
608                                 session.getAddress().getDomain(), ""));
609                     }
610                     catch (NoSuchRouteException e) {
611                         // Do nothing since the routingTable does not have routes to this user
612
}
613                 }
614                 // If all the user sessions are gone then remove the route to the default session
615
else if (sessionMap.getAvailableSessions().isEmpty()) {
616                     // Remove the route for the session's BARE address
617
routingTable.removeRoute(new JID(session.getAddress().getNode(),
618                             session.getAddress().getDomain(), ""));
619                     // Broadcast presence between the user's resources
620
broadcastPresenceToOtherResource(session);
621                 }
622                 else {
623                     // Update the order of the sessions based on the new presence of this session
624
sessionMap.sessionUnavailable(session);
625                     // Update the route for the session's BARE address
626
Session defaultSession = sessionMap.getDefaultSession(true);
627                     routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
628                             defaultSession.getAddress().getDomain(), ""),
629                             defaultSession);
630                     // Broadcast presence between the user's resources
631
broadcastPresenceToOtherResource(session);
632                 }
633             }
634             catch (UserNotFoundException e) {
635                 // Do nothing since the session is anonymous
636
}
637         }
638     }
639
640     /**
641      * Change the priority of a session, that was already available, associated with the sender.
642      *
643      * @param sender The sender who's session just changed priority
644      * @param priority The new priority for the session
645      */

646     public void changePriority(JID sender, int priority) {
647         if (sender.getNode() == null) {
648                 // Do nothing if the session belongs to an anonymous user
649
return;
650         }
651         String JavaDoc username = sender.getNode().toLowerCase();
652         synchronized (username.intern()) {
653             SessionMap resources = sessions.get(username);
654             if (resources == null) {
655                 return;
656             }
657             resources.changePriority(sender, priority);
658
659             // Get the session with highest priority
660
Session defaultSession = resources.getDefaultSession(true);
661             // Update the route to the bareJID with the session with highest priority
662
routingTable.addRoute(new JID(defaultSession.getAddress().getNode(),
663                     defaultSession.getAddress().getDomain(), ""),
664                     defaultSession);
665         }
666     }
667
668
669     /**
670      * Retrieve the best route to deliver packets to this session given the recipient jid. If the
671      * requested JID does not have a node (i.e. username) then the best route will be looked up
672      * in the anonymous sessions list. Otherwise, try to find a root for the exact JID
673      * (i.e. including the resource) and if none is found then answer the deafult session if any.
674      *
675      * @param recipient The recipient ID to deliver packets to
676      * @return The XMPPAddress best suited to use for delivery to the recipient
677      */

678     public Session getBestRoute(JID recipient) {
679         // Return null if the JID belongs to a foreign server
680
if (serverName == null || !serverName.equals(recipient.getDomain())) {
681              return null;
682         }
683         ClientSession session = null;
684         String JavaDoc resource = recipient.getResource();
685         String JavaDoc username = recipient.getNode();
686         if (username == null || "".equals(username)) {
687             if (resource != null) {
688                 session = anonymousSessions.get(resource);
689                 if (session == null){
690                     session = getSession(recipient);
691                 }
692             }
693         }
694         else {
695             username = username.toLowerCase();
696             synchronized (username.intern()) {
697                 SessionMap sessionMap = sessions.get(username);
698                 if (sessionMap != null) {
699                     if (resource == null) {
700                         session = sessionMap.getDefaultSession(false);
701                     }
702                     else {
703                         session = sessionMap.getSession(resource);
704                         if (session == null) {
705                             session = sessionMap.getDefaultSession(false);
706                         }
707                     }
708                 }
709             }
710         }
711         // Sanity check - check if the underlying session connection is closed. Remove the session
712
// from the list of sessions if the session is closed and proceed to look for another route.
713
if (session != null && session.getConnection().isClosed()) {
714             removeSession(session);
715             return getBestRoute(recipient);
716         }
717         return session;
718     }
719
720     public boolean isActiveRoute(String JavaDoc username, String JavaDoc resource) {
721         boolean hasRoute = false;
722
723         if (username == null || "".equals(username)) {
724             if (resource != null) {
725                 hasRoute = anonymousSessions.containsKey(resource);
726             }
727         }
728         else {
729             username = username.toLowerCase();
730             Session session = null;
731             synchronized (username.intern()) {
732                 SessionMap sessionMap = sessions.get(username);
733                 if (sessionMap != null) {
734                     if (resource == null) {
735                         hasRoute = !sessionMap.isEmpty();
736                     }
737                     else {
738                         if (sessionMap.hasSession(resource)) {
739                             session = sessionMap.getSession(resource);
740                         }
741                     }
742                 }
743             }
744             // Makes sure the session is still active
745
// Must occur outside of the lock since validation can cause
746
// the socket to close - deadlocking on session removal
747
if (session != null && !session.getConnection().isClosed()) {
748                 hasRoute = session.getConnection().validate();
749             }
750
751         }
752         return hasRoute;
753     }
754
755     /**
756      * Returns the session responsible for this JID.
757      *
758      * @param from the sender of the packet.
759      * @return the <code>Session</code> associated with the JID.
760      */

761     public ClientSession getSession(JID from) {
762         // Return null if the JID is null
763
if (from == null) {
764             return null;
765         }
766         return getSession(from.getNode(), from.getDomain(), from.getResource());
767     }
768
769     /**
770      * Returns the session responsible for this JID data. The returned Session may have never sent
771      * an available presence (thus not have a route) or could be a Session that hasn't
772      * authenticated yet (i.e. preAuthenticatedSessions).
773      *
774      * @param username the username of the JID.
775      * @param domain the username of the JID.
776      * @param resource the username of the JID.
777      * @return the <code>Session</code> associated with the JID data.
778      */

779     public ClientSession getSession(String JavaDoc username, String JavaDoc domain, String JavaDoc resource) {
780         // Return null if the JID's data belongs to a foreign server. If the server is
781
// shutting down then serverName will be null so answer null too in this case.
782
if (serverName == null || !serverName.equals(domain)) {
783             return null;
784         }
785
786         ClientSession session = null;
787         // Build a JID represention based on the given JID data
788
StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
789         if (username != null) {
790             buf.append(username).append("@");
791         }
792         buf.append(domain);
793         if (resource != null) {
794             buf.append("/").append(resource);
795         }
796         // Initially Check preAuthenticated Sessions
797
session = preAuthenticatedSessions.get(buf.toString());
798         if(session != null){
799             return session;
800         }
801
802         if (resource == null) {
803             return null;
804         }
805         if (username == null || "".equals(username)) {
806             session = anonymousSessions.get(resource);
807         }
808         else {
809             username = username.toLowerCase();
810             synchronized (username.intern()) {
811                 SessionMap sessionMap = sessions.get(username);
812                 if (sessionMap != null) {
813                     session = sessionMap.getSession(resource);
814                 }
815             }
816         }
817         return session;
818     }
819
820
821     public Collection<ClientSession> getSessions() {
822         List JavaDoc<ClientSession> allSessions = new ArrayList JavaDoc<ClientSession>();
823         copyUserSessions(allSessions);
824         copyAnonSessions(allSessions);
825         return allSessions;
826     }
827
828
829     public Collection<ClientSession> getSessions(SessionResultFilter filter) {
830         List JavaDoc<ClientSession> results = new ArrayList JavaDoc<ClientSession>();
831         if (filter != null) {
832             // Grab all the possible matching sessions by user
833
if (filter.getUsername() == null) {
834                 // No user id filtering
835
copyAnonSessions(results);
836                 copyUserSessions(results);
837             }
838             else {
839                 try {
840                     copyUserSessions(userManager.getUser(filter.getUsername()).getUsername(),
841                             results);
842                 }
843                 catch (UserNotFoundException e) {
844                 }
845             }
846
847             Date createMin = filter.getCreationDateRangeMin();
848             Date createMax = filter.getCreationDateRangeMax();
849             Date activityMin = filter.getLastActivityDateRangeMin();
850             Date activityMax = filter.getLastActivityDateRangeMax();
851
852             // Now we have a copy of the references so we can spend some time
853
// doing the rest of the filtering without locking out session access
854
// so let's iterate and filter each session one by one
855
List JavaDoc<ClientSession> filteredResults = new ArrayList JavaDoc<ClientSession>();
856             for (ClientSession session : results) {
857                 // Now filter on creation date if needed
858
if (createMin != null || createMax != null) {
859                     if (!isBetweenDates(session.getCreationDate(), createMin, createMax)) {
860                         session = null;
861                     }
862                 }
863                 // Now filter on activity date if needed
864
if ((activityMin != null || activityMax != null) && session != null) {
865                     if (!isBetweenDates(session.getLastActiveDate(), activityMin, activityMax)) {
866                         session = null;
867                     }
868                 }
869                 if (session != null) {
870                     if (!isBetweenPacketCount(session.getNumClientPackets(),
871                             filter.getClientPacketRangeMin(),
872                             filter.getClientPacketRangeMax())) {
873                         session = null;
874                     }
875                 }
876                 if (session != null) {
877                     if (!isBetweenPacketCount(session.getNumServerPackets(),
878                             filter.getServerPacketRangeMin(),
879                             filter.getServerPacketRangeMax())) {
880                         session = null;
881                     }
882                 }
883                 if (session != null) {
884                     filteredResults.add(session);
885                 }
886             }
887
888             // Sort list.
889
Collections.sort(filteredResults, filter.getSortComparator());
890
891             int maxResults = filter.getNumResults();
892             if (maxResults == SessionResultFilter.NO_RESULT_LIMIT) {
893                 maxResults = filteredResults.size();
894             }
895
896             // Now generate the final list. I believe it's faster to to build up a new
897
// list than it is to remove items from head and tail of the sorted tree
898
List JavaDoc<ClientSession> finalResults = new ArrayList JavaDoc<ClientSession>();
899             int startIndex = filter.getStartIndex();
900             Iterator<ClientSession> sortedIter = filteredResults.iterator();
901             for (int i = 0; sortedIter.hasNext() && finalResults.size() < maxResults; i++) {
902                 ClientSession result = sortedIter.next();
903                 if (i >= startIndex) {
904                     finalResults.add(result);
905                 }
906             }
907             return finalResults;
908         }
909         return results;
910     }
911
912     /**
913      * Returns a session that was originated by a remote server. IncomingServerSession can only
914      * receive packets from the remote server but are not capable of sending packets to the remote
915      * server.
916      *
917      * @param hostname the name of the remote server.
918      * @return a session that was originated by a remote server.
919      */

920     public IncomingServerSession getIncomingServerSession(String JavaDoc hostname) {
921         return incomingServerSessions.get(hostname);
922     }
923
924     /**
925      * Returns a session that was originated from this server to a remote server.
926      * OutgoingServerSession an only send packets to the remote server but are not capable of
927      * receiving packets from the remote server.
928      *
929      * @param hostname the name of the remote server.
930      * @return a session that was originated from this server to a remote server.
931      */

932     public OutgoingServerSession getOutgoingServerSession(String JavaDoc hostname) {
933         return outgoingServerSessions.get(hostname);
934     }
935
936     /**
937      * <p>Determines if the given date is before the min date, or after the max date.</p>
938      * <p>The check is complicated somewhat by the fact that min can be null indicating
939      * no earlier date, and max can be null indicating no upper limit.</p>
940      *
941      * @param date The date to check
942      * @param min The date must be after min, or any if min is null
943      * @param max The date must be before max, or any if max is null
944      * @return True if the date is between min and max
945      */

946     private boolean isBetweenDates(Date date, Date min, Date max) {
947         boolean between = true;
948         if (min != null) {
949             if (date.before(min)) {
950                 between = false;
951             }
952         }
953         if (max != null && between) {
954             if (date.after(max)) {
955                 between = false;
956             }
957         }
958         return between;
959     }
960
961     /**
962      * <p>Determines if the given count is before the min count, or after the max count.</p>
963      * <p>The check is complicated somewhat by the fact that min or max
964      * can be SessionResultFilter.NO_PACKET_LIMIT indicating no limit.</p>
965      *
966      * @param count The count to check
967      * @param min The count must be over min, or any if min is SessionResultFilter.NO_PACKET_LIMIT
968      * @param max The count must be under max, or any if max is SessionResultFilter.NO_PACKET_LIMIT
969      * @return True if the count is between min and max
970      */

971     private boolean isBetweenPacketCount(long count, long min, long max) {
972         boolean between = true;
973         if (min != SessionResultFilter.NO_PACKET_LIMIT) {
974             if (count < min) {
975                 between = false;
976             }
977         }
978         if (max != SessionResultFilter.NO_PACKET_LIMIT && between) {
979             if (count > max) {
980                 between = false;
981             }
982         }
983         return between;
984     }
985
986     private void copyAnonSessions(List JavaDoc sessions) {
987         // Add anonymous sessions
988
for (Session session : anonymousSessions.values()) {
989             sessions.add(session);
990         }
991     }
992
993     private void copyUserSessions(List JavaDoc sessions) {
994         // Get a copy of the sessions from all users
995
for (String JavaDoc username : getSessionUsers()) {
996             Collection<ClientSession> usrSessions = getSessions(username);
997             for (Session session : usrSessions) {
998                 sessions.add(session);
999             }
1000        }
1001    }
1002
1003    private void copyUserSessions(String JavaDoc username, List JavaDoc sessionList) {
1004        // Get a copy of the sessions from all users
1005
SessionMap sessionMap = sessions.get(username);
1006        if (sessionMap != null) {
1007            Iterator sessionItr = sessionMap.getSessions();
1008            while (sessionItr.hasNext()) {
1009                sessionList.add(sessionItr.next());
1010            }
1011        }
1012    }
1013
1014    public Iterator getAnonymousSessions() {
1015        return Arrays.asList(anonymousSessions.values().toArray()).iterator();
1016    }
1017
1018    public Collection<ClientSession> getSessions(String JavaDoc username) {
1019        List JavaDoc<ClientSession> sessionList = new ArrayList JavaDoc<ClientSession>();
1020        if (username != null) {
1021            copyUserSessions(username, sessionList);
1022        }
1023        return sessionList;
1024    }
1025
1026    public int getTotalSessionCount() {
1027        return sessionCount;
1028    }
1029
1030    public int getSessionCount() {
1031        int sessionCount = 0;
1032        for (String JavaDoc username : getSessionUsers()) {
1033            sessionCount += getSessionCount(username);
1034        }
1035        sessionCount += anonymousSessions.size();
1036        return sessionCount;
1037    }
1038
1039    public int getAnonymousSessionCount() {
1040        return anonymousSessions.size();
1041    }
1042
1043    public int getSessionCount(String JavaDoc username) {
1044        if (username == null) {
1045            return 0;
1046        }
1047        int sessionCount = 0;
1048        SessionMap sessionMap = sessions.get(username);
1049        if (sessionMap != null) {
1050            sessionCount = sessionMap.resources.size();
1051        }
1052        return sessionCount;
1053    }
1054
1055    public Collection<String JavaDoc> getSessionUsers() {
1056        return Collections.unmodifiableCollection(sessions.keySet());
1057    }
1058
1059    /**
1060     * Returns a collection with the established sessions from external components.
1061     *
1062     * @return a collection with the established sessions from external components.
1063     */

1064    public Collection<ComponentSession> getComponentSessions() {
1065        return Collections.unmodifiableCollection(componentsSessions);
1066    }
1067
1068    /**
1069     * Returns the session of the component whose domain matches the specified domain.
1070     *
1071     * @param domain the domain of the component session to look for.
1072     * @return the session of the component whose domain matches the specified domain.
1073     */

1074    public ComponentSession getComponentSession(String JavaDoc domain) {
1075        for (ComponentSession session : componentsSessions) {
1076            if (domain.equals(session.getAddress().getDomain())) {
1077                return session;
1078            }
1079        }
1080        return null;
1081    }
1082
1083    /**
1084     * Returns a collection with the hostnames of the remote servers that currently have an
1085     * incoming server connection to this server.
1086     *
1087     * @return a collection with the hostnames of the remote servers that currently have an
1088     * incoming server connection to this server.
1089     */

1090    public Collection<String JavaDoc> getIncomingServers() {
1091        return Collections.unmodifiableCollection(incomingServerSessions.keySet());
1092    }
1093
1094    /**
1095     * Returns a collection with the hostnames of the remote servers that currently may receive
1096     * packets sent from this server.
1097     *
1098     * @return a collection with the hostnames of the remote servers that currently may receive
1099     * packets sent from this server.
1100     */

1101    public Collection<String JavaDoc> getOutgoingServers() {
1102        return Collections.unmodifiableCollection(outgoingServerSessions.keySet());
1103    }
1104
1105    /**
1106     * Broadcasts the given data to all connected sessions. Excellent
1107     * for server administration messages.
1108     *
1109     * @param packet The packet to be broadcast
1110     */

1111    public void broadcast(Packet packet) throws UnauthorizedException {
1112        Iterator values = sessions.values().iterator();
1113        while (values.hasNext()) {
1114            ((SessionMap)values.next()).broadcast(packet);
1115        }
1116
1117        for (Session session : anonymousSessions.values()) {
1118            session.process(packet);
1119        }
1120    }
1121
1122    /**
1123     * Broadcasts the given data to all connected sessions for a particular
1124     * user. Excellent for updating all connected resources for users such as
1125     * roster pushes.
1126     *
1127     * @param packet The packet to be broadcast
1128     */

1129    public void userBroadcast(String JavaDoc username, Packet packet) throws UnauthorizedException, PacketException {
1130        SessionMap sessionMap = sessions.get(username);
1131        if (sessionMap != null) {
1132            sessionMap.broadcast(packet);
1133        }
1134    }
1135
1136    /**
1137     * Removes a session.
1138     *
1139     * @param session the session.
1140     */

1141    public void removeSession(ClientSession session) {
1142        // TODO: Requires better error checking to ensure the session count is maintained
1143
// TODO: properly (removal actually does remove).
1144
// Do nothing if session is null or if the server is shutting down. Note: When the server
1145
// is shutting down the serverName will be null.
1146
if (session == null || serverName == null) {
1147            return;
1148        }
1149        SessionMap sessionMap = null;
1150        if (anonymousSessions.containsValue(session)) {
1151            anonymousSessions.remove(session.getAddress().getResource());
1152            sessionCount--;
1153        }
1154        else {
1155            // If this is a non-anonymous session then remove the session from the SessionMap
1156
if (session.getAddress() != null && session.getAddress().getNode() != null) {
1157                String JavaDoc username = session.getAddress().getNode().toLowerCase();
1158                synchronized (username.intern()) {
1159                    sessionMap = sessions.get(username);
1160                    if (sessionMap != null) {
1161                        sessionMap.removeSession(session);
1162                        sessionCount--;
1163                        if (sessionMap.isEmpty()) {
1164                            sessions.remove(username);
1165                        }
1166                    }
1167                }
1168            }
1169        }
1170        // If the user is still available then send an unavailable presence
1171
Presence presence = session.getPresence();
1172        if (presence == null || presence.isAvailable()) {
1173            Presence offline = new Presence();
1174            offline.setFrom(session.getAddress());
1175            offline.setTo(new JID(null, serverName, null));
1176            offline.setType(Presence.Type.unavailable);
1177            router.route(offline);
1178        }
1179        else if (preAuthenticatedSessions.containsValue(session)) {
1180            // Remove the session from the pre-Authenticated sessions list
1181
preAuthenticatedSessions.remove(session.getAddress().toString());
1182        }
1183    }
1184
1185    public void addAnonymousSession(ClientSession session) {
1186        anonymousSessions.put(session.getAddress().getResource(), session);
1187        // Remove the session from the pre-Authenticated sessions list
1188
preAuthenticatedSessions.remove(session.getAddress().toString());
1189    }
1190
1191    public int getConflictKickLimit() {
1192        return conflictLimit;
1193    }
1194
1195    /**
1196     * Returns the temporary keys used by the sessions that has not been authenticated yet. This
1197     * is an utility method useful for debugging situations.
1198     *
1199     * @return the temporary keys used by the sessions that has not been authenticated yet.
1200     */

1201    public Collection<String JavaDoc> getPreAuthenticatedKeys() {
1202        return preAuthenticatedSessions.keySet();
1203    }
1204
1205    public void setConflictKickLimit(int limit) {
1206        conflictLimit = limit;
1207        JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
1208    }
1209
1210    private class ClientSessionListener implements ConnectionCloseListener {
1211        /**
1212         * Handle a session that just closed.
1213         *
1214         * @param handback The session that just closed
1215         */

1216        public void onConnectionClose(Object JavaDoc handback) {
1217            try {
1218                ClientSession session = (ClientSession)handback;
1219                try {
1220                    if (session.getPresence().isAvailable()) {
1221                        // Send an unavailable presence to the user's subscribers
1222
// Note: This gives us a chance to send an unavailable presence to the
1223
// entities that the user sent directed presences
1224
Presence presence = new Presence();
1225                        presence.setType(Presence.Type.unavailable);
1226                        presence.setFrom(session.getAddress());
1227                        presenceHandler.process(presence);
1228                    }
1229                }
1230                finally {
1231                    // Remove the session
1232
removeSession(session);
1233                }
1234            }
1235            catch (Exception JavaDoc e) {
1236                // Can't do anything about this problem...
1237
Log.error(LocaleUtils.getLocalizedString("admin.error.close"), e);
1238            }
1239        }
1240    }
1241
1242    private class ComponentSessionListener implements ConnectionCloseListener {
1243        /**
1244         * Handle a session that just closed.
1245         *
1246         * @param handback The session that just closed
1247         */

1248        public void onConnectionClose(Object JavaDoc handback) {
1249            ComponentSession session = (ComponentSession)handback;
1250            try {
1251                // Unbind the domain for this external component
1252
String JavaDoc domain = session.getAddress().getDomain();
1253                String JavaDoc subdomain = domain.substring(0, domain.indexOf(serverName) - 1);
1254                InternalComponentManager.getInstance().removeComponent(subdomain);
1255            }
1256            catch (Exception JavaDoc e) {
1257                // Can't do anything about this problem...
1258
Log.error(LocaleUtils.getLocalizedString("admin.error.close"), e);
1259            }
1260            finally {
1261                // Remove the session
1262
componentsSessions.remove(session);
1263            }
1264        }
1265    }
1266
1267    private class IncomingServerSessionListener implements ConnectionCloseListener {
1268        /**
1269         * Handle a session that just closed.
1270         *
1271         * @param handback The session that just closed
1272         */

1273        public void onConnectionClose(Object JavaDoc handback) {
1274            IncomingServerSession session = (IncomingServerSession)handback;
1275            // Remove all the hostnames that were registered for this server session
1276
for (String JavaDoc hostname : session.getValidatedDomains()) {
1277                unregisterIncomingServerSession(hostname);
1278            }
1279        }
1280    }
1281
1282    private class OutgoingServerSessionListener implements ConnectionCloseListener {
1283        /**
1284         * Handle a session that just closed.
1285         *
1286         * @param handback The session that just closed
1287         */

1288        public void onConnectionClose(Object JavaDoc handback) {
1289            OutgoingServerSession session = (OutgoingServerSession)handback;
1290            // Remove all the hostnames that were registered for this server session
1291
for (String JavaDoc hostname : session.getHostnames()) {
1292                unregisterOutgoingServerSession(hostname);
1293                // Remove the route to the session using the hostname
1294
XMPPServer.getInstance().getRoutingTable().removeRoute(new JID(hostname));
1295            }
1296        }
1297    }
1298
1299    public void initialize(XMPPServer server) {
1300        super.initialize(server);
1301        presenceHandler = server.getPresenceUpdateHandler();
1302        router = server.getPacketRouter();
1303        userManager = server.getUserManager();
1304        routingTable = server.getRoutingTable();
1305        serverName = server.getServerInfo().getName();
1306        serverAddress = new JID(serverName);
1307
1308        if (JiveGlobals.getBooleanProperty("xmpp.audit.active")) {
1309            streamIDFactory = new AuditStreamIDFactory();
1310        }
1311        else {
1312            streamIDFactory = new BasicStreamIDFactory();
1313        }
1314
1315        String JavaDoc conflictLimitProp = JiveGlobals.getProperty("xmpp.session.conflict-limit");
1316        if (conflictLimitProp == null) {
1317            conflictLimit = 0;
1318            JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
1319        }
1320        else {
1321            try {
1322                conflictLimit = Integer.parseInt(conflictLimitProp);
1323            }
1324            catch (NumberFormatException JavaDoc e) {
1325                conflictLimit = 0;
1326                JiveGlobals.setProperty("xmpp.session.conflict-limit", Integer.toString(conflictLimit));
1327            }
1328        }
1329        // Run through the server sessions every 5 minutes after a 5 minutes server
1330
// startup delay (default values)
1331
serverCleanupTask = new ServerCleanupTask();
1332        timer.schedule(serverCleanupTask, getServerSessionTimeout(), getServerSessionTimeout());
1333    }
1334
1335
1336    /**
1337     * Sends a message with a given subject and body to all the active user sessions in the server.
1338     *
1339     * @param subject the subject to broadcast.
1340     * @param body the body to broadcast.
1341     */

1342    public void sendServerMessage(String JavaDoc subject, String JavaDoc body) {
1343        sendServerMessage(null, subject, body);
1344    }
1345
1346    /**
1347     * Sends a message with a given subject and body to one or more user sessions related to the
1348     * specified address. If address is null or the address's node is null then the message will be
1349     * sent to all the user sessions. But if the address includes a node but no resource then
1350     * the message will be sent to all the user sessions of the requeted user (defined by the node).
1351     * Finally, if the address is a full JID then the message will be sent to the session associated
1352     * to the full JID. If no session is found then the message is not sent.
1353     *
1354     * @param address the address that defines the sessions that will receive the message.
1355     * @param subject the subject to broadcast.
1356     * @param body the body to broadcast.
1357     */

1358    public void sendServerMessage(JID address, String JavaDoc subject, String JavaDoc body) {
1359        Message packet = createServerMessage(subject, body);
1360        try {
1361            if (address == null || address.getNode() == null || address.getNode().length() < 1) {
1362                broadcast(packet);
1363            }
1364            else if (address.getResource() == null || address.getResource().length() < 1) {
1365                userBroadcast(address.getNode(), packet);
1366            }
1367            else {
1368                ClientSession session = getSession(address);
1369                if (session != null) {
1370                    session.process(packet);
1371                }
1372            }
1373        }
1374        catch (UnauthorizedException e) {
1375        }
1376    }
1377
1378    private Message createServerMessage(String JavaDoc subject, String JavaDoc body) {
1379        Message message = new Message();
1380        message.setFrom(serverAddress);
1381        if (subject != null) {
1382            message.setSubject(subject);
1383        }
1384        message.setBody(body);
1385        return message;
1386    }
1387
1388    public void stop() {
1389        Log.debug("Stopping server");
1390        timer.cancel();
1391        serverName = null;
1392        if (JiveGlobals.getBooleanProperty("shutdownMessage.enabled")) {
1393            sendServerMessage(null, LocaleUtils.getLocalizedString("admin.shutdown.now"));
1394        }
1395        try {
1396            for (Session session : getSessions()) {
1397                try {
1398                    session.getConnection().close();
1399                }
1400                catch (Throwable JavaDoc t) {
1401                }
1402            }
1403        }
1404        catch (Exception JavaDoc e) {
1405        }
1406    }
1407
1408    /******************************************************
1409     * Clean up code
1410     *****************************************************/

1411    /**
1412     * Sets the number of milliseconds to elapse between clearing of idle server sessions.
1413     *
1414     * @param timeout the number of milliseconds to elapse between clearings.
1415     */

1416    public void setServerSessionTimeout(int timeout) {
1417        if (getServerSessionTimeout() == timeout) {
1418            return;
1419        }
1420        // Cancel the existing task because the timeout has changed
1421
if (serverCleanupTask != null) {
1422            serverCleanupTask.cancel();
1423        }
1424        // Create a new task and schedule it with the new timeout
1425
serverCleanupTask = new ServerCleanupTask();
1426        timer.schedule(serverCleanupTask, getServerSessionTimeout(), getServerSessionTimeout());
1427        // Set the new property value
1428
JiveGlobals.setProperty("xmpp.server.session.timeout", Integer.toString(timeout));
1429    }
1430
1431    /**
1432     * Returns the number of milliseconds to elapse between clearing of idle server sessions.
1433     *
1434     * @return the number of milliseconds to elapse between clearing of idle server sessions.
1435     */

1436    public int getServerSessionTimeout() {
1437        return JiveGlobals.getIntProperty("xmpp.server.session.timeout", 5 * 60 * 1000);
1438    }
1439
1440    public void setServerSessionIdleTime(int idleTime) {
1441        if (getServerSessionIdleTime() == idleTime) {
1442            return;
1443        }
1444        // Set the new property value
1445
JiveGlobals.setProperty("xmpp.server.session.idle", Integer.toString(idleTime));
1446    }
1447
1448    public int getServerSessionIdleTime() {
1449        return JiveGlobals.getIntProperty("xmpp.server.session.idle", 30 * 60 * 1000);
1450    }
1451
1452    /**
1453     * Task that closes the idle server sessions.
1454     */

1455    private class ServerCleanupTask extends TimerTask {
1456        /**
1457         * Close outgoing server sessions that have been idle for a long time.
1458         */

1459        public void run() {
1460            // Do nothing if this feature is disabled
1461
if (getServerSessionIdleTime() == -1) {
1462                return;
1463            }
1464            final long deadline = System.currentTimeMillis() - getServerSessionIdleTime();
1465            // Check outgoing server sessions
1466
for (OutgoingServerSession session : outgoingServerSessions.values()) {
1467                try {
1468                    if (session.getLastActiveDate().getTime() < deadline) {
1469                        session.getConnection().close();
1470                    }
1471                }
1472                catch (Throwable JavaDoc e) {
1473                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
1474                }
1475            }
1476            // Check incoming server sessions
1477
for (IncomingServerSession session : incomingServerSessions.values()) {
1478                try {
1479                    if (session.getLastActiveDate().getTime() < deadline) {
1480                        session.getConnection().close();
1481                    }
1482                }
1483                catch (Throwable JavaDoc e) {
1484                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
1485                }
1486            }
1487        }
1488    }
1489}
1490
Popular Tags