KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb3 > cache > simple > 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.ejb3.cache.simple;
23
24 import java.io.BufferedInputStream JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.FileNotFoundException JavaDoc;
28 import java.io.FileOutputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.ObjectInputStream JavaDoc;
31 import java.io.ObjectOutputStream 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 import javax.ejb.EJBException JavaDoc;
37 import org.jboss.ejb3.Container;
38 import org.jboss.ejb3.stateful.StatefulBeanContext;
39 import org.jboss.serial.io.JBossObjectInputStream;
40 import org.jboss.serial.io.JBossObjectOutputStream;
41 import org.jboss.system.server.ServerConfigLocator;
42 import org.jboss.util.id.UID;
43
44 /**
45  * A file-based stateful session bean persistence manager.
46  * <p/>
47  * <p/>
48  * Reads and writes session bean objects to files by using the
49  * standard Java serialization mechanism.
50  * <p/>
51  * <p/>
52  * Passivated state files are stored under:
53  * <tt><em>jboss-server-data-dir</em>/<em>storeDirectoryName</em>/<em>ejb-name</em>-<em>unique-id</em></tt>.
54  * <p/>
55  * <p/>
56  * Since ejb-name is not unique across deployments we generate a <em>unique-id</em> to make
57  * sure that beans with the same EJB name do not collide.
58  *
59  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard ?berg</a>
60  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
61  * @author <a HREF="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
62  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
63  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
64  * @version <tt>$Revision: 44568 $</tt>
65  * @jmx:mbean extends="org.jboss.system.ServiceMBean"
66  */

