KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > StatefulSessionFilePersistenceManager


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.ejb.plugins;
23
24 import java.io.BufferedInputStream JavaDoc;
25 import java.io.BufferedOutputStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.FileNotFoundException JavaDoc;
29 import java.io.FileOutputStream JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.rmi.RemoteException JavaDoc;
32 import java.security.AccessController JavaDoc;
33 import java.security.PrivilegedAction JavaDoc;
34 import java.security.PrivilegedActionException JavaDoc;
35 import java.security.PrivilegedExceptionAction JavaDoc;
36
37 import javax.ejb.EJBException JavaDoc;
38 import javax.ejb.RemoveException JavaDoc;
39 import javax.ejb.SessionBean JavaDoc;
40
41 import org.jboss.ejb.AllowedOperationsAssociation;
42 import org.jboss.ejb.Container;
43 import org.jboss.ejb.StatefulSessionContainer;
44 import org.jboss.ejb.StatefulSessionEnterpriseContext;
45 import org.jboss.ejb.StatefulSessionPersistenceManager;
46 import org.jboss.system.ServiceMBeanSupport;
47 import org.jboss.system.server.ServerConfigLocator;
48 import org.jboss.util.id.UID;
49
50 /**
51  * A file-based stateful session bean persistence manager.
52  * <p>
53  * Reads and writes session bean objects to files by using the
54  * standard Java serialization mechanism.
55  * <p>
56  * Passivated state files are stored under:
57  * <tt><em>jboss-server-data-dir</em>/<em>storeDirectoryName</em>/<em>ejb-name</em>-<em>unique-id</em></tt>.
58  * <p>
59  * Since ejb-name is not unique across deployments we generate a <em>unique-id</em> to make
60  * sure that beans with the same EJB name do not collide.
61  *
62  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard ´┐Żberg</a>
63  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
64  * @author <a HREF="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
65  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
66  * @author <a HREF="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
67  * @version <tt>$Revision: 40314 $</tt>
68  */

