KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ha > httpsession > server > ClusteredHTTPSessionService


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

7
8 package org.jboss.ha.httpsession.server;
9
10 import java.util.Collection JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.Random JavaDoc;
13 import java.security.MessageDigest JavaDoc;
14 import java.security.NoSuchAlgorithmException JavaDoc;
15 import java.security.SecureRandom JavaDoc;
16
17 import javax.ejb.EJBException JavaDoc;
18 import javax.naming.InitialContext JavaDoc;
19 import javax.rmi.PortableRemoteObject JavaDoc;
20
21 import org.jboss.system.ServiceMBeanSupport;
22
23 import org.jboss.ha.httpsession.beanimpl.interfaces.ClusteredHTTPSession;
24 import org.jboss.ha.httpsession.beanimpl.interfaces.LocalClusteredHTTPSession;
25 import org.jboss.ha.httpsession.beanimpl.interfaces.ClusteredHTTPSessionBusiness;
26 import org.jboss.ha.httpsession.beanimpl.interfaces.ClusteredHTTPSessionHome;
27 import org.jboss.ha.httpsession.beanimpl.interfaces.LocalClusteredHTTPSessionHome;
28 import org.jboss.ha.httpsession.interfaces.SerializableHttpSession;
29
30 /**
31  * JMX Service implementation for ClusteredHTTPSessionServiceMBean
32  *
33  * @see org.jboss.ha.httpsession.server.ClusteredHTTPSessionServiceMBean
34  *
35  * @author <a HREF="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
36  * @version $Revision: 1.11 $
37  *
38  * <p><b>Revisions:</b>
39  *
40  * <p><b>31. decembre 2001 Sacha Labourey:</b>
41  * <ul>
42  * <li> First implementation </li>
43  * </ul>
44  */

