Recipe20.3.Adding Persistence to a Class


Recipe 20.3. Adding Persistence to a Class

Problem

You want to add persistence to a class so it can be saved and restored.

Solution

Apply the Director aspect-oriented design pattern and create an abstract aspect that defines the roles and behavior required to manage object persistence, as shown in Example 20-6.

This abstract aspect can be extended into specialized subaspects that will implement an appropriate persistence mechanism for each collection of persisted objects within your application.

Example 20-6. Defining the generic roles and behavior of persistence in an abstract aspect
public abstract aspect PersistenceAspect  {    public interface ObjectStore    {       public void persist( );       public void restore( );    }        protected abstract pointcut restoreStorage(ObjectStore store);        after(ObjectStore store) : restoreStorage(store)    {       store.restore( );    }        protected abstract pointcut persistStorage(ObjectStore store);        after(ObjectStore store) : persistStorage(store)    {         store.persist( );    } }

Discussion

The abstract PersistenceAspect aspect defines the ObjectStore role as an interface that can be applied to any class that will manage the persistence of a collection of objects. The restoreStorage(ObjectStore) and persistStorage(ObjectStore) abstract pointcuts are implemented by specialized subaspects to trigger the corresponding after( ) advice blocks that will restore or persist the specified ObjectStore.

The restore( ) and persist( ) methods specified in the ObjectStore interface are implemented according to the specific persistence strategy for a collection of objects within your application so you can vary the persistence strategy on a per ObjectStore basis.

Example 20-7 shows how the abstract PersistenceAspect can be implemented to provide persistence to an Employee class managed as a collection within the EmployeeCollection class.

Example 20-7. Applying the abstract Persistence aspect to the Employee and EmployeeCollection classes
public aspect EmployeePersistenceAspect extends PersistenceAspect {    declare parents : EmployeeCollection implements ObjectStore;    protected pointcut restoreStorage(ObjectStore store) :        execution(EmployeeCollection.new(..)) &&        target(store);        protected pointcut persistStorage(ObjectStore store) :        call(* java.util.List.add(..)) &&        target(EmployeeCollection) &&       target(store);    declare parents : Employee extends Serializable;        private File EmployeeCollection.employeesFile = new File("employees.ser");        public void EmployeeCollection.persist( )    {       try       {          ObjectOutput out = new ObjectOutputStream(             new FileOutputStream(this.employeesFile));          Object[] objectsToStore = this.toArray( );          out.writeObject(objectsToStore);          out.flush( );          out.close( );       }       catch (Exception e)       {          System.err.println("Couldn't store employees to " +                              this.employeesFile);       }    }        public void EmployeeCollection.restore( )    {       if (this.employeesFile.exists( ) && this.employeesFile.canRead( ))       {          try          {             ObjectInput input = new ObjectInputStream(                new FileInputStream(this.employeesFile));                          Object[] objectsToRestore = (Object[]) input.readObject( );             for(int x = 0; x < objectsToRestore.length; x++)             {                this.add(objectsToRestore[x]);             }                          input.close( );          }          catch (Exception e)          {             System.err.println(                "Couldn't restore employees due to a corrupt " +                  this.employeesFile +                 " file");             e.printStackTrace( );          }       }    } }

The EmployeePersistenceAspect applies the ObjectStore interface to the EmployeeCollection class. The restoreStorage(ObjectStore) aspect is then implemented to capture when the EmployeeCollection is constructed and uses the target(TypePattern || Identifier) pointcut to expose the EmployeeCollection as the ObjectStore to be restored.

The persistStorage(ObjectStore) pointcut is implemented to capture whenever the EmployeeCollection is changed and to persist the contents of the ObjectStore at this point. The EmployeeCollection is a specialization of java.util.ArrayList; to avoid weaving directly into the Java standard libraries, a call(Signature) pointcut is used to capture when the add(..) method is called on java.util.List.

However, the call(Signature) pointcut definition on its own is too general, so you must restrict the join points captured by the persistStorage(ObjectStore) pointcut to only capturing when add(..) is called on an EmployeeCollection. To apply this restriction, the first target(TypePattern || Identifier) pointcut uses a TypePattern to specify you are only interested in join points where the target of the join point is an EmployeeCollection class. The second target(TypePattern || Identifier) pointcut uses an Identifier to pass the current ObjectStore to the advice block as the persistStorage(ObjectStore) pointcut's single parameter.

