KickJava   Java API By Example, From Geeks To Geeks.

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


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.File JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.io.ObjectInputStream JavaDoc;
28 import java.io.ObjectOutputStream JavaDoc;
29 import java.io.FileOutputStream JavaDoc;
30 import java.io.FileInputStream JavaDoc;
31 import java.io.BufferedOutputStream JavaDoc;
32 import java.io.BufferedInputStream JavaDoc;
33 import java.io.IOException JavaDoc;
34
35 import java.lang.reflect.Method JavaDoc;
36 import java.lang.reflect.Field JavaDoc;
37
38 import java.util.ArrayList JavaDoc;
39 import java.util.Collection JavaDoc;
40 import java.util.Collections JavaDoc;
41 import java.util.Iterator JavaDoc;
42
43 import javax.ejb.EJBObject JavaDoc;
44 import javax.ejb.Handle JavaDoc;
45 import javax.ejb.CreateException JavaDoc;
46 import javax.ejb.DuplicateKeyException JavaDoc;
47 import javax.ejb.EJBException JavaDoc;
48 import javax.ejb.FinderException JavaDoc;
49 import javax.ejb.RemoveException JavaDoc;
50
51 import org.jboss.ejb.Container;
52 import org.jboss.ejb.EntityContainer;
53 import org.jboss.ejb.EntityPersistenceStore;
54 import org.jboss.ejb.EntityEnterpriseContext;
55 import org.jboss.ejb.GenericEntityObjectFactory;
56 import org.jboss.metadata.EntityMetaData;
57
58 import org.jboss.system.server.ServerConfigLocator;
59 import org.jboss.system.ServiceMBeanSupport;
60
61 import org.jboss.util.file.FilenameSuffixFilter;
62
63 /**
64  * A file-based CMP entity bean persistence manager.
65  *
66  * <p>
67  * Reads and writes entity bean objects to files by using the
68  * standard Java serialization mechanism.
69  *
70  * <p>
71  * Enitiy state files are stored under:
72  * <tt><em>jboss-server-data-dir</em>/<em>storeDirectoryName</em>/<em>ejb-name</em></tt>.
73  *
74  * <p>
75  * Note, currently the name of the entity must be unique across the server, or
76  * unless the store directory is changed, to avoid data collisions.
77  *
78  * <p>
79  * jason: disabled because XDoclet can not handle right now
80  * _@_jmx:mbean extends="org.jboss.system.ServiceMBean"
81  *
82  * @version <tt>$Revision: 37459 $</tt>
83  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
84  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
85  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
86  * <p><b>20010801 marc fleury:</b>
87  * <ul>
88  * <li>- insertion in cache upon create in now done in the instance interceptor
89  * </ul>
90  * <p><b>20011201 Dain Sundstrom:</b>
91  * <ul>
92  * <li>- added createBeanInstance and initEntity methods
93  * </ul>
94  * <p><b>20020525 Dain Sundstrom:</b>
95  * <ul>
96  * <li>- Replaced FinderResults with Collection
97  * <li>- Removed unused method loadEntities
98  * </ul>
99  */