45 public class ClusteredHTTPSessionService
46    extends ServiceMBeanSupport
47    implements ClusteredHTTPSessionServiceMBean
48 {
49    protected final static long CLEANUP_FREQUENCY = 30000; // every 30 seconds
50
protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
51
protected final static String JavaDoc SESSION_ID_HASH_ALGORITHM = "MD5";
52    protected final static String JavaDoc SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
53    protected final static String JavaDoc SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
54
55
56    protected ClusteredHTTPSessionHome httpSessionHome = null;
57    protected LocalClusteredHTTPSessionHome localHttpSessionHome = null;
58    protected ClusteredHTTPSessionService.CleanupDaemon cleanup = null;
59    protected long sessionTimeout = 15*60*1000;
60    protected boolean useLocalBean = false;
61
62    protected MessageDigest JavaDoc digest=null;
63    protected Random JavaDoc random=null;
64
65    public ClusteredHTTPSessionService()
66    {
67       super();
68    }
69
70    // ClusteredHTTPSessionServiceMBean ----------------------------------------------
71

72    public void setHttpSession (String JavaDoc sessionId, SerializableHttpSession session) throws EJBException JavaDoc
73    {
74       if (log.isDebugEnabled ())
75          log.debug ("setHttpSession called for session: "+ sessionId);
76
77       ClusteredHTTPSessionBusiness bean = null;
78
79       // We first try to find the bean or we create it if it does not yet exist
80
//
81
try
82       {
83          if (useLocalBean)
84          {
85             bean = localHttpSessionHome.findByPrimaryKey (sessionId);
86          }
87          else
88          {
89             bean = httpSessionHome.findByPrimaryKey (sessionId);
90          }
91
92          // We have one bean: we set its session
93
//
94
try
95          {
96             bean.setSession (session);
97          }
98          catch (Exception JavaDoc e)
99          {
100             throw new EJBException JavaDoc ("Exception in setHttpSession: ", e);
101          }
102
103       }
104       catch (javax.ejb.FinderException JavaDoc fe)
105       {
106          try
107          {
108             bean = createSession (sessionId, session);
109          }
110          catch (Exception JavaDoc e)
111          {
112             throw new EJBException JavaDoc ("Exception in setHttpSession while creating unexisting session: ", e);
113          }
114       }
115       catch (Exception JavaDoc e)
116       {
117          throw new EJBException JavaDoc ("Exception in setHttpSession: ", e);
118       }
119
120    }
121
122    public SerializableHttpSession getHttpSession (String JavaDoc sessionId, ClassLoader JavaDoc tcl)
123       throws EJBException JavaDoc
124    {
125       if (log.isDebugEnabled ())
126          log.debug ("getHttpSession called for session: "+ sessionId);
127
128       ClassLoader JavaDoc prevTCL = Thread.currentThread().getContextClassLoader();
129       try
130       {
131          Thread.currentThread().setContextClassLoader(tcl);
132          if (useLocalBean)
133          {
134             LocalClusteredHTTPSession sessionBean = localHttpSessionHome.findByPrimaryKey (sessionId);
135             return sessionBean.getSession ();
136          }
137          else
138          {
139             ClusteredHTTPSession sessionBean = httpSessionHome.findByPrimaryKey (sessionId);
140             return sessionBean.getSession ();
141          }
142       }
143       catch (Exception JavaDoc e)
144       {
145          throw new EJBException JavaDoc ("Exception in setHttpSession: ", e);
146       }
147       finally
148       {
149          Thread.currentThread().setContextClassLoader(prevTCL);
150       }
151    }
152
153    public void removeHttpSession (String JavaDoc sessionId) throws EJBException JavaDoc
154    {
155       if (log.isDebugEnabled ())
156          log.debug ("removeHttpSession called for session: "+ sessionId);
157       try
158       {
159          if (useLocalBean)
160          {
161             localHttpSessionHome.remove (sessionId);
162          }
163          else
164          {
165             httpSessionHome.remove (sessionId);
166          }
167       }
168       catch (Exception JavaDoc e)
169       {
170          throw new EJBException JavaDoc ("Exception in removeHttpSession: ", e);
171       }
172    }
173
174    public long getSessionTimeout () { return this.sessionTimeout; }
175    public void setSessionTimeout (long miliseconds) { this.sessionTimeout = miliseconds; }
176
177    public synchronized String JavaDoc getSessionId ()
178    {
179       String JavaDoc id=generateSessionId();
180       if (log.isDebugEnabled ())
181          log.debug ("getSessionId called: " + id);
182       return id;
183    }
184
185    public void setUseLocalBean (boolean useLocal)
186    {
187       int state = this.getState ();
188       if (state == this.STARTED || state == this.STARTING)
189          return;
190       else
191          this.useLocalBean = useLocal;
192    }
193
194    public boolean getUseLocalBean ()
195    {
196       return this.useLocalBean;
197    }
198
199    // ServiceMBeanSupport overrides ---------------------------------------------------
200

201    protected void startService ()
202       throws Exception JavaDoc
203    {
204       // we (try to) acquire a home reference to the entity bean that stores our sessions
205
//
206
this.initRefToBean ();
207       cleanup = new ClusteredHTTPSessionService.CleanupDaemon ();
208       cleanup.start ();
209    }
210
211    protected void stopService ()
212       throws Exception JavaDoc
213    {
214       cleanup.stop ();
215       httpSessionHome = null;
216    }
217
218    protected void initRefToBean () throws Exception JavaDoc
219    {
220       InitialContext JavaDoc jndiContext = new InitialContext JavaDoc ();
221       if (useLocalBean)
222       {
223          localHttpSessionHome = (LocalClusteredHTTPSessionHome)
224             jndiContext.lookup (LocalClusteredHTTPSessionHome.JNDI_NAME);
225       }
226       else
227       {
228          Object JavaDoc ref = jndiContext.lookup (ClusteredHTTPSessionHome.JNDI_NAME);
229          httpSessionHome = (ClusteredHTTPSessionHome)
230          PortableRemoteObject.narrow (ref, ClusteredHTTPSessionHome.class);
231       }
232    }
233
234    protected ClusteredHTTPSessionBusiness createSession (String JavaDoc id, SerializableHttpSession session) throws Exception JavaDoc
235    {
236       try
237       {
238          ClusteredHTTPSessionBusiness sessionBean = null;
239          if (useLocalBean)
240          {
241             sessionBean = localHttpSessionHome.create (id, session);
242          }
243          else
244          {
245             sessionBean = httpSessionHome.create (id, session);
246          }
247          return sessionBean;
248       }
249       catch (Exception JavaDoc e)
250       {
251          throw new EJBException JavaDoc ("Exception in createSession : ", e);
252       }
253    }
254
255    /**
256      Generate a session-id that is not guessable
257      @return generated session-id
258      */

259    protected synchronized String JavaDoc generateSessionId()
260    {
261         if (this.digest==null) {
262       this.digest=getDigest();
263     }
264
265     if (this.random==null) {
266        this.random=getRandom();
267     }
268
269     byte[] bytes=new byte[SESSION_ID_BYTES];
270
271     // get random bytes
272
this.random.nextBytes(bytes);
273
274     // Hash the random bytes
275
bytes=this.digest.digest(bytes);
276
277         // Render the result as a String of hexadecimal digits
278
return encode(bytes);
279    }
280
281    /**
282     Encode the bytes into a String with a slightly modified Base64-algorithm
283     This code was written by Kevin Kelley <kelley@ruralnet.net>
284     and adapted by Thomas Peuss <jboss@peuss.de>
285     @param data The bytes you want to encode
286     @return the encoded String
287    */

288    protected String JavaDoc encode(byte[] data)
289    {
290     char[] out = new char[((data.length + 2) / 3) * 4];
291     char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*".toCharArray();
292
293     //
294
// 3 bytes encode to 4 chars. Output is always an even
295
// multiple of 4 characters.
296
//
297
for (int i=0, index=0; i<data.length; i+=3, index+=4) {
298         boolean quad = false;
299         boolean trip = false;
300
301         int val = (0xFF & (int) data[i]);
302         val <<= 8;
303         if ((i+1) < data.length) {
304             val |= (0xFF & (int) data[i+1]);
305             trip = true;
306         }
307         val <<= 8;
308         if ((i+2) < data.length) {
309             val |= (0xFF & (int) data[i+2]);
310             quad = true;
311         }
312         out[index+3] = alphabet[(quad? (val & 0x3F): 64)];
313         val >>= 6;
314         out[index+2] = alphabet[(trip? (val & 0x3F): 64)];
315         val >>= 6;
316         out[index+1] = alphabet[val & 0x3F];
317         val >>= 6;
318         out[index+0] = alphabet[val & 0x3F];
319     }
320     return new String JavaDoc(out);
321    }
322
323    /**
324     get a random-number generator
325     @return a random-number generator
326    */

327    protected synchronized Random JavaDoc getRandom()
328    {
329       long seed;
330       Random JavaDoc random=null;
331
332       // Mix up the seed a bit
333
seed=System.currentTimeMillis();
334       seed^=Runtime.getRuntime().freeMemory();
335
336       try {
337          random=SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
338       }
339       catch (NoSuchAlgorithmException JavaDoc e)
340       {
341          try
342          {
343             random=SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
344          }
345          catch (NoSuchAlgorithmException JavaDoc e_alt)
346          {
347            log.error("Could not generate SecureRandom for session-id randomness",e);
348            log.error("Could not generate SecureRandom for session-id randomness",e_alt);
349            return null;
350          }
351       }
352
353       // set the generated seed for this PRNG
354
random.setSeed(seed);
355
356       return random;
357    }
358
359    /**
360      get a MessageDigest hash-generator
361      @return a hash generator
362      */

363    protected synchronized MessageDigest JavaDoc getDigest()
364    {
365       MessageDigest JavaDoc digest=null;
366
367       try {
368      digest=MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
369       } catch (NoSuchAlgorithmException JavaDoc e) {
370      log.error("Could not generate MessageDigest for session-id hashing",e);
371      return null;
372       }
373
374       return digest;
375    }
376
377    protected class CleanupDaemon
378       implements Runnable JavaDoc
379    {
380       protected boolean stopping = false;
381
382       public CleanupDaemon () throws Exception JavaDoc
383       {
384       }
385
386       public void start ()
387       {
388          stopping = false;
389          Thread JavaDoc t = new Thread JavaDoc (this, "ClusteredHTTPSessionService - CleanupDaemon");
390          t.start ();
391       }
392
393       public void stop ()
394       {
395          stopping = true;
396       }
397
398       public void run ()
399       {
400          while (!stopping)
401          {
402             try
403             {
404                // we get all beans and only check if they have timeouted. If we
405
// don't ask for the HTTPSession content attribute, this will *not*
406
// deserialize their content!
407
//
408
Collection JavaDoc allBeans = null;
409
410                if (useLocalBean)
411                   allBeans = localHttpSessionHome.findAll ();
412                else
413                   allBeans = httpSessionHome.findAll ();
414
415                Iterator JavaDoc iter = allBeans.iterator ();
416                long now = System.currentTimeMillis ();
417                while (iter.hasNext ())
418                {
419                   try
420                   {
421                      ClusteredHTTPSessionBusiness sessionBean = (ClusteredHTTPSessionBusiness)iter.next ();
422                      long lastAccess = sessionBean.getLastAccessedTime ();
423
424                      if ( ( now - lastAccess) > sessionTimeout )
425                      {
426                         if (useLocalBean)
427                            ((LocalClusteredHTTPSession)sessionBean).remove ();
428                         else
429                            ((ClusteredHTTPSession)sessionBean).remove ();
430                      }
431                   }
432                   catch(Exception JavaDoc notImportant)
433                   {
434                      log.debug(notImportant);
435                   }
436
437
438                   if (stopping)
439                      return;
440                }
441             }
442             catch (Exception JavaDoc e)
443             {
444                log.info ("unexpected exception while removing orphean replicated sessions", e);
445             }
446             finally
447             {
448                if (!stopping)
449                   // and we sleep a while...
450
//
451
try
452                   {
453                      synchronized (this)
454                      {
455                         this.wait (CLEANUP_FREQUENCY);
456                      }
457                   }
458                   catch (InterruptedException JavaDoc gameOver)
459                   {
460                      stopping = true;
461                      return;
462                   }
463
464             }
465          }
466       }
467    }
468 }
469
Popular Tags