>
How to create and integrate extra configuration properties

You may sometimes need to initialize other objects for your application, Spine allows you to pass additional initialization properties to your application but you must implement an instance of SpinePlugin and ensure that you handle the initialization of your objects from this implementation. Spine will then use the SpinePlugin instance to perform initialization.Eg if you wish to read another XML file at initialization, follow the steps below:

  1. Create a class which reads the XML file and transposes it into a composite object eg a Document object.

  2. Create a class which implements the SpinePlugin interface.

  3. Add an entry to the spine-init.xml to reflect your new SpinePlugin. This must be listed as a plugin tag in the spine-init.xml. Eg:

    <plugins>
            <plugin key="xmlReaderPlugin">
                <pluginName>                 foo.bar.plugin.XMLReaderPlugin             </pluginName>
                <property name="fileName"   value="/home/joebloggs/myXmlFile.xml"/>
               
    <property name="anotherProperty"    value="PROPERTY_VALUE "/>
            </plugin>
        </plugins>

  4. Handle your calls to your XML reader from the SpinePlugin.process(Map):void method. The Map sent from the ApplicationConfigurator becomes available to the SpinePlugin and it contains all the parameters defined by the user for the plugin in the spine-init.xml file. The plugin is initialized after the ApplicationConfigurator will have run its initialization processes.

  5. If you wish to access your plugin after initialization or from one of your processes, you can can access the plugin by calling PluginServiceLocator.getPlugin(pluginName:String):SpinePlugin.
Further information on creating plugins is available in the next section.

 
>
How to create and Use a SpinePlugin

In order to create a SpinePlugin, you will need to create a java class which either extends AbstractSpinePlugin or implements the SpinePlugin interface.As AbstractSpinePlugin already defines 2 of the 3 methods you will need to implement, we recommend you extend this object.