100 public class CMPFilePersistenceManager
101    extends ServiceMBeanSupport
102    implements EntityPersistenceStore /*, CMPFilePersistenceManagerMBean */
103 {
104    /** The default store directory name ("<tt>entities</tt>"). */
105    public static final String JavaDoc DEFAULT_STORE_DIRECTORY_NAME = "entities";
106    
107    /** Our container. */
108    private EntityContainer con;
109
110    /**
111     * The sub-directory name under the server data directory where
112     * entity data is stored.
113     *
114     * @see #DEFAULT_STORE_DIRECTORY_NAME
115     */

116    private String JavaDoc storeDirName = DEFAULT_STORE_DIRECTORY_NAME;
117    
118    /** The base directory where bean state will be stored. */
119    private File JavaDoc storeDir;
120
121    /** A reference to the ID field for the entiy bean. */
122    private Field JavaDoc idField;
123
124    /** Optional isModified method used by storeEntity. */
125    private Method JavaDoc isModified;
126    
127    /**
128     * Saves a reference to the {@link EntityContainer} for
129     * its bean type.
130     *
131     * @throws ClassCastException Container is not a EntityContainer.
132     */

133    public void setContainer(final Container c)
134    {
135       con = (EntityContainer)c;
136    }
137
138    //
139
// jason: these properties are intended to be used when plugins/interceptors
140
// can take configuration values (need to update xml schema and processors).
141
//
142

143    /**
144     * Set the sub-directory name under the server data directory
145     * where entity data will be stored.
146     *
147     * <p>
148     * This value will be appened to the value of
149     * <tt><em>jboss-server-data-dir</em></tt>.
150     *
151     * <p>
152     * This value is only used during creation and will not dynamically
153     * change the store directory when set after the create step has finished.
154     *
155     * @jmx:managed-attribute
156     *
157     * @param dirName A sub-directory name.
158     */

159    public void setStoreDirectoryName(final String JavaDoc dirName)
160    {
161       this.storeDirName = dirName;
162    }
163
164    /**
165     * Get the sub-directory name under the server data directory
166     * where entity data is stored.
167     *
168     * @jmx:managed-attibute
169     *
170     * @see #setStoreDirectoryName
171     *
172     * @return A sub-directory name.
173     */

174    public String JavaDoc getStoreDirectoryName()
175    {
176       return storeDirName;
177    }
178
179    /**
180     * Returns the directory used to store entity state files.
181     *
182     * @jmx:managed-attibute
183     *
184     * @return The directory used to store entity state files.
185     */

186    public File JavaDoc getStoreDirectory()
187    {
188       return storeDir;
189    }
190    
191    protected void createService() throws Exception JavaDoc
192    {
193       // Initialize the dataStore
194

195       String JavaDoc ejbName = con.getBeanMetaData().getEjbName();
196
197       // Get the system data directory
198
File JavaDoc dir = ServerConfigLocator.locate().getServerDataDir();
199
200       //
201
// jason: may have to use a generated token from container config
202
// to determine a unique name for this config for the given
203
// entity name. it must persist through restarts though...
204
//
205

206       // Setup the reference to the entity data store directory
207
dir = new File JavaDoc(dir, storeDirName);
208       dir = new File JavaDoc(dir, ejbName);
209       storeDir = dir;
210       
211       log.debug("Storing entity state for '" + ejbName + "' in: " + storeDir);
212
213       // if the directory does not exist then try to create it
214
if (!storeDir.exists()) {
215          if (!storeDir.mkdirs()) {
216             throw new IOException JavaDoc("Failed to create directory: " + storeDir);
217          }
218       }
219       
220       // make sure we have a directory
221
if (!storeDir.isDirectory()) {
222          throw new IOException JavaDoc("File exists where directory expected: " + storeDir);
223       }
224
225       // make sure we can read and write to it
226
if (!storeDir.canWrite() || !storeDir.canRead()) {
227          throw new IOException JavaDoc("Directory must be readable and writable: " + storeDir);
228       }
229
230       // Get the ID field
231
idField = con.getBeanClass().getField("id");
232       log.debug("Using id field: " + idField);
233
234       // Lookup the isModified method if it exists
235
try
236       {
237          isModified = con.getBeanClass().getMethod("isModified", new Class JavaDoc[0]);
238          if (!isModified.getReturnType().equals(Boolean.TYPE)) {
239             isModified = null; // Has to have "boolean" as return type!
240
log.warn("Found isModified method, but return type is not boolean; ignoring");
241          }
242          else {
243             log.debug("Using isModified method: " + isModified);
244          }
245       }
246       catch (NoSuchMethodException JavaDoc ignored) {}
247    }
248
249    /**
250     * Try to remove the store directory, if we can't then ignore.
251     */

252    protected void destroyService() throws Exception JavaDoc
253    {
254       storeDir.delete();
255    }
256    
257    public Object JavaDoc createBeanClassInstance() throws Exception JavaDoc
258    {
259       return con.getBeanClass().newInstance();
260    }
261
262    /**
263     * Reset all attributes to default value
264     *
265     * <p>
266     * The EJB 1.1 specification is not entirely clear about this,
267     * the EJB 2.0 spec is, see page 169.
268     * Robustness is more important than raw speed for most server
269     * applications, and not resetting atrribute values result in
270     * *very* weird errors (old states re-appear in different instances and the
271     * developer thinks he's on drugs).
272     */

273    public void initEntity(final EntityEnterpriseContext ctx)
274    {
275       // first get cmp metadata of this entity
276
Object JavaDoc instance = ctx.getInstance();
277       Class JavaDoc ejbClass = instance.getClass();
278       Field JavaDoc cmpField;
279       Class JavaDoc cmpFieldType;
280
281       EntityMetaData metaData = (EntityMetaData)con.getBeanMetaData();
282       Iterator JavaDoc i = metaData.getCMPFields();
283
284       while (i.hasNext())
285       {
286          // get the field declaration
287
try
288          {
289             cmpField = ejbClass.getField((String JavaDoc)i.next());
290             cmpFieldType = cmpField.getType();
291             // find the type of the field and reset it
292
// to the default value
293
if (cmpFieldType.equals(boolean.class))
294             {
295                cmpField.setBoolean(instance,false);
296             }
297             else if (cmpFieldType.equals(byte.class))
298             {
299                cmpField.setByte(instance,(byte)0);
300             }
301             else if (cmpFieldType.equals(int.class))
302             {
303                cmpField.setInt(instance,0);
304             }
305             else if (cmpFieldType.equals(long.class))
306             {
307                cmpField.setLong(instance,0L);
308             }
309             else if (cmpFieldType.equals(short.class))
310             {
311                cmpField.setShort(instance,(short)0);
312             }
313             else if (cmpFieldType.equals(char.class))
314             {
315                cmpField.setChar(instance,'\u0000');
316             }
317             else if (cmpFieldType.equals(double.class))
318             {
319                cmpField.setDouble(instance,0d);
320             }
321             else if (cmpFieldType.equals(float.class))
322             {
323                cmpField.setFloat(instance,0f);
324             }
325             else
326             {
327                cmpField.set(instance,null);
328             }
329          }
330          catch (NoSuchFieldException JavaDoc e)
331          {
332             // will be here with dependant value object's private attributes
333
// should not be a problem
334
}
335          catch (Exception JavaDoc e)
336          {
337             throw new EJBException JavaDoc(e);
338          }
339       }
340    }
341
342    public Object JavaDoc createEntity(final Method JavaDoc m,
343                               final Object JavaDoc[] args,
344                               final EntityEnterpriseContext ctx)
345       throws Exception JavaDoc
346    {
347       try {
348          Object JavaDoc id = idField.get(ctx.getInstance());
349          
350          // Check exist
351
if (getFile(id).exists())
352             throw new DuplicateKeyException JavaDoc("Already exists: "+id);
353          
354          // Store to file
355
storeEntity(id, ctx.getInstance());
356          
357          return id;
358       }
359       catch (IllegalAccessException JavaDoc e)
360       {
361          throw new CreateException JavaDoc("Could not create entity: "+e);
362       }
363    }
364
365    public Object JavaDoc postCreateEntity(final Method JavaDoc m,
366                                   final Object JavaDoc[] args,
367                                   final EntityEnterpriseContext ctx)
368       throws Exception JavaDoc
369    {
370       return null;
371    }
372
373    public Object JavaDoc findEntity(final Method JavaDoc finderMethod,
374                             final Object JavaDoc[] args,
375                             final EntityEnterpriseContext ctx,
376                             GenericEntityObjectFactory factory)
377       throws FinderException JavaDoc
378    {
379       if (finderMethod.getName().equals("findByPrimaryKey"))
380       {
381          if (!getFile(args[0]).exists())
382             throw new FinderException JavaDoc(args[0]+" does not exist");
383             
384          return factory.getEntityEJBObject(args[0]);
385       }
386
387       return null;
388    }
389      
390    public Collection JavaDoc findEntities(final Method JavaDoc finderMethod,
391                                   final Object JavaDoc[] args,
392                                   final EntityEnterpriseContext ctx,
393                                   GenericEntityObjectFactory factory)
394    {
395       if (finderMethod.getName().equals("findAll"))
396       {
397          String JavaDoc[] files = storeDir.list(new FilenameSuffixFilter(".ser"));
398          ArrayList JavaDoc result = new ArrayList JavaDoc(files.length);
399          for (int i = 0; i < files.length; i++) {
400             final String JavaDoc key = files[i].substring(0,files[i].length()-4);
401             result.add(factory.getEntityEJBObject(key));
402          }
403          
404          return result;
405       }
406       else
407       {
408          // we only support find all
409
return Collections.EMPTY_LIST;
410       }
411    }
412
413    /**
414     * Non-operation.
415     */

416    public void activateEntity(final EntityEnterpriseContext ctx)
417    {
418       // Nothing to do
419
}
420    
421    public void loadEntity(final EntityEnterpriseContext ctx)
422    {
423       try
424       {
425          Object JavaDoc obj = ctx.getInstance();
426          
427          // Read fields
428
ObjectInputStream JavaDoc in = new CMPObjectInputStream
429             (new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(getFile(ctx.getId()))));
430
431          try {
432             Field JavaDoc[] f = obj.getClass().getFields();
433             for (int i = 0; i < f.length; i++)
434             {
435                f[i].set(obj, in.readObject());
436             }
437          }
438          finally {
439             in.close();
440          }
441       }
442       catch (Exception JavaDoc e)
443       {
444          throw new EJBException JavaDoc("Load failed", e);
445       }
446    }
447       
448    private void storeEntity(Object JavaDoc id, Object JavaDoc obj)
449    {
450       try
451       {
452          // Store fields
453
ObjectOutputStream JavaDoc out = new CMPObjectOutputStream
454             (new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(getFile(id))));
455          
456          try {
457             Field JavaDoc[] f = obj.getClass().getFields();
458             for (int i = 0; i < f.length; i++)
459             {
460                out.writeObject(f[i].get(obj));
461             }
462          }
463          finally {
464             out.close();
465          }
466       }
467       catch (Exception JavaDoc e)
468       {
469          throw new EJBException JavaDoc("Store failed", e);
470       }
471    }
472
473    public boolean isStoreRequired(final EntityEnterpriseContext ctx) throws Exception JavaDoc
474    {
475       if (isModified == null)
476       {
477          return true;
478       }
479
480       Boolean JavaDoc modified = (Boolean JavaDoc) isModified.invoke(ctx.getInstance(), new Object JavaDoc[0]);
481       return modified.booleanValue();
482    }
483
484    public boolean isModified(EntityEnterpriseContext ctx) throws Exception JavaDoc
485    {
486       return isStoreRequired(ctx);
487    }
488
489    public void storeEntity(final EntityEnterpriseContext ctx)
490    {
491       storeEntity(ctx.getId(), ctx.getInstance());
492    }
493
494    /**
495     * Non-operation.
496     */

