KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > tomcat > tc5 > session > JBossManagerCMP


1 /*
2  * JBoss, the OpenSource WebOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.web.tomcat.tc5.session;
8
9 import org.apache.catalina.LifecycleException;
10 import org.apache.catalina.Session;
11 import org.apache.catalina.session.StandardManager;
12 import org.apache.catalina.Context;
13 import org.apache.catalina.Globals;
14 import org.jboss.mx.util.MBeanProxyExt;
15 import org.jboss.mx.util.MBeanServerLocator;
16 import org.jboss.ha.httpsession.interfaces.SerializableHttpSession;
17 import org.jboss.ha.httpsession.server.ClusteredHTTPSessionServiceMBean;
18
19 import java.io.IOException JavaDoc;
20 import javax.ejb.EJBException JavaDoc;
21 import javax.management.MBeanServer JavaDoc;
22 import javax.management.ObjectName JavaDoc;
23 import javax.servlet.http.HttpServletResponse JavaDoc;
24 import javax.servlet.http.Cookie JavaDoc;
25
26 import org.jboss.logging.Logger;
27 import org.jboss.metadata.WebMetaData;
28
29 /**
30  * Implementation of a clustered session manager for
31  * catalina.
32  *
33  * @author Thomas Peuss <jboss@peuss.de>
34  * @version $Revision: 1.1.2.2 $
35  * @see org.jboss.ha.httpsession.server.ClusteredHTTPSessionService
36  */