To demonstrate the creation and use of a SpinePlugin, we will use the example in the previous section i.e we need a plugin which effective reads data from an XML file, whose properties will be used to create some Java objects which we intend to use in certain parts of our application. The steps enumerated below describes how we could go about creating a SpinePlugin to store Groups and Applications registered with the framework:

  1. Create your plugin class and define the class and its key in the spine-init.xml file. For our plugin class we define:

    public class RolePlugin extends AbstractSpinePlugin {


               private Map membersMap = null;

               public RolePlugin(){
                          membersMap = Collections.synchronizedMap(new HashMap());
                }
    }

    For our plugin initialization configuration, we define:

    <plugins>
            <plugin key="rolePlugin">
                <pluginName>                 foo.bar.plugin.RolePlugin             </pluginName>
                <property name="fileName"  value="/path-to/spine-distro/groups.xml"  />
          </plugin>
    </plugins>

  2. Read the previous section and follow the instructions to see how your plugin will be initiated and persisted in the framework.

  3. Create an XML file to store your data, for this example we will be using the default groups.xml file shipped with this distribution.

  4. Create a class to read and store the contents of the XML file in object format. Our example uses the class RoleConfigHelper which reads the XML file based on the filename sent to it and saves it as a map of Member objects.

  5. Implement the process method of the plugin you have just created, for this example we call the plugin, RolePlugin.As we also need a means of accessing and querying the contents of the map of Members, we add some other delegate methods i.e:

    • getAllGroups():Map
    • Used to retrieve the map of Groups And Applications
    • getGroupById(id:String):Group
    • Used to retrieve a Group whose id is known to the system.
    • getGroupByName(userName:String):Group
    • Used to retrieve a Group whose userName is known to the system.
    These method implementations can be seen in the code snippet shown below:

    /*
    * Gets the map of saved Groups and Applications in this plugin
    *
    */
    public  Map  getAllGroups(){
               return this.membersMap;
    }

    /*
    * Gets the  Group or Application whose id is specified by id in the map of Groups and Applications
    *
    */
    public Group getGroupById(String id){
              return (Group) this.membersMap.get(id);
    }

    /*
    * Gets the  Group or Application whose userName is specified by userName in the map of Groups  and Applications
    *
    */
    public Group getGroupByName(String userName){
              Group someGroup = null;
              Iterator it = this.membersMap.keySet().iterator();
              while(it.hasNext()){
                       String key = (String) it.next();
                       someGroup = membersMap.get(key);
                       if(someGroup.getUserName().equals(userName){
                               break;
                       }
                       else continue;
               }

    }

  6. In the process method of the plugin, we simply extract the parameters we specified in the spine-init.xml file, pass the extracted file name to the instance of RoleConfigHelper, and save the returned Map as an instance variable in our plugin as shown below:

    public void process(Map map){
               String fileName = (String) map.get("fileName");
               RoleConfigHelper rch = new RoleConfigHelper();
               this.membersMap = rch.createConfig(fileName);
    }

  7. To retrieve the plugin at anytime during the duration of this application, we call PluginServiceLocator.getPlugin(key:String):SpinePlugin method and request the plugin by the key by which it is known to the system. i.e:

    PluginServiceLocator psl =  PluginServiceLocator.getInstance();
    RolePlugin rolePlug = psl.getPlugin("role");


As you can see from the example above, it is easy to add your plugins to the framework and as the framework maintains synchronized reference to all the registered plugins, we can easily retrieve the objects we have persisted in our plugins at any time the application is running.

 
>
How to create and initialize a DataSourceBuilder implementation

The Spine framework does not provide reference DataSource connector implementations for all defined databases. This connector, called a DataSourceBuilder performs the function of instantiating a DataSource and making it available to the framework and the user application.
It does however provide a default DataSourceBuilder which is suitable for some databases but not for all databases.

In order to create a DataSource connector or DataSourceBuilder, you will need to perform the following actions:

  1. Ensure your datasource class libraries are in the classpath of the application and framework.

  2. Specify your datasource initialization and access parameters in the spine-init.xml file. This will usually be in the dataSource tag e.g

        <dataSources>
            <dataSource key="Mysql" default="true">
                <dataSourceBuilder>
                    com.zphinx.spine.start.helpers.impl.MysqlDataSourceBuilder
                </dataSourceBuilder>
                <sourceClass>
                    com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
                </sourceClass>
                <property name="userName"   value="testuser"/>
               
    <property name="password"   value="minerva"/>
                <property name="url"   value="jdbc:mysql://192.168.1.5:3306/securesite"/>
                <property name="port"   value="3306"/>
                <property name="databaseName"   value="securesite"/>
            </dataSource>
    </dataSources>


  3. Create a class which implements the interface DataSourceBuilder of which only method requires a concrete implementation. This method, the DataSourceBuilder.createDataSource(m:Map):DataSource will receive a map of all the properties specified in the initialization file ( spine-init.xml). E.g.


    public class MysqlDataSourceBuilder implements DataSourceBuilder {
         /**
         * Public Constructor
         */
        public MysqlDataSourceBuilder() {
            super();
        }
       


    }

  4. In the DataSourceBuilder.createDataSource(m:Map):DataSource method of your DataSourceBuilder implementation, retrieve the initialization properties you have set in the spine-init.xml as shown below:

    /*
    * (non-Javadoc)
    *
    * @see com.zphinx.spine.start.helpers.DataSourceBuilder#createDataSource(java.util.Map)

    */
    public DataSource createDataSource(Map map) {
            Iterator it = map.keySet().iterator();

            String userName = null;
            String password = null;
            String databaseName = null;
            String url = null;
            String sourceClass = null;
            String server = null;
            int port = 3306;
            int loginTimeout = 15;
            String profileSql = null;
            while (it.hasNext()){
                String key = (String) it.next();
                String value = (String) map.get(key);

                if(key.equalsIgnoreCase(USER_NAME)){
                    userName =  value;
                }
                else if(key.equalsIgnoreCase(PASSWORD)){
                    password =  value;
                }
                else if(key.equalsIgnoreCase(DATABASE_NAME)){
                    databaseName =  value;
                }
                else if(key.equalsIgnoreCase(URL)){
                    url = value;
                }
                else if(key.equalsIgnoreCase(PORT)){
                    try{
                        port = Integer.parseInt( value);
                    }
                    catch (NumberFormatException e){
                        log.debug("Unable to set port address, setting port to default: " + port);
                    }
                }
                else if(key.equalsIgnoreCase(LOGIN_TIMEOUT)){
                    try{
                        loginTimeout = Integer.parseInt( value);
                    }
                    catch (NumberFormatException e){
                        log.debug("Unable to set login timeout, setting timeout to default: " + loginTimeout);
                    }
                }
                else if(key.equalsIgnoreCase(SERVER_NAME)){
                    server =  value;
                }
                else if(key.equalsIgnoreCase(PROFILE_SQL)){
                    profileSql =  value;
                }
                else if(key.equalsIgnoreCase(SOURCE_CLASS)){
                    sourceClass =  value;
                }
           
          }
          return this.createMysqlDataSource(sourceClass, userName, password, databaseName, url, port, server, profileSql, loginTimeout);
    }



  5. After retrieving the properties from the stated Map, you should then instantiate your DataSource class and use its setter methods to set the defined properties which you retrieved from the map. E.g:

        private DataSource createMysqlDataSource(String sourceClass,
                String userName, String password, String databaseName, String url,
                int port, String server, String profileSql, int loginTimeout) {
                MysqlDataSource source = null;
                try {

                       source = (MysqlDataSource) Class.forName(sourceClass).newInstance();
                       source.setDatabaseName(databaseName);
                       source.setPort(port);
                       source.setPassword(password);
                       source.setUrl(url);
                       source.setUser(userName);
                       source.setServerName(server);
                       source.setLoginTimeout(loginTimeout);
                       source.setProfileSql(profileSql);

            }
            catch (Throwable e) {

                      e.printStackTrace();
            }
            return source;
        }


    Return the instantiated DataSource from the implementated method to conclude the DataSource creation process as shown above.

You can see from above that only one method need be implemented for us to make available a DataSource connector to the Spine framework. It is also possible to use this method to call your JNDI resources to grant the framework an instance of a pre-initiated DataSource.

 
>
Using integrated security properties in Spine

Security properties in spine are available for 2 main objects, the Member object and its sub classes, and SpineBeans and its  extensions.
SpineBeans and Member objects are entity beans which contain a security profile known as a Permission.  There are 2 kinds of Permissions, namely:

SpinePermission



SpinePermission operates similarly to the Unix access control rules system i.e resources have owners,groups and public access. An extension of the SpineBean which possesses a SpinePermission can have its permission queried by any principal which wishes to access it. This Permission also exposes flags to denote if the accessing principal has Read,Write and/or execute access.  

The steps for creating a SpineBean are described above and require that a principal's permission be used (Where the bean is not created by the system) in the creation process. This  automatically applies  the security properties of the  principal to the  SpineBean which can then be  querried at a latter time.

To enforce security at any time, the client developer must invoke one of SpinePermission.getPermit(String):boolean or  SpinePermission.checkGuard(Object):void. Please note that these two methods only check accessibility to the  stated SpineBean and not  the specific  ACL in use  relating to the specified principal.

To find out what access rights the principal has over the stated SpineBean you will need to call the methods:

The value of  the flags returned by the 3 methods above are determined by the PermissionLevel associated with the SpinePermission and are based on the unix octal representation of ACL. The PermissionLevel. can be set at any time but users are advised when using a principal to set this  PermissionLevel,  you should use the secure variant of SpinePermission, i.e SpinePermission.setPermission(PermissionLevel,SpinePermission):void.

MemberPermission

 
Both Permission types work differently, although the MemberPermission inherits from the SpinePermission, the PermissionLevel object available in the MemberPermission is not queried when determining if a principal can access a role or manage another principal, instead spine will use  the records of  the principal's roles and principals to determine access control.

For this reason, a call to Permission.getPermit(String):boolean where the Permission is a MemberPermission will not determine read,write and execute access but  will be sufficient to determine if access is permited.

If the client developer wishes to add unix type ACL rules, then the developer should set a PermissionLevel into the MemberPermission and assign values to it's properties.

 
>
How to create a Proxy extension

Creating a proxy extension requires that the client developer extend the AbstractDataProxy class and implement 2 methods, namely

AbstractDataProxy.open(Object,String,DAOInput):DataAccessObject
AbstractDataProxy.close(DataAccessObject):void

The open method allows the new DataBaseProxy to instantiate/initialize all parameters needed by the DataBaseProxy and also provides the new DataBaseProxy with 3 parameters namely:
  1. Object: Any arbitary object which can possess properties needed to drive the DataProxy, eg DataBaseProxy uses a DataSource object but FileProxy uses an array of Strings( Path,boolean expressed as a String ).

  2. String: A string representing the type of DataAccessObject to create.This will usually be the full className of a DataAccessObject.You will notice that this and the DAOInput parameter can be passed directly to the
    AbstractDataProxy.createDataAccessImpl(String,DAOInput):Object method to create the DataAccessObject  specified by the full class name.

  3. DAOInput: This is the interface which must be implemented when passing parameters to any custom DAO. It can be null but when we create a DAO and we wish it to pass parameters to it, we must wrap this parameter(s) in a DAOInput so that Spine can recognize and instantiate it for us.

The close method is used to close resources opened by the AbstractDataProxy and the DataAccessObject, any resources that you wish to  ensure are closed should be handled in this method.
 

DataAbstracts



DataAbstracts are implementation of DataAccessObjects specific to a particular DataProxy, E.G. the DatabaseAbstract is specific to DataAccessObjects which access a database. Please note you do not have to use a DataAbstract class but as several methods within the DataAccessObject interface will be recurrent, creating a DataAbstract to implement this methods is good coding sense.

If creating a new DataProxy, you should look for generic methods which can be abstracted to a DataAbstract class.You will discover that every time you need to create a DataAccessObject, all you need do is to extend your DataAbstract which can be used by your DataProxy.

Defining the new Proxy in configuration



Everytime you create a new DataProxy, you must define this proxy in the spine.xml file. This is to allow the framework instantiate and associate this DataProxy with DAOs which require the DataProxy for operation. You will normally use an XML tag which has been assigned a unique index as shown below:

<dataProxy name="com.foo.bar.NewProxy" index="11"/>

 
>
How to use localized messages and exception components

Spine messaging and Exception components require a registered java properties file to be available at initialization. Several properties files can be registered with the system as the system will usually read through all this files until it finds the key it requires. This provides multilingual/locale sensitive messages which can be rendered to your view when needed.

Messaging Components

There are three main messaging components in Spine. They are :

  • DisplayMessage
  • This object represents a single message which can be displayed to the user of the spine framework. It use a single key and an array of parametric objects for string replacement to create a locale sensitive message.
  • DisplayError
  • This DisplayMessage extension represents a single error which can be displayed to the user of the spine framework. It use a severity,a single key and an array of parametric objects for string replacement to create a locale sensitive message.
  • DisplayMessages
  • Serves as a container for all the messages generated as a result of the processing activity. It can contain several messages and errors all of which can be identified by a set of unique keys. EG one DisplayMessages can contain several errors and messages registered with key "ERRORS" and key "MESSAGES".There is no limit on the number of keys which may be registered with a single DisplayMessages object.
The user should note that only the message key is available in a DisplayMessage or a DisplayError object. This message key is parsed through the properties files immediately it is added to a DisplayMessages object.
The persisted message key is returned if an entry is not found for that key in any of the properties files defined for the framework.

Exception Components



There are 5 exceptions included in the spine  framework,namely:

  • ResourceException
  • The exception which is obtained when we fail to obtain a resource associated with the spine framework
  • SpineApplicationException
  • An application specific exception which can be thrown when processing a request using the framework
  • SpineException
  • A general exception thrown by the framework
  • SpineRuntimeException
  • A runtime exception that can be thrown by the framework
  • SpineMessageException
  • A locale sensitive exception useful for displaying exception information in a locale sensitive manner. This exception defines messages as keys which are available in  the  properties files stored in the configuration. Parametric replacement is possible because most of the methods of this exception allow an Object array to be  used as a parameter.
You may use any of the above exceptions in your code. The most useful exception is the SpineMessageException as it allows your application to display locale sensitive information obtained from a properties file.
 
>
The variants of spine request processing

Client developers can send a request to spine in 2 different ways, these are shown in the use of the ViewProcessor default types i.e

  1. DefaultViewProcessor
  2. MultiViewProcessor

DefaultViewProcessor


This is the default ViewProcessor to use when your request is a simple ViewProcessor - BusinessDelegate - DAO request .

If the request requires that  the ViewProcessor has to go back to the engine and possibly invoke another BusinessDelegate,you simply specify this BusinessDelegate in configuration and the ViewProcessor will invoke the delegate and its associated DAO.

You will need to reuse the same instance of ViewProcessor (do not create a new instance!!) otherwise extend the DefaultViewProcessor but ensure that a single instance handles your calls in a sequential manner. The order in which the ViewProcessor instance will call the BusinessDelegate will depend on the configuration of the ViewProcessor in the spine.xml file.

MultiViewProcessor


The MultiViewProcessor is specifically meant for objects which need to handle various DAOS and/or ManagedObjects within one delegate. A builder should be provided which we may use to perform various operations on the different objects. We recommend using extensions of the SpineBean as ManagedObjects specified in the configuration file spine.xml.

The MultiViewProcessor behaves just like a normal processor, but allows us to define more than one DataProxy,DAO instance for a BusinessDelegate.i.e

This configuration allows the delegate to use a DAO to process different ManagedObjects each directly linked to a DAO via configuration. It is also  possible to use a more sophisticated configuration of more than one BusinessDelegate interfacing with several DAOs as seen by the one to many relationship in the diagram above.

Again you must reuse the same instance of MultiViewProcessor(do not create a new instance!!) otherwise extend the default MultiViewProcessor but ensure that a single instance handles your calls in a sequential manner. The order in which the MultiViewProcessor instance will call a BusinessDelegate will depend on the configuration of the MultiViewProcessor in the spine.xml file.




<< Back    |     Home    |     Index    |     Forward >>

Copyright © 2008 Zphinx Software Solutions , all rights reserved