497    public void passivateEntity(final EntityEnterpriseContext ctx)
498    {
499       // This plugin doesn't do anything specific
500
}
501       
502    public void removeEntity(final EntityEnterpriseContext ctx)
503       throws RemoveException JavaDoc
504    {
505       // Remove file
506
File JavaDoc file = getFile(ctx.getId());
507       
508       if (!file.delete()) {
509          throw new RemoveException JavaDoc("Could not remove file: " + file);
510       }
511    }
512    
513    protected File JavaDoc getFile(final Object JavaDoc id)
514    {
515       return new File JavaDoc(storeDir, String.valueOf(id) + ".ser");
516    }
517     
518    // Inner classes -------------------------------------------------
519

520    static class CMPObjectOutputStream
521       extends ObjectOutputStream JavaDoc
522    {
523       public CMPObjectOutputStream(final OutputStream JavaDoc out)
524          throws IOException JavaDoc
525       {
526          super(out);
527          enableReplaceObject(true);
528       }
529       
530       protected Object JavaDoc replaceObject(final Object JavaDoc obj)
531          throws IOException JavaDoc
532       {
533          if (obj instanceof EJBObject JavaDoc)
534             return ((EJBObject JavaDoc)obj).getHandle();
535             
536          return obj;
537       }
538    }
539    
540    static class CMPObjectInputStream
541       extends ObjectInputStream JavaDoc
542    {
543       public CMPObjectInputStream(final InputStream JavaDoc in)
544          throws IOException JavaDoc
545       {
546          super(in);
547          enableResolveObject(true);
548       }
549       
550       protected Object JavaDoc resolveObject(final Object JavaDoc obj)
551          throws IOException JavaDoc
552       {
553          if (obj instanceof Handle JavaDoc)
554             return ((Handle JavaDoc)obj).getEJBObject();
555             
556          return obj;
557       }
558    }
559 }
560
561
Popular Tags