67 public class StatefulSessionFilePersistenceManager implements StatefulSessionPersistenceManager
68 {
69
70    /**
71     * The sub-directory name under the server data directory where
72     * session data is stored.
73     *
74     * @see #DEFAULT_STORE_DIRECTORY_NAME
75     * @see #setStoreDirectoryName
76     */

77    private String JavaDoc storeDirName = DEFAULT_STORE_DIRECTORY_NAME;
78
79    /**
80     * The base directory where sessions state files are stored for our container.
81     */

82    private File JavaDoc storeDir;
83
84    private Container con;
85
86    /**
87     * Enable purging leftover state files at create and destroy
88     * time (default is true).
89     */

90    private boolean purgeEnabled = true;
91
92    /**
93     * Set the sub-directory name under the server data directory
94     * where session data will be stored.
95     * <p/>
96     * <p/>
97     * This value will be appened to the value of
98     * <tt><em>jboss-server-data-dir</em></tt>.
99     * <p/>
100     * <p/>
101     * This value is only used during creation and will not dynamically
102     * change the store directory when set after the create step has finished.
103     *
104     * @param dirName A sub-directory name.
105     */

106    public void setStoreDirectoryName(final String JavaDoc dirName)
107    {
108       this.storeDirName = dirName;
109    }
110
111    /**
112     * Get the sub-directory name under the server data directory
113     * where session data is stored.
114     *
115     * @return A sub-directory name.
116     * @see #setStoreDirectoryName
117     */

118    public String JavaDoc getStoreDirectoryName()
119    {
120       return storeDirName;
121    }
122
123    /**
124     * Set the stale session state purge enabled flag.
125     *
126     * @param flag The toggle flag to enable or disable purging.
127     */

128    public void setPurgeEnabled(final boolean flag)
129    {
130       this.purgeEnabled = flag;
131    }
132
133    /**
134     * Get the stale session state purge enabled flag.
135     *
136     * @return True if purge is enabled.
137     */

138    public boolean getPurgeEnabled()
139    {
140       return purgeEnabled;
141    }
142
143    /**
144     * Returns the directory used to store session passivation state files.
145     *
146     * @return The directory used to store session passivation state files.
147     */

148    public File JavaDoc getStoreDirectory()
149    {
150       return storeDir;
151    }
152
153    public void setContainer(Container con)
154    {
155       this.con = con;
156    }
157
158    /**
159     * Setup the session data storage directory.
160     * <p/>
161     * <p>Purges any existing session data found.
162     */

163    public void initialize(Container con) throws Exception JavaDoc
164    {
165       this.con = con;
166       boolean debug = log.isDebugEnabled();
167
168       // Initialize the dataStore
169

170       String JavaDoc ejbName = con.getEjbName();
171
172       // Get the system data directory
173
File JavaDoc dir = ServerConfigLocator.locate().getServerTempDir();
174
175       // Setup the reference to the session data store directory
176
dir = new File JavaDoc(dir, storeDirName);
177       // ejbName is not unique across all deployments, so use a unique token
178
dir = new File JavaDoc(dir, ejbName + "-" + new UID().toString());
179       storeDir = dir;
180
181       if (debug)
182       {
183          log.debug("Storing sessions for '" + ejbName + "' in: " + storeDir);
184       }
185
186       // if the directory does not exist then try to create it
187
if (!storeDir.exists())
188       {
189          if (MkdirsFileAction.mkdirs(storeDir) == false)
190          {
191             throw new IOException JavaDoc("Failed to create directory: " + storeDir);
192          }
193       }
194
195       // make sure we have a directory
196
if (!storeDir.isDirectory())
197       {
198          throw new IOException JavaDoc("File exists where directory expected: " + storeDir);
199       }
200
201       // make sure we can read and write to it
202
if (!storeDir.canWrite() || !storeDir.canRead())
203       {
204          throw new IOException JavaDoc("Directory must be readable and writable: " + storeDir);
205       }
206
207       // Purge state session state files, should be none, due to unique directory
208
purgeAllSessionData();
209    }
210
211    /**
212     * Removes any state files left in the storgage directory.
213     */

214    public void purgeAllSessionData()
215    {
216       if (!purgeEnabled) return;
217
218       log.debug("Purging all session data in: " + storeDir);
219
220       File JavaDoc[] sessions = storeDir.listFiles();
221       for (int i = 0; i < sessions.length; i++)
222       {
223          if (!sessions[i].delete())
224          {
225             log.warn("Failed to delete session state file: " + sessions[i]);
226          }
227          else
228          {
229             log.debug("Removed stale session state: " + sessions[i]);
230          }
231       }
232    }
233
234    /**
235     * Purge any data in the store, and then the store directory too.
236     */

237    public void destroy() throws Exception JavaDoc
238    {
239       // Purge data and attempt to delete directory
240
purgeAllSessionData();
241
242       // Nuke the directory too if purge is enabled
243
if (purgeEnabled && !storeDir.delete())
244       {
245          log.warn("Failed to delete session state storage directory: " + storeDir);
246       }
247    }
248
249    /**
250     * Make a session state file for the given instance id.
251     */

252    private File JavaDoc getFile(final Object JavaDoc id)
253    {
254       //
255
// jason: may have to translate id into a os-safe string, though
256
// the format of UID is safe on Unix and win32 already...
257
//
258

259       return new File JavaDoc(storeDir, String.valueOf(id) + ".ser");
260    }
261
262    /**
263     * Restores session state from the serialized file & invokes
264     * {@link javax.ejb.SessionBean#ejbActivate} on the target bean.
265     */

266    public StatefulBeanContext activateSession(Object JavaDoc id)
267    {
268       boolean debug = log.isDebugEnabled();
269       if (debug)
270       {
271          log.debug("Attempting to activate; id=" + id);
272       }
273
274       // Load state
275
File JavaDoc file = getFile(id);
276       if (debug)
277       {
278          log.debug("Reading session state from: " + file);
279       }
280       if (!file.exists()) return null;
281
282       StatefulBeanContext bean = null;
283       try
284       {
285          FileInputStream JavaDoc fis = FISAction.open(file);
286          // todo need to rewrite SessionObjectInputStream to support EJB3 classes
287
ObjectInputStream JavaDoc in;
288
289          in = new JBossObjectInputStream(new BufferedInputStream JavaDoc(fis));
290          try
291          {
292             bean = (StatefulBeanContext) in.readObject();
293          }
294          finally
295          {
296             fis.close();
297             in.close();
298          }
299       }
300       catch (EJBException JavaDoc e)
301       {
302          throw e;
303       }
304       catch (Exception JavaDoc e)
305       {
306          throw new EJBException JavaDoc("Could not activate; failed to " +
307                                 "restore state", e);
308       }
309
310       removePassivated(id);
311
312       bean.postActivate();
313       return bean;
314    }
315
316    /**
317     * Invokes {@link javax.ejb.SessionBean#ejbPassivate} on the target bean and saves the
318     * state of the session to a file.
319     */

320    public void passivateSession(StatefulBeanContext ctx)
321    {
322       boolean debug = log.isDebugEnabled();
323       if (debug)
324       {
325          log.debug("Attempting to passivate; id=" + ctx.getId());
326       }
327
328       ctx.prePassivate();
329       // Store state
330

331       File JavaDoc file = getFile(ctx.getId());
332       if (debug)
333       {
334          log.debug("Saving session state to: " + file);
335       }
336
337       try
338       {
339          FileOutputStream JavaDoc fos = FOSAction.open(file);
340          // todo need to rewrite SessionObjectOutputStream to support EJB3 classes
341
ObjectOutputStream JavaDoc out;
342
343          out = new JBossObjectOutputStream(fos, false);
344
345          try
346          {
347             out.writeObject(ctx);
348             out.flush();
349             fos.flush();
350             fos.close();
351          }
352          finally
353          {
354             out.close();
355          }
356       }
357       catch (EJBException JavaDoc e)
358       {
359          throw e;
360       }
361       catch (Exception JavaDoc e)
362       {
363          throw new EJBException JavaDoc("Could not passivate; failed to save state", e);
364       }
365
366       if (debug)
367       {
368          log.debug("Passivation complete; id=" + ctx.getId());
369       }
370    }
371
372    /**
373     * Removes the saved state file (if any) for the given session id.
374     */

375    public void removePassivated(Object JavaDoc id)
376    {
377       boolean debug = log.isDebugEnabled();
378
379       File JavaDoc file = getFile(id);
380
381       // only attempt to delete if the file exists
382
if (file.exists())
383       {
384          if (debug)
385          {
386             log.debug("Removing passivated state file: " + file);
387          }
388
389          if (DeleteFileAction.delete(file) == false)
390          {
391             log.warn("Failed to delete passivated state file: " + file);
392          }
393       }
394    }
395
396    static class DeleteFileAction implements PrivilegedAction JavaDoc
397    {
398       File JavaDoc file;
399
400       DeleteFileAction(File JavaDoc file)
401       {
402          this.file = file;
403       }
404
405       public Object JavaDoc run()
406       {
407          boolean deleted = file.delete();
408          return new Boolean JavaDoc(deleted);
409       }
410
411       static boolean delete(File JavaDoc file)
412       {
413          DeleteFileAction action = new DeleteFileAction(file);
414          Boolean JavaDoc deleted = (Boolean JavaDoc) AccessController.doPrivileged(action);
415          return deleted.booleanValue();
416       }
417    }
418
419    static class MkdirsFileAction implements PrivilegedAction JavaDoc
420    {
421       File JavaDoc file;
422
423       MkdirsFileAction(File JavaDoc file)
424       {
425          this.file = file;
426       }
427
428       public Object JavaDoc run()
429       {
430          boolean ok = file.mkdirs();
431          return new Boolean JavaDoc(ok);
432       }
433
434       static boolean mkdirs(File JavaDoc file)
435       {
436          MkdirsFileAction action = new MkdirsFileAction(file);
437          Boolean JavaDoc ok = (Boolean JavaDoc) AccessController.doPrivileged(action);
438          return ok.booleanValue();
439       }
440    }
441
442    static class FISAction implements PrivilegedExceptionAction JavaDoc
443    {
444       File JavaDoc file;
445
446       FISAction(File JavaDoc file)
447       {
448          this.file = file;
449       }
450
451       public Object JavaDoc run() throws Exception JavaDoc
452       {
453          FileInputStream JavaDoc fis = new FileInputStream JavaDoc(file);
454          return fis;
455       }
456
457       static FileInputStream JavaDoc open(File JavaDoc file) throws FileNotFoundException JavaDoc
458       {
459          FISAction action = new FISAction(file);
460          FileInputStream JavaDoc fis = null;
461          try
462          {
463             fis = (FileInputStream JavaDoc) AccessController.doPrivileged(action);
464          }
465          catch (PrivilegedActionException JavaDoc e)
466          {
467             throw (FileNotFoundException JavaDoc) e.getException();
468          }
469
470          return fis;
471       }
472    }
473
474    static class FOSAction implements PrivilegedExceptionAction JavaDoc
475    {
476       File JavaDoc file;
477
478       FOSAction(File JavaDoc file)
479       {
480          this.file = file;
481       }
482
483       public Object JavaDoc run() throws Exception JavaDoc
484       {
485          FileOutputStream JavaDoc fis = new FileOutputStream JavaDoc(file);
486          return fis;
487       }
488
489       static FileOutputStream JavaDoc open(File JavaDoc file) throws FileNotFoundException JavaDoc
490       {
491          FOSAction action = new FOSAction(file);
492          FileOutputStream JavaDoc fos = null;
493          try
494          {
495             fos = (FileOutputStream JavaDoc) AccessController.doPrivileged(action);
496          }
497          catch (PrivilegedActionException JavaDoc e)
498          {
499             throw (FileNotFoundException JavaDoc) e.getException();
500          }
501
502          return fos;
503       }
504    }
505 }
506
Popular Tags