37 public class JBossManagerCMP
38    extends StandardManager
39    implements AbstractJBossManager, JBossManagerCMPMBean
40 {
41    // -- Constants ----------------------------------------
42

43    /**
44     * Informational name for this Catalina component
45     */

46    private static final String JavaDoc info = "JBossManagerCMP/1.0";
47
48    // -- Class attributes ---------------------------------
49
/**
50     * The Log-object for this class
51     */

52    private static Logger log = Logger.getLogger(JBossManagerCMP.class);
53
54    /**
55     * Proxy-object for the ClusteredHTTPSessionService
56     */

57    private ClusteredHTTPSessionServiceMBean proxy;
58
59    /**
60     * The ObjectName for the ClusteredHttpSessionService
61     */

62    private ObjectName JavaDoc clusteredHttpServiceName;
63
64    /**
65     * The objectname this Manager is associated with
66     */

67    protected ObjectName JavaDoc objectName;
68
69    /**
70     * Is the reaper-thread started?
71     */

72    protected boolean started = false;
73
74    /**
75     * Should we use the local cache?
76     */

77    private boolean useLocalCache = true;
78
79    protected int invalidateSessionPolicy = WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET;
80    protected int replicationType = WebMetaData.REPLICATION_TYPE_SYNC;
81
82    public JBossManagerCMP()
83    {
84    }
85
86    public void init(String JavaDoc name, WebMetaData webMetaData, boolean useJK, boolean useLocalCache)
87       throws ClusteringNotSupportedException
88    {
89       this.useLocalCache = useLocalCache;
90
91       // We only allow serializable session attributes
92
setDistributable(true);
93       this.invalidateSessionPolicy = webMetaData.getInvalidateSessionPolicy();
94       this.replicationType = webMetaData.getReplicationType();
95
96       // Find ClusteredHttpSessionService
97
try
98       {
99          clusteredHttpServiceName = new ObjectName JavaDoc("jboss", "service", "ClusteredHttpSession");
100          // Create Proxy-Object for this service
101
proxy = (ClusteredHTTPSessionServiceMBean) MBeanProxyExt.create(ClusteredHTTPSessionServiceMBean.class, clusteredHttpServiceName);
102       }
103       catch (Throwable JavaDoc e)
104       {
105          log.info("ClusteredHTTPSessionService not found");
106          throw new ClusteringNotSupportedException("ClusteredHTTPSessionService not found");
107       }
108
109       try
110       {
111          // set the JBoss' ClusteredHttpSession service timeout to 4 hours because we have our own expiry mechanism
112
proxy.setSessionTimeout(14400000);
113
114          // Give this manager a name
115
objectName = new ObjectName JavaDoc("jboss.web:service=ClusterManager,WebModule=" + name);
116
117          log.info("ClusteredHTTPSessionService found");
118       }
119       catch (Throwable JavaDoc e)
120       {
121          log.error("Could not create ObjectName", e);
122          throw new ClusteringNotSupportedException(e.toString());
123       }
124    }
125
126    public boolean isUseLocalCache()
127    {
128       return useLocalCache;
129    }
130
131    // MBean-methods ---------------------------------------
132
public Integer JavaDoc getLocalActiveSessionCount()
133    {
134       return new Integer JavaDoc(sessions.size());
135    }
136
137    public ClusteredSession[] getSessions()
138    {
139       ClusteredSession[] sess = new ClusteredSession[0];
140
141       synchronized (sessions)
142       {
143          sess = (ClusteredSession[]) sessions.values().toArray(sess);
144       }
145       return sess;
146    }
147
148    public int getInvalidateSessionPolicy()
149    {
150       return this.invalidateSessionPolicy;
151    }
152
153    public int getReplicationType()
154    {
155       return replicationType;
156    }
157
158
159    // Manager-methods -------------------------------------
160

161    /**
162     * Create a new session
163     */

164    public Session createSession()
165    {
166       ClusteredSessionCMP session = new ClusteredSessionCMP(this);
167
168       session.setNew(true);
169       session.setCreationTime(System.currentTimeMillis());
170       session.setMaxInactiveInterval(this.maxInactiveInterval);
171
172       String JavaDoc sessionId = this.getNextId();
173       String JavaDoc jvmRoute = this.getJvmRoute();
174       if (jvmRoute != null)
175       {
176          sessionId += '.' + jvmRoute;
177       }
178
179       session.setValid(true);
180
181       session.setId(sessionId);
182
183       return session;
184    }
185
186    /**
187     * Generate new sessionid for a new jvmRoute - during failover
188     *
189     * @param id The session id
190     */

191    public String JavaDoc getJvmRouteId(String JavaDoc id)
192    {
193       String JavaDoc sessid = null;
194       if (id != null)
195       {
196          if (this.getJvmRoute() != null)
197          {
198             if (!this.getJvmRoute().equals(id.substring(id.indexOf('.') + 1, id.length())))
199             {
200                sessid = id.substring(0, id.indexOf('.') + 1) + this.getJvmRoute();
201                log.debug("JvmRoute id is :" + sessid);
202             }
203             else
204             {
205                return id;
206             }
207          }
208       }
209       return sessid;
210    }
211
212    /**
213     * Sets a new cookie for the given session id and response
214     *
215     * @param sessionId The session id
216     */

217    public void setSessionCookie(String JavaDoc sessionId)
218    {
219       HttpServletResponse JavaDoc response = (HttpServletResponse JavaDoc) ClusteredSessionValve.responseThreadLocal.get();
220       setNewSessionCookie(sessionId, response);
221    }
222
223    public void setNewSessionCookie(String JavaDoc sessionId, HttpServletResponse JavaDoc response)
224    {
225       if (response != null)
226       {
227          Context context = (Context) container;
228          if (context.getCookies())
229          {
230             // set a new session cookie
231
Cookie JavaDoc newCookie = new Cookie JavaDoc(Globals.SESSION_COOKIE_NAME, sessionId);
232             if (log.isDebugEnabled())
233             {
234                log.debug("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
235             }
236             newCookie.setMaxAge(-1);
237             newCookie.setPath(context.getPath());
238             response.addCookie(newCookie);
239          }
240       }
241    }
242
243    /**
244     * Find the session for the given id
245     *
246     * @param id The session id
247     * @return The session for the given id or null if not found in local or distributed store
248     */

249    public Session findSession(String JavaDoc id) throws IOException JavaDoc
250    {
251       ClusteredSessionCMP session = null;
252
253       if (id == null)
254       {
255          return null;
256       }
257
258       log.debug("Looking for session with id=" + id);
259
260       if (useLocalCache)
261       {
262          synchronized (sessions)
263          {
264             // first in local store
265
session = (ClusteredSessionCMP) sessions.get(id);
266          }
267
268          if (session == null && this.getJvmRoute() != null)
269          {
270             String JavaDoc key = getJvmRouteId(id);
271             synchronized (sessions)
272             {
273                //check for sessionid with new jvmRoute because of session failover
274
session = (ClusteredSessionCMP) sessions.get(key);
275             }
276
277             //set cookie with new sessionid
278
if (session != null)
279             {
280                // Do we use Cookies for session id storage?
281
setSessionCookie(session.getId());
282             }
283          }
284
285          // not found --> distributed store
286
if (session == null)
287          {
288             session = loadSession(id);
289
290             if (session == null && this.getJvmRoute() != null)
291             {
292                session = loadSession(getJvmRouteId(id));
293             }
294             // did we find the session in the distributed store?
295
if (session != null)
296             {
297                // If jvmRoute is set manipulate the sessionid and generate a cookie to make
298
// the session sticky on its new node
299

300                if (this.getJvmRoute() != null)
301                {
302                   String JavaDoc sessionid = getJvmRouteId(id);
303
304                   //setId() resets session id and adds it back to local & distributed store
305
session.setId(sessionid);
306
307                   //set cookie (if using cookies for session)
308
setSessionCookie(sessionid);
309
310                }
311                else
312                {
313                   // add to local store - no jvmRoute specified
314
log.debug("Found in distributed store - adding to local store");
315                   add(session);
316                }
317             }
318          }
319       }
320       else
321       {
322          // as we do not use the local cache look into the distributed store first
323
session = loadSession(id);
324
325          // maybe failover -> so we must lookup with the jvmRoute
326
if (session == null && this.getJvmRoute() != null)
327          {
328             String JavaDoc sessionId = this.getJvmRouteId(id);
329
330             session = loadSession(sessionId);
331
332             if (session != null)
333             {
334                session.setId(sessionId);
335                setSessionCookie(sessionId);
336                id = sessionId;
337             }
338          }
339
340          // update local session map with distributed session
341
// this is only needed for the eviction code
342
if (session != null)
343          {
344             synchronized (sessions)
345             {
346                sessions.put(id, session);
347             }
348          }
349       }
350
351       if (session != null)
352       {
353          log.debug("Found");
354       }
355       return session;
356    }
357
358    /**
359     * Add session to this Manager
360     *
361     * @param session The session that wants to be added
362     */

363    public void add(Session session)
364    {
365       if (session == null)
366       {
367          return;
368       }
369
370       if (!session.isValid())
371       {
372          log.error("Cannot add session with id=" + session.getId() + " because it is invalid");
373          return;
374       }
375
376       // is this session of the right type?
377
if (session instanceof ClusteredSessionCMP)
378       {
379          synchronized (sessions)
380          {
381             // add to local store first
382
sessions.put(session.getId(), session);
383
384             try
385             {
386                // add to distributed store
387
storeSession(session);
388             }
389             catch (Exception JavaDoc e)
390             {
391                log.error("Adding a session to the clustered store failed", e);
392             }
393             log.debug("Session with id=" + session.getId() + " added");
394          }
395       }
396       else
397       {
398          throw new IllegalArgumentException JavaDoc("You can only add ClusteredSessionCMPs to this Manager");
399       }
400    }
401
402    /**
403     * Removes a session from this Manager
404     *
405     * @param session that wants to be removed
406     */

407    public void remove(Session session)
408    {
409       if (session == null)
410       {
411          return;
412       }
413       synchronized (sessions)
414       {
415          try
416          {
417             // remove from distributed store
418
removeSession(session.getId());
419          }
420          catch (Exception JavaDoc e)
421          {
422             log.warn("Removing a session from the clustered store failed", e);
423          }
424          // remove from local store
425
sessions.remove(session.getId());
426          log.debug("Session with id=" + session.getId() + " removed");
427       }
428    }
429
430    /**
431     * Remove a session from the local store only
432     *
433     * @param session the session to be removed
434     */

435    public void removeLocal(Session session)
436    {
437       if (session == null)
438       {
439          return;
440       }
441       synchronized (sessions)
442       {
443          sessions.remove(session.getId());
444       }
445    }
446
447    /**
448     * Remove a session from the local store only
449     *
450     * @param id the session id of the session to be removed
451     */

452    public void removeLocal(String JavaDoc id)
453    {
454       if (id == null)
455       {
456          return;
457       }
458       synchronized (sessions)
459       {
460          sessions.remove(id);
461       }
462    }
463
464
465    protected void recycle(Session session)
466    {
467       // ignore - we do no recycling
468
}
469
470    /**
471     * Get a informational string about this class
472     *
473     * @return Information string
474     */

475    public String JavaDoc getInfo()
476    {
477       return info;
478    }
479
480    /**
481     * Start this Manager
482     *
483     * @throws LifecycleException
484     */

485    public void start() throws LifecycleException
486    {
487       startManager();
488    }
489
490    /**
491     * Stop this Manager
492     *
493     * @throws LifecycleException
494     */

495    public void stop() throws LifecycleException
496    {
497       stopManager();
498    }
499
500    /**
501     * Prepare for the beginning of active use of the public methods of this
502     * component. This method should be called after <code>configure()</code>,
503     * and before any of the public methods of the component are utilized.
504     *
505     * @throws IllegalStateException if this component has already been
506     * started
507     * @throws LifecycleException if this component detects a fatal error
508     * that prevents this component from being used
509     */

510    protected void startManager() throws LifecycleException
511    {
512       log.info("Starting");
513
514       // Validate and update our current component state
515
if (started)
516          throw new LifecycleException
517             (sm.getString("standardManager.alreadyStarted"));
518       lifecycle.fireLifecycleEvent(START_EVENT, null);
519       started = true;
520
521       // register ClusterManagerMBean to the MBeanServer
522
try
523       {
524          MBeanServer JavaDoc server = MBeanServerLocator.locateJBoss();
525          server.registerMBean(this, objectName);
526       }
527       catch (Exception JavaDoc e)
528       {
529          log.error("Could not register ClusterManagerMBean to MBeanServer", e);
530       }
531    }
532
533    /**
534     * Gracefully terminate the active use of the public methods of this
535     * component. This method should be the last one called on a given
536     * instance of this component.
537     *
538     * @throws IllegalStateException if this component has not been started
539     * @throws LifecycleException if this component detects a fatal error
540     * that needs to be reported
541     */

542    protected void stopManager() throws LifecycleException
543    {
544       log.info("Stopping");
545
546       // Validate and update our current component state
547
if (!started)
548          throw new LifecycleException
549             (sm.getString("standardManager.notStarted"));
550       lifecycle.fireLifecycleEvent(STOP_EVENT, null);
551       started = false;
552
553       // unregister ClusterManagerMBean from the MBeanServer
554
try
555       {
556          MBeanServer JavaDoc server = MBeanServerLocator.locateJBoss();
557          server.unregisterMBean(objectName);
558       }
559       catch (Exception JavaDoc e)
560       {
561          log.error("Could not unregister ClusterManagerMBean from MBeanServer", e);
562       }
563    }
564
565    /**
566     * Load persisted sessions (NOT supported by this Manager)
567     */

568    public void load() throws ClassNotFoundException JavaDoc, IOException JavaDoc
569    {
570       // We do not support persistence for sessions
571
}
572
573    /**
574     * Load persisted sessions (NOT supported by this Manager)
575     */

576    public void unload() throws IOException JavaDoc
577    {
578       // We do not support persistence for sessions
579
}
580
581    /**
582     * Overloaded run()-method of the session-cleanup-thread.
583     * We have our own cleanup-code - so no code here
584     */

585    public void run()
586    {
587       // We do our own expire() so no code here
588
}
589    // private methods ----------------------------------------
590

591    /**
592     * Get a new session-id from the distributed store
593     *
594     * @return new session-id
595     */

596    private String JavaDoc getNextId()
597    {
598       return proxy.getSessionId();
599    }
600
601    /**
602     * Store a session in the distributed store
603     *
604     * @param session The session to store
605     */

606    public boolean storeSession(Session session)
607    {
608       if (session == null)
609       {
610          return false;
611       }
612       if (session.isValid())
613       {
614          // Notify all session attributes that they get serialized (SRV 7.7.2)
615
ClusteredSessionCMP cmpSession = (ClusteredSessionCMP) session;
616          cmpSession.passivate();
617
618          if (log.isDebugEnabled())
619          {
620             log.debug("Replicating session with id " + session.getId());
621          }
622
623          if (!cmpSession.isReplicationTypeAlreadySet())
624             cmpSession.setReplicationTypeForSession(this.replicationType); //set default if not yet overidden
625

626          proxy.setHttpSession(session.getId(), (SerializableHttpSession) session);
627       }
628       return true;
629    }
630
631    /**
632     * Load a session from the distributed store
633     *
634     * @param id The session-id for the session to load
635     * @return the session or null if the session cannot be found in the distributed store
636     */

637    protected ClusteredSessionCMP loadSession(String JavaDoc id)
638    {
639       ClusteredSessionCMP session = null;
640
641       if (id == null)
642       {
643          return null;
644       }
645
646       try
647       {
648          /* Pass in the web ctx class loader to handle the loading of classes
649             that originate from the web application war.
650          */

651          ClassLoader JavaDoc ctxCL = super.getContainer().getLoader().getClassLoader();
652          session = (ClusteredSessionCMP) proxy.getHttpSession(id, ctxCL);
653
654          if (session != null)
655          {
656             // set attributes that were not serialized (are marked transient)
657
session.initAfterLoad(this);
658          }
659
660       }
661       catch (EJBException JavaDoc e)
662       {
663          // ignore
664
log.debug("Loading a session out of the clustered store failed", e);
665       }
666
667       return session;
668    }
669
670    /**
671     * Remove a session from the distributed store
672     *
673     * @param id The session-id for the session to remove
674     */

675    protected void removeSession(String JavaDoc id)
676    {
677       if (id == null)
678       {
679          return;
680       }
681       try
682       {
683          proxy.removeHttpSession(id);
684       }
685       catch (EJBException JavaDoc e)
686       {
687          //ignore
688
log.debug("Removing a session out of the clustered store failed", e);
689       }
690    }
691
692    /**
693     * Go through all sessions and look if they have expired
694     */

695    public void processExpires()
696    {
697       // What's the time?
698
long timeNow = System.currentTimeMillis();
699
700       // Get all sessions
701
Session sessions[] = findSessions();
702
703       log.debug("Looking for sessions that have expired");
704
705       for (int i = 0; i < sessions.length; ++i)
706       {
707          ClusteredSessionCMP session = (ClusteredSessionCMP) sessions[i];
708
709          // We only look at valid sessions
710
if (!session.isValid())
711          {
712             continue;
713          }
714
715          // How long are they allowed to be idle?
716
int maxInactiveInterval = session.getMaxInactiveInterval();
717
718          // Negative values = never expire
719
if (maxInactiveInterval < 0)
720          {
721             continue;
722          }
723
724          // How long has this session been idle?
725
int timeIdle =
726             (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
727
728          // Too long?
729
if (timeIdle >= maxInactiveInterval)
730          {
731             try
732             {
733                log.debug("Session with id = " + session.getId() + " has expired on local node");
734                // Did another node access this session?
735
// Try to get the session from the clustered store
736
ClusteredSessionCMP clusteredSession = loadSession(session.getId());
737                if (clusteredSession != null)
738                {
739                   int timeIdleCluster =
740                      (int) ((timeNow - clusteredSession.getLastAccessedTime()) / 1000L);
741                   if (timeIdleCluster < maxInactiveInterval)
742                   {
743                      log.debug("Session " + session.getId() + " has only expired on local node but is alive on another node - removing only from local store");
744                      // Remove from local store, because the session is
745
// alive on another node
746
removeLocal(session);
747                      continue;
748                   }
749
750                   log.debug("Session " + session.getId() + " has also expired on all other nodes - removing globally");
751                }
752
753
754                // Kick this session
755
session.expire();
756             }
757             catch (Throwable JavaDoc t)
758             {
759                log.error("Problems while expiring session with id = " + session.getId(), t);
760             }
761          }
762       }
763    }
764
765
766 }
767
Popular Tags