Recipe21.4.Managing Application Properties


Recipe 21.4. Managing Application Properties

Problem

You want to manage your applications configuration properties transparently to your applications classes.

Solution

Create an aspect that loads, supplies, monitors, and stores your applications properties.

Discussion

Java application properties are traditionally loaded from and managed by a singleton class. An example of this is the System.getProperty() method, which returns a property supplied to the application from the command line with the -D option.

Unfortunately, singletons tend to be a brittle solution resulting in many areas of your application depending on the interface to the singleton. If the singleton's interface were to change, it is likely that the many dependent areas of your application would have to change to incorporate the new interface. This is the sort of cross-cutting concern that aspects can solve.

In traditional approaches to property management, the singleton property manager is a passive participant in your application and responds to requests for property information from the various parts of your application. The property manager doesn't have any knowledge of what is done with the property information it provides, or where it goes, and is dependent on notifications when any property is updated.

With an aspect-oriented approach to system properties, the perspective is switched around. The mechanisms provided by AspectJ allow you to design your property manager so it will actively apply the properties to those areas of your application where they are needed. All of the information about where a property is deployed is contained within the aspect, so if new properties are required then only the aspect will change.

The property managing aspect decouples the rest of the application from any considerations about how a property is loaded, stored, and supplied. You are no longer tied to a simplistic name/value form of interface to properties because that interface no longer exists. The aspect loads the properties, applies them where they are needed, and stores them away when the application closes.

One final advantage to managing properties using aspects is that, because a property management aspect is likely to be a privileged aspect to set the variables in the application that correspond to the properties that it manages, it can monitor any changes to those variables if necessary to reflect those changes back into its properties. This means that the aspect loads, supplies, and stores the properties and can monitor the properties for changes removing any need for the application to notify the properties manager when a property has changed.

Example 21-10 shows a property manager aspect for a simple example application.