69 public class StatefulSessionFilePersistenceManager extends ServiceMBeanSupport
70    implements StatefulSessionPersistenceManager, StatefulSessionFilePersistenceManagerMBean
71 {
72    /** The default store directory name ("<tt>sessions</tt>"). */
73    public static final String JavaDoc DEFAULT_STORE_DIRECTORY_NAME = "sessions";
74
75    /** Our container. */
76    private StatefulSessionContainer con;
77
78    /**
79     * The sub-directory name under the server data directory where
80     * session data is stored.
81     *
82     * @see #DEFAULT_STORE_DIRECTORY_NAME
83     * @see #setStoreDirectoryName
84     */

85    private String JavaDoc storeDirName = DEFAULT_STORE_DIRECTORY_NAME;
86
87    /** The base directory where sessions state files are stored for our container. */
88    private File JavaDoc storeDir;
89
90    /**
91     * Enable purging leftover state files at create and destroy
92     * time (default is true).
93     */

94    private boolean purgeEnabled = true;
95
96    /**
97     * Saves a reference to the {@link StatefulSessionContainer} for
98     * its bean type.
99     *
100     * @throws ClassCastException Container is not a StatefulSessionContainer.
101     */

102    public void setContainer(final Container con)
103    {
104       this.con = (StatefulSessionContainer) con;
105    }
106
107    //
108
// jason: these properties are intended to be used when plugins/interceptors
109
// can take configuration values (need to update xml schema and processors).
110
//
111

112    /**
113     * Set the sub-directory name under the server data directory
114     * where session data will be stored.
115     *
116     * <p>
117     * This value will be appened to the value of
118     * <tt><em>jboss-server-data-dir</em></tt>.
119     *
120     * <p>
121     * This value is only used during creation and will not dynamically
122     * change the store directory when set after the create step has finished.
123     *
124     * @jmx:managed-attribute
125     *
126     * @param dirName A sub-directory name.
127     */

128    public void setStoreDirectoryName(final String JavaDoc dirName)
129    {
130       this.storeDirName = dirName;
131    }
132
133    /**
134     * Get the sub-directory name under the server data directory
135     * where session data is stored.
136     *
137     * jmx:managed-attribute
138     *
139     * @see #setStoreDirectoryName
140     *
141     * @return A sub-directory name.
142     */

143    public String JavaDoc getStoreDirectoryName()
144    {
145       return storeDirName;
146    }
147
148    /**
149     * Set the stale session state purge enabled flag.
150     *
151     * jmx:managed-attribute
152     *
153     * @param flag The toggle flag to enable or disable purging.
154     */

155    public void setPurgeEnabled(final boolean flag)
156    {
157       this.purgeEnabled = flag;
158    }
159
160    /**
161     * Get the stale session state purge enabled flag.
162     *
163     * jmx:managed-attribute
164     *
165     * @return True if purge is enabled.
166     */

167    public boolean getPurgeEnabled()
168    {
169       return purgeEnabled;
170    }
171
172    /**
173     * Returns the directory used to store session passivation state files.
174     *
175     * jmx:managed-attribute
176     *
177     * @return The directory used to store session passivation state files.
178     */

179    public File JavaDoc getStoreDirectory()
180    {
181       return storeDir;
182    }
183
184    /**
185     * Setup the session data storage directory.
186     *
187     * <p>Purges any existing session data found.
188     */

189    protected void createService() throws Exception JavaDoc
190    {
191       // Initialize the dataStore
192
String JavaDoc ejbName = con.getBeanMetaData().getEjbName();
193
194       // Get the system data directory
195
File JavaDoc dir = ServerConfigLocator.locate().getServerTempDir();
196
197       // Setup the reference to the session data store directory
198
dir = new File JavaDoc(dir, storeDirName);
199       // ejbName is not unique across all deployments, so use a unique token
200
dir = new File JavaDoc(dir, ejbName + "-" + new UID().toString());
201       storeDir = dir;
202
203       log.debug("Storing sessions for '" + ejbName + "' in: " + storeDir);
204
205       // if the directory does not exist then try to create it
206
if( !storeDir.exists() )
207       {
208          if( MkdirsFileAction.mkdirs(storeDir) == false )
209          {
210             throw new IOException JavaDoc("Failed to create directory: " + storeDir);
211          }
212       }
213       
214       // make sure we have a directory
215
if( !storeDir.isDirectory() )
216       {
217          throw new IOException JavaDoc("File exists where directory expected: " + storeDir);
218       }
219
220       // make sure we can read and write to it
221
if( !storeDir.canWrite() || !storeDir.canRead() )
222       {
223          throw new IOException JavaDoc("Directory must be readable and writable: " + storeDir);
224       }
225       
226       // Purge state session state files, should be none, due to unique directory
227
purgeAllSessionData();
228    }
229
230    /**
231     * Removes any state files left in the storgage directory.
232     */

233    private void purgeAllSessionData()
234    {
235       if( !purgeEnabled ) return;
236
237       log.debug("Purging all session data in: " + storeDir);
238
239       File JavaDoc[] sessions = storeDir.listFiles();
240       for(int i = 0; i < sessions.length; i++)
241       {
242          if( !sessions[i].delete() )
243          {
244             log.warn("Failed to delete session state file: " + sessions[i]);
245          }
246          else
247          {
248             log.debug("Removed stale session state: " + sessions[i]);
249          }
250       }
251    }
252
253    /**
254     * Purge any data in the store, and then the store directory too.
255     */

256    protected void destroyService() throws Exception JavaDoc
257    {
258       // Purge data and attempt to delete directory
259
purgeAllSessionData();
260
261       // Nuke the directory too if purge is enabled
262
if( purgeEnabled && !storeDir.delete() )
263       {
264          log.warn("Failed to delete session state storage directory: " + storeDir);
265       }
266    }
267
268    /**
269     * Make a session state file for the given instance id.
270     */

271    private File JavaDoc getFile(final Object JavaDoc id)
272    {
273       //
274
// jason: may have to translate id into a os-safe string, though
275
// the format of UID is safe on Unix and win32 already...
276
//
277

278       return new File JavaDoc(storeDir, String.valueOf(id) + ".ser");
279    }
280
281    /**
282     * @return A {@link UID}.
283     */

284    public Object JavaDoc createId(StatefulSessionEnterpriseContext ctx)
285       throws Exception JavaDoc
286    {
287       return new UID();
288    }
289
290    /**
291     * Non-operation.
292     */

293    public void createdSession(StatefulSessionEnterpriseContext ctx)
294       throws Exception JavaDoc
295    {
296       // nothing
297
}
298
299    /**
300     * Restores session state from the serialized file & invokes
301     * {@link SessionBean#ejbActivate} on the target bean.
302     */

303    public void activateSession(final StatefulSessionEnterpriseContext ctx)
304       throws RemoteException JavaDoc
305    {
306       boolean trace = log.isTraceEnabled();
307       if (trace)
308       {
309          log.trace("Attempting to activate; ctx=" + ctx);
310       }
311
312       Object JavaDoc id = ctx.getId();
313       
314       // Load state
315
File JavaDoc file = getFile(id);
316       if (trace)
317       {
318          log.trace("Reading session state from: " + file);
319       }
320
321       try
322       {
323          FileInputStream JavaDoc fis = FISAction.open(file);
324          SessionObjectInputStream in = new SessionObjectInputStream(ctx,
325             new BufferedInputStream JavaDoc(fis));
326
327          try
328          {
329             Object JavaDoc obj = in.readObject();
330             if (trace)
331             {
332                log.trace("Session state: " + obj);
333             }
334             ctx.setInstance(obj);
335          }
336          finally
337          {
338             in.close();
339          }
340       }
341       catch(Exception JavaDoc e)
342       {
343          throw new EJBException JavaDoc("Could not activate; failed to " +
344             "restore state", e);
345       }
346
347       removePassivated(id);
348             
349       try
350       {
351          // Instruct the bean to perform activation logic
352
AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_ACTIVATE);
353          SessionBean JavaDoc bean = (SessionBean JavaDoc) ctx.getInstance();
354          bean.ejbActivate();
355       }
356       finally
357       {
358          AllowedOperationsAssociation.popInMethodFlag();
359       }
360
361       if (trace)
362       {
363          log.trace("Activation complete; ctx=" + ctx);
364       }
365    }
366
367    /**
368     * Invokes {@link SessionBean#ejbPassivate} on the target bean and saves the
369     * state of the session to a file.
370     */