Finally, a straightforward object serialization persistence strategy is applied to the EmployeeCollection. Each object within the EmployeeCollection is an instance of the Employee class, and this class is extended to implement the Serializable interface so it can be subjected to standard Java object serialization techniques. The serialized Employee objects are to be stored in a file; therefore, this file information is added to the EmployeeCollection class in the form of an employeesFile attribute.

To complete the picture, the persist( ) and restore( ) method implementations are added to the EmployeeCollection class so it can meet the behavior required of the ObjectStore interface. These methods execute the serialization and restoration of the Employee objects to and from the file indicated by the employeesFile attribute.

In Example 20-7, the EmployeeCollection was persisted every time it was changed. This could be overkill for your application, but you can change the join points captured by the persistStorage(ObjectStore) pointcut to persist an ObjectStore more applicable to your own application.

Example 20-8 shows part of an alternative implementation of the abstract PersistenceAspect that only persists its corresponding ObjectStore when the application is shutting down.

Example 20-8. Declaring that an ObjectStore should be persisted when an application is shut down
public privileged aspect AccountPersistenceAspect extends PersistenceAspect {    declare parents : MainApplication implements ObjectStore, Runnable;    protected pointcut restoreStorage(ObjectStore store) :        execution(MainApplication.new(..)) &&        target(store);        protected pointcut persistStorage(ObjectStore store) :        execution(public void MainApplication.run( )) &&        this(store);        declare parents : Account extends Serializable;    private File MainApplication.accountsFile = new File("accounts.ser");        public void MainApplication.persist( )    {       // ... Code to persist the Accounts ...     }        public void MainApplication.restore( )    {       // ... Code to restore the Accounts ...    }         after(MainApplication mainApplication) :        restoreStorage(ObjectStore) &&        target(mainApplication)    {       // Register a shutdown hook       Thread shutdownThread = new Thread(mainApplication);       Runtime.getRuntime( ).addShutdownHook(shutdownThread);    }    public void MainApplication.run( )    {       // Do nothing, merely provides the trigger that the shutdown hook has been       // executed so as to persist the store on shutdown.    } }

The AccountPersistenceAspect declares that the MainApplication class is to fulfill the ObjectStore interface and adds the Runnable interface as well. The Runnable interface, from java.lang, is used in Java as the interface to all objects that can be executed in their own thread. The public void run( ) method is required by the Runnable interface to provide the entry point for the Java Virtual Machine (JVM) to execute the class in its own thread.

In this example, the persistStorage(ObjectStore) pointcut is amended to capture the execution of the Runnable enforced public void run() method on the MyApplication class. The AccountPersistenceAspect then adds the necessary run( ) method implementation to the MainApplication class to meet the needs of the Runnable interface, but this merely provides a marker and does not need any implementation.

The addition of the Runnable interface and the stub run( ) method on the MainApplication class means that the MainApplication can be registered with the JVM as a shutdown hook so the MainApplicatin.run() method will be called when the overall application finishes normally. Registering the MainApplication class as a shutdown hook is completed by the around(MainApplication) advice block executed when an ObjectStore that is a MainApplication class is restored.

By using the MainApplication as a shutdown hook, the persistStorage(ObjectStore) pointcut will trigger the persisting of the MainApplication's objects when the shutdown hook is triggered. Therefore, the collection of Account classes stored within the MainApplication will be persisted once the application is shut down cleanly.

See Also

The call(Signature) pointcut is discussed in Recipe 4.1; the execution(Signature) pointcut is explained in Recipe 4.4; the this(TypePattern | Identifier) pointcut is explained in Recipe 9.1; the target(TypePattern | Identifier) pointcut is described in Recipe 9.2; the after( ) form of advice is described in Recipe 13.5; specializing abstract aspects is discussed in detail in Recipe 15.2; extending an existing class using the declare parents statement is explained in Recipe Recipe 16.2; the Director aspect-oriented design pattern is described in Recipe 23.2.



AspectJ Cookbook
Aspectj Cookbook
ISBN: 0596006543
EAN: 2147483647
Year: 2006
Pages: 203
Authors: Russ Miles

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net