Example 21-10. Loading, applying, monitoring, and storing an application's properties usingaspects
package com.oreilly.aspectjcookbook; import java.util.Properties; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; public privileged aspect MyApplicationProperties  {    // Property names        private static final String MYCLASS_PROPERTY_NAME =        "com.oreilly.aspectjcookbook.MyClass.property";        private static final String MAINAPPLICATION_PROPERTY_NAME =        "com.oreilly.aspectjcookbook.MainApplication.property";    // Default property values        private static final int DEFAULT_MAINAPPLICATION_PROPERTY = 1;        private static final String DEFAULT_MYCLASS_PROPERTY =     "Property Initialized:";        // Property Storage    private static final String PROPERTY_FILE_SYSTEM_PROPERTY = "props";        private static final String DEFAULT_PROPERTIES_FILENAME =        "myapplication.properties";        Properties applicationProperties = new Properties( );        File propertiesFile;        // Load Properties        public MyApplicationProperties( )    {       try       {                   String propertyFilename = System.getProperty          (PROPERTY_FILE_SYSTEM_PROPERTY);          if (propertyFilename != null)          {             propertiesFile = new File(propertyFilename);          }          else          {             propertiesFile = new File(DEFAULT_PROPERTIES_FILENAME);          }          FileInputStream inputStream = new FileInputStream(propertiesFile);          applicationProperties.load(inputStream);          inputStream.close( );       }       catch (Exception e)       {          // Just using default properties instead.          System.err.println("Unable to load properties file,           reverting to default values");       }           }        // Supply Properties        public pointcut mainApplicationInitialization( ) :        staticinitialization(MainApplication);        after( ) : mainApplicationInitialization( )    {       try       {          int mainApplicationProperty =              new Integer(                applicationProperties.getProperty(                   MAINAPPLICATION_PROPERTY_NAME)).intValue( );                    MainApplication.property = mainApplicationProperty;       }       catch (Exception e)       {          MainApplication.property = DEFAULT_MAINAPPLICATION_PROPERTY;          applicationProperties.setProperty(            MAINAPPLICATION_PROPERTY_NAME,             new Integer(DEFAULT_MAINAPPLICATION_PROPERTY).toString( ));        }    }        public pointcut myClassObjectCreation(MyClass myObject) :        execution(public MyClass.new(..)) &&        this(myObject);        before(MyClass myObject) : myClassObjectCreation(myObject)    {       String myClassProperty = applicationProperties.          getProperty(MYCLASS_PROPERTY_NAME);       if (myClassProperty != null)       {          myObject.property = myClassProperty;       }       else       {          myObject.property = DEFAULT_MYCLASS_PROPERTY;          applicationProperties.setProperty(             MYCLASS_PROPERTY_NAME, DEFAULT_MYCLASS_PROPERTY);       }    }        // Monitoring properties        public pointcut monitorMainApplicationProperty(int newValue) :        set(int MainApplication.property) &&        args(newValue);        after(int newValue) : monitorMainApplicationProperty(newValue) &&        !within(MyApplicationProperties)    {             applicationProperties.setProperty(          MAINAPPLICATION_PROPERTY_NAME,           new Integer(newValue).toString( ));       }            public pointcut monitorMyClassProperty(String newValue) :        set(String MyClass.property) &&        args(newValue);        after(String newValue) : monitorMyClassProperty(newValue) &&       !within(MyApplicationProperties)    {             applicationProperties.setProperty(MYCLASS_PROPERTY_NAME, newValue);    }        // Store properties on application close        class ShutdownMonitor implements Runnable    {       public ShutdownMonitor( )       {          Thread shutdownThread = new Thread(this);          Runtime.getRuntime( ).addShutdownHook(shutdownThread);       }       public void run( )       {          try          {             FileOutputStream outputStream = new FileOutputStream(                                                 propertiesFile);             applicationProperties.store(             outputStream);             outputStream.close( );          }          catch (Exception e)          {             System.err.println(                "Unable to save properties file,                  will use default on next run");          }       }    }        private ShutdownMonitor shutdownMonitor = new ShutdownMonitor( ); }

First, the MyApplicationProperties aspect needs to be declared as privileged because it is going to affect the internal private areas of the application's class to apply and monitor the property values to the areas where they are needed.

The names of the two application properties are then declared as constants. In this case, two properties are being managed, an int in the com.oreilly.aspectjcookbook.MainApplication class and a String in the com.oreilly.aspectjcookbook.MyClass class. Some default values for these two properties will be defined in case any problems occur when loading the properties.

The properties are loaded from a file and stored as java.util.Properties within the aspect as specified by the java.util.File attribute, propertiesFile, and applicationProperties attribute. Aspects in AspectJ are initialized before the class containing the public void main(String[]) method entry point for a Java application, so the MyApplicationProperties constructor can load the applications properties from the specified file and then apply those properties before the application starts running.

The mainApplicationInitialization( ) and myClassObjectCreation() pointcuts capture when the classes that need the properties are initialized so as to apply the properties before instances of the classes access those properties. The monitorMainApplicationProperty() and monitorMyClassProperty() pointcuts then watch the properties in the classes that need them to detect when any class changes the properties' value. If a change occurs, the after( ) advice will be triggered and the master value of the property updated. Other objects that use the property are not notified when the master value is changed because this is not how properties traditionally work. If one object changes a properties value, another object will not be informed of that change. You could implement notification of all dependent objects when a property value changes by using the Observer pattern from Recipe 15.1.

Finally, to handle storage of the applications properties when the application closes, the ShutdownMonitor class is created and instantiated as an attribute of the aspect. The ShutdownMonitor class registers itself as a shutdown hook in its constructor, and when its run( ) method is called as the application closes, the ShutdownMonitor stores the applications properties back to the indicated file.

See Also

The execution(Signature) pointcut is described in Recipe 4.4; the set(Signature) pointcut is examined in Recipe Recipe 8.3; the staticinitialization(TypePattern) pointcut is described in Recipe 7.5; the within(TypePattern) pointcut is described in Recipe 9.1; the this(TypePattern | Identifier) pointcut is explained in Recipe 11.1; The args([TypePatterns || Identifiers]) pointcut is covered in Recipe 11.3; the AND (&&) operator and the OR (||) operator are described in Recipes 12.2 and 12.3 respectively; the before( ) form of advice is explained in Recipe 13.3; the after( ) form of advice is explained in Recipe 13.5; Recipe 19.1 shows how to implement the Observer object-oriented design pattern using aspects.



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