371    public void passivateSession(final StatefulSessionEnterpriseContext ctx)
372       throws RemoteException JavaDoc
373    {
374       boolean trace = log.isTraceEnabled();
375       if (trace)
376       {
377          log.trace("Attempting to passivate; ctx=" + ctx);
378       }
379
380       try
381       {
382          // Instruct the bean to perform passivation logic
383
AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_PASSIVATE);
384          SessionBean JavaDoc bean = (SessionBean JavaDoc) ctx.getInstance();
385          bean.ejbPassivate();
386       }
387       finally
388       {
389          AllowedOperationsAssociation.popInMethodFlag();
390       }
391       
392       // Store state
393

394       File JavaDoc file = getFile(ctx.getId());
395       if (trace)
396       {
397          log.trace("Saving session state to: " + file);
398       }
399
400       try
401       {
402          FileOutputStream JavaDoc fos = FOSAction.open(file);
403          SessionObjectOutputStream out = new SessionObjectOutputStream(
404             new BufferedOutputStream JavaDoc(fos));
405
406          Object JavaDoc obj = ctx.getInstance();
407          if (trace)
408          {
409             log.trace("Writing session state: " + obj);
410          }
411
412          try
413          {
414             out.writeObject(obj);
415          }
416          finally
417          {
418             out.close();
419          }
420       }
421       catch(Exception JavaDoc e)
422       {
423          throw new EJBException JavaDoc("Could not passivate; failed to save state", e);
424       }
425
426       if (trace)
427       {
428          log.trace("Passivation complete; ctx=" + ctx);
429       }
430    }
431
432    /**
433     * Invokes {@link SessionBean#ejbRemove} on the target bean.
434     */

435    public void removeSession(final StatefulSessionEnterpriseContext ctx)
436       throws RemoteException JavaDoc, RemoveException JavaDoc
437    {
438       boolean trace = log.isTraceEnabled();
439       if (trace)
440       {
441          log.trace("Attempting to remove; ctx=" + ctx);
442       }
443       
444       // Instruct the bean to perform removal logic
445
SessionBean JavaDoc bean = (SessionBean JavaDoc) ctx.getInstance();
446       bean.ejbRemove();
447
448       if (trace)
449       {
450          log.trace("Removal complete; ctx=" + ctx);
451       }
452    }
453
454    /**
455     * Removes the saved state file (if any) for the given session id.
456     */

457    public void removePassivated(final Object JavaDoc id)
458    {
459       boolean trace = log.isTraceEnabled();
460
461       File JavaDoc file = getFile(id);
462
463       // only attempt to delete if the file exists
464
if( file.exists() )
465       {
466          if (trace)
467          {
468             log.trace("Removing passivated state file: " + file);
469          }
470
471          if( DeleteFileAction.delete(file) == false )
472          {
473             log.warn("Failed to delete passivated state file: " + file);
474          }
475       }
476    }
477
478    static class DeleteFileAction implements PrivilegedAction JavaDoc
479    {
480       File JavaDoc file;
481       DeleteFileAction(File JavaDoc file)
482       {
483          this.file = file;
484       }
485       public Object JavaDoc run()
486       {
487          boolean deleted = file.delete();
488          return new Boolean JavaDoc(deleted);
489       }
490       static boolean delete(File JavaDoc file)
491       {
492          DeleteFileAction action = new DeleteFileAction(file);
493          Boolean JavaDoc deleted = (Boolean JavaDoc) AccessController.doPrivileged(action);
494          return deleted.booleanValue();
495       }
496    }
497    static class MkdirsFileAction implements PrivilegedAction JavaDoc
498    {
499       File JavaDoc file;
500       MkdirsFileAction(File JavaDoc file)
501       {
502          this.file = file;
503       }
504       public Object JavaDoc run()
505       {
506          boolean ok = file.mkdirs();
507          return new Boolean JavaDoc(ok);
508       }
509       static boolean mkdirs(File JavaDoc file)
510       {
511          MkdirsFileAction action = new MkdirsFileAction(file);
512          Boolean JavaDoc ok = (Boolean JavaDoc) AccessController.doPrivileged(action);
513          return ok.booleanValue();
514       }
515    }
516
517    static class FISAction implements PrivilegedExceptionAction JavaDoc
518    {
519       File JavaDoc file;
520       FISAction(File JavaDoc file)
521       {
522          this.file = file;
523       }
524       public Object JavaDoc run() throws Exception JavaDoc
525       {
526          FileInputStream JavaDoc fis = new FileInputStream JavaDoc(file);
527          return fis;
528       }
529       static FileInputStream JavaDoc open(File JavaDoc file) throws FileNotFoundException JavaDoc
530       {
531          FISAction action = new FISAction(file);
532          FileInputStream JavaDoc fis = null;
533          try
534          {
535             fis = (FileInputStream JavaDoc) AccessController.doPrivileged(action);
536          }
537          catch(PrivilegedActionException JavaDoc e)
538          {
539             throw (FileNotFoundException JavaDoc) e.getException();
540          }
541
542          return fis;
543       }
544    }
545
546    static class FOSAction implements PrivilegedExceptionAction JavaDoc
547    {
548       File JavaDoc file;
549       FOSAction(File JavaDoc file)
550       {
551          this.file = file;
552       }
553       public Object JavaDoc run() throws Exception JavaDoc
554       {
555          FileOutputStream JavaDoc fis = new FileOutputStream JavaDoc(file);
556          return fis;
557       }
558       static FileOutputStream JavaDoc open(File JavaDoc file) throws FileNotFoundException JavaDoc
559       {
560          FOSAction action = new FOSAction(file);
561          FileOutputStream JavaDoc fos = null;
562          try
563          {
564             fos = (FileOutputStream JavaDoc) AccessController.doPrivileged(action);
565          }
566          catch(PrivilegedActionException JavaDoc e)
567          {
568             throw (FileNotFoundException JavaDoc) e.getException();
569          }
570
571          return fos;
572       }
573    }
574 }
575
Popular Tags