Bridgetown IoC Framework 0.9.2, API Specification

Bridgetown IoC Framework Overview API The Bridgetown IoC Framework is an implementation of a lightweight container framework.

See:
          Description

Service-Oriented Architecture
com.xenonsoft.bridgetown.soa This is the package defines the Service Oriented Architecture, defines the interfaces for service assembler factories.
com.xenonsoft.bridgetown.soa.config This is packages contains the configuration elements for a assembly factory, contexts, and their service beans.
com.xenonsoft.bridgetown.soa.impl This package contains the default core implementation for service bean factories, configuration loaders, and application assemblies.
com.xenonsoft.bridgetown.soa.spi This package specifies Service Provider Interfaces for developers who want to write assembly factories themselves, users will not normally find any these classes and definitions useful.

 

Resources & Loaders
com.xenonsoft.bridgetown.resources This packages contains resource configuration classes and strategies to a service assembly decoupled from the factory that maintains it.

 

AOP Support
com.xenonsoft.bridgetown.aop This is the package for Aspect Oriented programming interfaces and abstract classes that are part of the Brigetown IoC Framework, here you will find pointcut, joinpoint, finder, context and method interceptor interfaces.
com.xenonsoft.bridgetown.aop.helper This is the package for supporting Aspect Oriented programming with the Brigetown IoC Framework, here you will convenience class to handle before, after, and afterThrowing advice.
com.xenonsoft.bridgetown.aop.impl This is the package for Aspect Oriented Programming and it contains default implementation of the interface and abstract classes, here you will find a default method interceptor and default method pointcut based on glob expressions.
com.xenonsoft.bridgetown.aop.transaction This is the package for transaction support with the Brigetown IoC Framework using AOP, here you will find interfaces for transaction management, transaction context, session factories, and of course exceptions.
com.xenonsoft.bridgetown.aop.transaction.expresso This is the package dedicated to Bridgetown AOP transaction support for Expresso Framework
com.xenonsoft.bridgetown.aop.transaction.hibernate This is the package dedicated to Bridgetown AOP transaction support for Hibernate OR/M
com.xenonsoft.bridgetown.aop.transaction.jdbc This is the package for transaction DataSource and JDBC support with the Brigetown IoC Framework using AOP interceptors.
com.xenonsoft.bridgetown.aop.transaction.jta This is the package for Java Transaction API (or JTA) support with the Brigetown IoC Framework using AOP interceptors.
com.xenonsoft.bridgetown.aop.transaction.lumberjacque This is the package for Lumberjacque Transaction Service Manager implementation.
com.xenonsoft.bridgetown.aop.transaction.support This is the package for abstract and concrete implementations of the interfaces for certains kinds of transaction object: such as a method interceptor, data source resource controller, transaction context and object.

 

Application Support
com.xenonsoft.bridgetown.app This is the package for the application-wide assembler factory and their utility classes.

 

Examples
com.xenonsoft.bridgetown.examples.categoriser This is the package for the categoriser / processor dependency injection example.
com.xenonsoft.bridgetown.examples.dvdrental This is the package for the DVD Kiosk dependency injection example, which is based on the Martin Fowler's Dependency Injection paper.
com.xenonsoft.bridgetown.examples.services This is the package for the services examples, demonstrating setter injection and generic method injection.
com.xenonsoft.bridgetown.examples.services.impl This is the implementation package for the services examples, demonstrating setter injection and generic method injection.

 

Unit Test & Verification
com.xenonsoft.bridgetown.test This is the root package of the Bridgetown IoC Framework unit test strategies, invariably making full use of JUnit.
com.xenonsoft.bridgetown.test.aop This is the test package for assembly factory that are based on Aspect Oriented proxy bean builders.
com.xenonsoft.bridgetown.test.aop.transaction This is packages contains the unit tests, supporting beans, and interfaces to demonastrate the power and effectiveness of AOP transactions.
com.xenonsoft.bridgetown.test.ejb This is the package with a test Enterprise JavaBean classes to demonstrate the validity and plausibility of the JTA Virtual Transaction Service Manager implementation.
com.xenonsoft.bridgetown.test.research This is packages contains the research programs, beans, and interface for possible inclusion into the framework.
com.xenonsoft.bridgetown.test.servlets This is the test package for assembly factories that require the J2EE web environment, namely Servlets and JavaServer Pages.

 

Bridgetown IoC Framework Overview API

The Bridgetown IoC Framework is an implementation of a lightweight container framework. It allows Java developer to retrieve Service Beans from an assembly. The assembly creates JavaBeans, which typically have dependencies on other JavaBeans. The assembly will automatically "wire" these dependencies from an assembly configuration. In other word Bridgetown will inject dependencies into your Plain Old Java Object (POJO)

Bridgetown IoC Framework

LEGAL. Bridgetown is distributed with an Apache License 2.0 license. For terms and conditions please read the license.

ATTRIBUTIONS. The original software was written and produced by Peter A. Pilgrim for XeNoNSoFT.com Productions. http://www.xenonsoft.com/
Copyright (c) Wed Sep 01 00:44:46 BST 2004 all rights reserved.

Table of Contents

  1. Overview
  2. Assembly
  3. Dependency Injection
  4. Aspect Oriented Support

Overview

Bridgetown is a Inversion of Control framework and a lightweight container.

NB: When I am discussing tiers, I am talking about logical software and not physical deployment tiers.

The IoC container was created after developing a number of applications in Expresso Framework and Jakarta Struts . It became quite clear that the presentation tier was deeply coupled with the business tier classes. During the development of some applications introduced either singletons or some type of hand-me-down Service locator. These look up codes were continously being used at key fractures between the presentation tier and the business tier. Some locators in particular had dependencies on other services, and these were typically "wired" directly in the programming code, specifically at the point of initialisation.

Instead of developing application software with a multitude of service locators, and/or singletons, why not invert the way this configuration and instantiation of service object actually works.?

Bridgetown introduces the idea of Services Beans (JavaBeans) that are instantiated and managed by an Assembly Factory. These beans, which are managed by the assembly (the lightweight container), can have dependencies between them.

Background

Let us look at a standard singleton example: a corporate printer service. Let us examine how this will be coded:

public class PrinterService {
    private static PrinterService    theInstance
        = new PrinterService();
    
    /** Cannot instantiate the printer service */
    private Printer() { }
    
    public static PrinterService getInstance() {
        return theInstance;
    }
}

Now let us a suppose we have to use a finder service that would allow our burgeoning enterprise to use more than one network printer. Let us introduce this printer finder as a service interface.

public interface PrinterFinder {
    /** Find a printer by name */
    public Printer  findPrinter( String name );
}

Let us imagine there is a default implementation of the printer finder interface that a developer on the otherside of the world wrote just for you.

public class PrinterFinderImpl 
implements PrinterFinder
{
    /** Find a printer by name */
    public Printer  findPrinter( String name ) {
        // Do whatever!!
        return aPrinter;
    }
}

Now if we introduce the finder back into the printer service we can write applications to locate the printers by name. However, now, we have a singleton that is hard-wired directly into our printer service.

public class PrinterService {
    private static PrinterService    theInstance
        = new PrinterService();
        
    /** Cannot instantiate the printer service */
    private Printer() { }
    
    public static PrinterService getInstance() {
        return theInstance;
    }
    
    public Printer findPrinter( String name ) {
        // HARD-WIRED dependency
        PrinterFinder finder = new PrinterFinderImpl();
        Printer result = finder.findPrinter( name );
        return result;
    }
}

So how can Bridgetown container help me manage creation of services? How do I create a lightweight container? How is the container started? Patience, my dear boy, all will be revealed in due course. Please read onwards.

About Naive Dependencies

If you coded all your application services just like the printer networking locator described above, then it is easy to see that managing the dependencies between the services becomes more difficult as more services are added.

The first action should be refactoring of the services from the clients of the service. In other words, the lookup code is deeply coupled with the thing that is actually using the service. Let us move the responsibility of creation outside of the printer service singleton.

public class PrinterService {
    private static PrinterService    theInstance
        = new PrinterService();
    
    PrinterFinder finder;
    
    /** Cannot instantiate the printer service */
    private Printer() { }
    
    public static PrinterService getInstance() {
        return theInstance;
    }
    
    public void setPrinterFinder( PrinterFinder finder )
    {
        this.finder = finder;
    }
    
    public Printer findPrinter( String name ) {
        return finder.findPrinter( name );
    }
}

Assembly

With the printer service in its current state we are ready to convert it into a simply service bean.


public class PrinterService {
    PrinterFinder finder;
    
    /** Requires a default no-arguments contructor! */
    public Printer() { }
    
    public void setPrinterFinder( PrinterFinder finder )
    {
        this.finder = finder;
    }
    
    public Printer findPrinter( String name ) {
        return finder.findPrinter( name );
    }
}

Notice how this form is almost like a JavaBean (or plain old Java object also known as the infamous ubiquitous POJO). A couple of questions remain to be answered now. Exactly how does the printer service get created? How does it get the reference to the finder object? This is were you hand that responsibility to a lightweight container such as Bridgetown IoC Framework.

Within Bridgetown IoC management of services beans is controlled by Service Assembly Factories. To use an assembly simply create an instance of it. The default assembler with the Bridgetown is com.xenonsoft.bridgetown.soa.impl.ServiceAssemblerImpl. As we are trying to follow best software development practices in Java, we should alway program to interfaces, hence there is an interface for service assemblers, i.e com.xenonsoft.bridgetown.soa.IServiceAssmbler.

Here is an example of how our printer service could be coded using the Bridgetown service assembler.


import com.xenonsoft.bridge.resource.*;
import com.xenonsoft.bridge.soa.*;
import com.xenonsoft.bridge.soa.impl.*;

public class PrinterApplication {
    public static void main( String argv[] ) {
        
        // [1] Creation
        IServiceAssembler sai = new ServiceAssemblerImpl();
        // [2] Configuration
        IConfigLoader configLoader = new XMLConfigLoader();
        configLoader.setFile( new File( argv[0] ));
        sai.load( configLoader );
        // [3] Start the assembly
        sai.start();
        
        
        // [4] Retrieve a bean from the assembly by context 
        // and name identifier.
        PrinterService printerService = (PrinterService)
            sai.getName("acme", "myPrinterService" );
        Printer printer =  printerService.find(
              "\\LONUK_P10124\LP_COLOR_771" );
        
        // [5] Dispose of the assembly
        sai.stop();
    }
}

Let us now go through what is happening here in the above example code.

Configuration

As alluded to above, the service assembly requires a configuration. Normally this is supported by an XML document, as this is the default implementation. In theory any other means of loading the configuration can be invented by you or by a third party, including reading the dataset from relational database.

Pressing ahead, to the default XML configuration, Bridgetown, manages a service bean inside namespaced contextes. An assembly configuration is divided into contextes. Each context can have one or more service bean definitions. A service bean definition has a implementation class name, and an optional list of interface classes. It must have a unique identifier. A service bean definition may declare setter property injection, method injection, and/or constructor injection.

The basic form of XML assembly configuration file is:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE assembly PUBLIC
    "-//XeNoNSoFT.com Open Source//DTD Bridgetown IoC Configuration//EN"
    "service-assembly-1.0.dtd">

<assembly>
	{ a list of contextes }
</assembly>

Each context declares a set of services. Each service tag declares one or more service beans.

<?xml version="1.0" encoding="UTF-8"?>
...
<assembly>
  <context name="[CONTEXT NAME 1]" >
    <services>
	   { a list of service beans }
    </services>
  </context>
	
  <context name="[CONTEXT NAME 2]" >
    <services>
	   { a list of service beans }
    </services>
  </context>
  
  ...
  
</assembly>

A service bean is namespaced to the containing context.

<?xml version="1.0" encoding="UTF-8"?>
...
<assembly>
  <context name="[FIRST CONTEXT NAME]" >
    <services>
      <service  id="[SERVICE ID 1]" ... > 
          ...  
      </service>
      <service  id="[SERVICE ID 2]" ... > 
          ...  
      </service>
      ...
    </services>
  </context>
	
  ...	
</assembly>

Here is a complete assembly configuration example for the printer service that is described above:

<?xml version="1.0" encoding="UTF-8"?>
...
<assembly>
  <context name="acme" >
    <services>
      <service       
                id="myPrinterFinder"
                interface="COM.acme.foo.bar.PrinterFinder" 
                singleton="true"  
                lazyLoad="false" >
        <impl>COM.acme.foo.bar.PrinterFinderImpl</impl>
      </service>

      <service       
                id="myPrinterService"
                interface="COM.acme.foo.bar.PrinterService" >
        <impl>COM.acme.foo.bar.PrinterFinderImpl</impl>
        <set-property name="printerFinder" >
                <ref service="myPrinterFinder" />
        </set-property>
      </service>

    </services>
  </context>
</assembly>

Resources

Bridgetown has assembly factories to create and initialise the service beans. In order to create the beans, each assembly factory must be configured before it is used. The responsibility of loading a configuration falls to the objects that implement the IConfigLoader interface. This interface is found in the package com.xenonsoft.bridgetown.resources.

Assembly factory can be configured from any type of resource. The limit is just the developer's own imagination. For example, it is totally possible to write a new configuration loader that parses a data set retrieved from a relational database.

The default configuration loader is XMLConfigLoader. Here is a typical idiom to creating a default service assembler that is loaded and configured by a XMLConfigFile object.

        IServiceAssembler sai = new AOPServiceAssemblerImpl();
        IConfigLoader configLoader = new XMLConfigLoader();
        configLoader.setFile( new File( "assembly-service.xml" ));
        
        sai.load( configLoader );

The interface IConfigLoader interface defines a few methods to load an assembly configuration from a java.io.InputStream, java.io.File, and an arbitary java.lang.String.

    public AssemblyConfig load( String uri );

    public AssemblyConfig load( File file );

    public AssemblyConfig load( InputStream inputStream );

Web Resources

For the web environment, there is a special WebConfigLoader configuration class, but there is a good chance you may never have to instantiate this class directly. The web application context configuration WebApplicationAssembler and WebApplicationAssemblerContextListener is creates and invokes WebConfigLoader

Starting the Assembly Factory

After loading the configuration into an assembly factory, it can be started with a simple call typically.

        IServiceAssembler sai = new AOPServiceAssemblerImpl();
        IConfigLoader configLoader = new XMLConfigLoader();
        configLoader.setFile( new File( "assembly-service.xml" ));
        sai.load( configLoader );

        sai.start();

Calling the start() method, creates and instantiates the service beans and/or proxies. By the time the method returns, the assembly guarantees to have injected the dependencies into the beans. The assembly factory is now ready for use.

Retrieving Service Beans

To retrieve a service from the assembly, you need to know the context name and the service bean identifier, which are ,ofcourse, declared in the assembly configuration.

        // Create and configure assembly factory
        IServiceAssembler sai = new AOPServiceAssemblerImpl();
        
        // Retrieve the service
        IDifferenceEngine engine1 = 
            (IDifferenceEngine)sai.getName("babbage", "myDifferenceEngine" );
        
        // Use the service
        engine1.analyse( ... );

In Bridgetown a service bean can be configured as a singleton or a prototype. A singleton is shared throughout the application (or rather the current accessible class loader defined by the application). A prototype service bean is alway instantiated and injected on every call for the service on the assembly factory.

There is a way of improving the type safety of retrieving service beans from the assembly. If you want to make sure that a service bean implements a particular interface or class, you can use this alternative method.

        // Retrieve the service
        IDifferenceEngine engine1 = 
            (IDifferenceEngine)sai.getName(
                "babbage", "myDifferenceEngine", IGamblingDevice ); 
        
        // Use the service
        engine1.analyse( ... );

Where IGamblingDevice is some interface or class that the service bean must implement or extend.

Can you add your own service beans to the assembly?
The short answer is no. You cannot store arbitary JavaBeans inside the assembly without having defined beforehand in an assembly configuration. The assembly factory is designed to be the place where you retrieve your services without having to answer awkward questions such as how do I configure a service bean that I want to initialise. Making a assembly support mutable service beans would be a much harder problem. The question you should really answer is why do want to be change the service beans that I can retrieve by name identifer at runtime? If you can answer that difficult question then you should be able to build your own IServiceAssembler implementation. If you do so, please don't forget your humble OPEN SOURCE benefactor!

Service beans can choose to implement two interfaces if you want to be notified when the assembly factory starts and disposes the bean. Admittedly, these interface strongly couple your service beans to the Bridgetown IoC Framework.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE assembly PUBLIC
    "-//XeNoNSoFT.com Open Source//DTD Bridgetown IoC Configuration//EN"
    "service-assembly-1.0.dtd">

<assembly>
  <context name="acme" >
    <services>
      <service       
                id="myPrinterFinder"
                interface="COM.acme.foo.bar.PrinterFinder" 
                singleton="true"  
                lazyLoad="false" >
        <impl>COM.acme.foo.bar.PrinterFinderImpl</impl>
      </service>

      <service       
                id="myPrinterService"
                interface="COM.acme.foo.bar.PrinterService" >
        <impl>COM.acme.foo.bar.PrinterFinderImpl</impl>
        <set-property name="printerFinder" >
                <ref service="myPrinterFinder" />
        </set-property>
      </service>

    </services>
  </context>
</assembly>

Disposing the Assembly Factory

If you decide to hold on to a reference of the assembly factory, it is quite reasonably to allow a means of safely dispose of it including any references to the service beans.

To discard the assembly factory, simply call the dispose() method. After this method the factory is in an undefined state, and your mileage may vary if you try to retrieve service beans.


        IServiceAssembler sai = new AOPServiceAssemblerImpl();
        IConfigLoader configLoader = new XMLConfigLoader();
        configLoader.setFile( new File( "assembly-service.xml" ));
        sai.load( configLoader );

        // ...
        
        sai.dispose();
        

Assembly Factory Types

There are currently two types of assembly factories within the Bridgetown IoC Framework. All assemblies make use of proxy bean builder. This is an interface that specifies how to create service beans. Some assembly factories will instantiate services directly, whilst other will create service bean using a proxy. As long as they implement the basic interface com.xenonsoft.bridgetown.soa.IServiceAssembler then it does not matter.

As normal user you will just develop application using either a default assembly factory or find a suitable third party custom implementation. To make development of assemblies easier, there are two abstract classes that hackers/painters can subclass. These are AbstractServiceAssembler.java and for AOP services AOPAbstractServiceAssembler.java and they both can found in the package com.xenonsoft.bridgetown.soa.impl.

Dependency Injection

Bridgetown IoC Framework supports three types of dependency injection.

Setter Injection

Bridgetown default assembler implementation AbstractServiceAssembler supports setter injection. This injection method works with JavaBean property writer methods also known as "setter" methods.

Service bean can have properties set (or injected) at assembly time. To set properties in the service bean configuration use the XML tag <set-property>. This tag requires a name attribute that represents the bean property and, of course, a value. For example.

      <service 	id="myCrashTestDummy" 
   		singleton="true" 
   		lazyLoad="false" >
        <impl>com.xenonsoft.bridgetown.test.CrashTestDummy</impl>
        <set-property name="fldString"
        	      value="Mary had a little lamb, its fleece was white as snow" />
        <set-property name="fldChar" value="A" />
        <set-property name="fldByte" value="48" />
        <set-property name="fldShort" value="32678" />
        <set-property name="fldInt" value="123456789" />
        <set-property name="fldLong" value="400500600900" />
        <set-property name="fldFloat" value="-14.95e-06" />
        <set-property name="fldDouble" value="6.789e60" />
        <set-property name="fldDecimal" value="3.141596527" />
      </service>

There a long-hand form method to define a property and value.

       <set-property name="[PROPERTY NAME]" value="[PROPERTY VALUE]" />

There also a short-hand method to define a property and value

       <set-property name="[PROPERTY NAME]" >
           <value>[PROPERTY VALUE]</value>
       </set-property>

Property values can be references to other service bean in the same context. The references are configured by the bean identifier name. References must be written with the long form syntax. Here example of calculation service that delegate four constituent service beans to handle different arithmetic operators;

    <services>
      <service 	id="calcService" 
   		interface="com.xenonsoft.bridgetown.examples.services.ICalcService" >
        <impl>com.xenonsoft.bridgetown.examples.services.impl.CalcServiceImpl</impl>
        
        <set-property name="adder" >
          <ref service="myAdder" />
        </set-property>
        <set-property name="subtractor" >
          <ref service="mySubtractor" />
        </set-property>
        <set-property name="multiplier" >
          <ref service="myMultiplier" />
        </set-property>
        <set-property name="divider" >
          <ref service="myDivider" />
        </set-property>
      </service>
      

It is possible to define inline java.util.List collection for a setter property.

        <set-property name="itemList" >
           
           <list>
              <value> Apple </value>
              <value> Peach </value>
              <value> Pear </value>
              <value> Plum </value>
              <ref service="myDummy" />
           </list>
           
        </set-property>

It is possible to define inline java.util.Map collection for a setter property.

        <set-property name="itemMap" >
           
           <map>
              
              <entry key="England" >
                 <value> London </value>
              </entry>
              <entry key="Scotland" >
                 <value> Edingburgh </value>
              </entry>
              <entry key="Wales">
                 <value> Cardiff </value>
              </entry>
              <entry key="Northern Ireland" >
                 <value> Belfast </value>
              </entry>
              <entry key="Other" >
                 <ref service="myDummy" />
              </entry>
              
           </map>
           
        </set-property>

Method Injection

Method injection is a way of injecting dependencies into a service bean, by calling a public accessible Java method. The method can be static or non-static. (Depending on the AOP proxy bean builder implementation, the method may or may not be final.) Method injection is similar to constructor injection, but obviously happens after the service bean is instantiated.

The advantages of method injection are:

The disadvantages of method injection are:

To configure method injection for the XML assembly configuration, use the following XML syntax:

        <method name="[ METHOD NAME ]" >
           <arg type="[ ARGUMENT TYPE ]" >
           	<value> [ ARGUMENT STRING VALUE ] </value>
           </arg>
           <arg value="[ ARGUMENT STRING VALUE ]" />
           <ref service="[ REFERENCE BEAN ID ]" />
        </method>

The method tag follows the set-property tag, and it obviously requires a Java method name as a tag attribute, and an arguments list. The arg tag optional has a type attribute, that defines the fully qualified class name of the argument type. If the type attribute is not specified then the argument class is assumed to be java.lang.String.

Here is an example:

    <services>
      <service 	id="testCenter" >
        <impl>org.acme.app.EdisonTelephoneTest</impl>
        
        <method name="etPhoneHome" >
           <arg type="java.lang.String" >
           	<value> Mary had a little lamb, its fleece was white as snow (LONG)</value>
           </arg>
           <arg value="Hiya ET (SHORT)" />
           <arg type="int" value="1048576" />
           <arg type="double" value="2.30258509" /> 
           <ref service="myET" /> 
        </method>
        
      </service> 
    </services>       

Please note: There is a shorthand and longhand way of declaring arguments. The latter XML extract would call the method signature below:

	public T testCenter( String, String, int, double, Object );

To specify in an argument list primitive, the default assembly factories make use of the following mapping table.

TextClass
bytejava.lang.Byte.TYPE
charjava.lang.Character.TYPE
shortjava.lang.Short.TYPE
intjava.lang.Integer.TYPE
longjava.lang.Long.TYPE
floatjava.lang.Float.TYPE
doublejava.lang.Double.TYPE
booleanjava.lang.Boolean.TYPE
Does Method Injection Happen in Declaration Order?
The assembly factory does not declare that methods are injected in the order that they appear in the configuration file. You, as a developer, should need rely on this fact even if it is a feature of a particular assembly factory. If you really want to manage the order of the calls during injection, then just use good old-fashion programming and refactoring! Hope this helps.

Constructor Injection

Constructor injection is applied in order instantiate a service bean. This form of injection is very similar to method injection in terms of the Bridgetown assembly, except constructors do not have method names.

There XML syntax for a invoking a constructor syntax is like this.

        <constructor>
           <arg type="[ ARGUMENT TYPE ]" >
           	<value> [ ARGUMENT STRING VALUE ] </value>
           </arg>
           <arg value="[ ARGUMENT STRING VALUE ]" />
           <ref service="[ REFERENCE BEAN ID ]" />
        </constructor>

In Java construction of a new object is guaranteed to be atomic operation. If you use constructor injection, you must pay attention to recursive dependencies.

Here is an example of the constructor for a object class called Foo

	T( double, String,  java.lang.Short, Object )  

Here is the signature for the above constructor declaration.

        <constructor>
           <arg type="double" >
           	<value> 3.14159 </value>
           </arg>
           <arg value=" Hello Everyone! " />
           <arg type="short" value=" 32678 " />
           <ref service=" myDigitalWatch " />
        </constructor>

Destroy Method

There is no such thing in Java as a C++ style destructor. However, it is possible for a service bean to be notified when the assembly factory wants to dispose of it. A service bean can register a destroy method, in contrast to implementing the IDisposable interface. In this way a service bean does not have to a compile dependency on the Bridgetown IoC Framework.

Here is the XML syntax for the destroy method.

        <destroy-method name="[ METHOD NAME ]" >
           <arg type="[ ARGUMENT TYPE ]" >
           	<value> [ ARGUMENT STRING VALUE ] </value>
           </arg>
           <arg value="[ ARGUMENT STRING VALUE ]" />
           <ref service="[ REFERENCE BEAN ID ]" />
        </destroy-method>

The assembly configuration only allows one destroy method per service bean.

Aspect Oriented Support

If you choose an application assembly that support Aspect Oriented proxies then you can use the factory to weave behaviours into your service beans. Because Bridgetown separates the concern of loading and parsing a configuration IConfigFile from the task of managing and maintaining an set of beans IServiceAssembler, the factory can choose or choose not to support AOP.

A full discussion of the phenomenon of Aspect Oriented Programming is outside the scope of this book, err blog .. err overview discussion. However, you can find various notes on this branch of computer science from the web and various wikis. It is assumed that you know what a pointcut is, what joinpoints are, and what we mean by weaving an advice into a joinpoint to modify a behaviour. If you do not understand all or any one of these terms, then stop right here.

XML Definition

The general XML configuration file accepts tag elements that declare pointcuts. There are two types of pointcuts. A local pointcut and a global pointcut.

A local pointcut is associated with the context scope. The tag pointcuts can be declared in the context tag after the services tag.

A global pointcut is not associated with any scope. It declare a pointcut that reference to service bean in any named context.

In Bridgetown any pointcut class implements the interface com.xenonsoft.bridgetown.aop.IPointcut interface. ( At the moment Bridgetown the default assembly use of com.xenonsoft.bridgetown.aop.IMethodPointCut interface.)

In Bridgetown any joinpoint class implements the interface com.xenonsoft.bridgetown.aop.IJoinPoint interface. ( At the moment Bridgetown the default assembly make use of com.xenonsoft.bridgetown.aop.IMethodJoinPoint interface.)

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE assembly PUBLIC
    "-//XeNoNSoFT.com Open Source//DTD Bridgetown IoC Configuration//EN"
    "service-assembly-1.0.dtd">

<assembly>
  <context name="acme" >
    <services>
        <!-- Define the service bean -->
    </services>
    
    
    <pointcuts>
        <!-- Define the local pointcuts here -->
    </pointcuts>
    
    
  </context>
  
  
  <global-pointcuts>
        <!-- Define the local pointcuts here -->
  </global-pointcuts>
  
</assembly>

This is a design for an pointcut interceptors.

    <pointcuts  name="[ POINTCUT NAME ]">
        <!-- Declare a set of JOINPOINTS  -->
        <!-- Declare a METHOD INVOCATION POINT -->
    </pointcuts>

Pointcuts defines a set of pointcut that constraint to classes of services bean defined in the enclosing context

    <pointcuts>
        
        <pointcut name="p1" 
                  description="Here is an optional description" >
            <joinpoints>
                <joinpoint>
                    <!-- 
                    A service filter tag defines the class of object to filter 
                    So technically this is a convenient shorthand to a class filter.
                    -->
                     
                    <service-finder>myCrashTestDummy</service-finder>;
                    <!-- 
                    A method filter tag specifies the method to intercept 
                    -->
                    <method-finder>hello</method-finder>;
                </joinpoint>
            </joinpoints>
            <invoke service="myInterceptorBean1" />
        </pointcut>;
    </pointcuts>

Pointcuts XML tag must contain one or more pointcut XML tags. Each pointcut tag contains a joinpoints tag. A joinpoints tag contains one or more joinpoint tags.

First, this XML design allows one or more pointcut to be defined per context. Second, any pointcut can have one or more joinpoints.

A pointcut has a name identifier and a implementation type. Default implementation type for a pointcut is com.xenonsoft.bridgetown.aop.impl.DefaultMethodPointCut. By designing a implementation type, we allow other forms of pointcuts to be invented in future.

A joinpoint has no name identifier, but must contain at least one finder declaration. For a method joinpoint the user must define one method finder, at least. He or she can choose to specify the class that will be intercepted using a class finder or class derives from a known service bean ( a service bean finder). Finder tags essentially define an expression that denotes how to locate a joinpoint.

The default implementation of pointcuts and joinpoints makes use of glob expressions across method interceptions.

A joinpoint has a optional type. Default implementation type for joinpoint is com.xenonsoft.bridgetown.aop.impl.DefaultMethodJoinPoint. By designing a implementation type, we allow other forms of joinpoint to be invented in future.

The invoke tag specifies the bean with a method interceptor The invocation service bean must implement com.xenonsoft.bridgetown.aop.IMethodInterceptor interface.

Here is a second example that demonstrates the class finder tag for a joinpoint.

        <pointcut name="p2" >
            <description>Here is an optional description</description>
            <joinpoints>
                <joinpoint>
                    <class-finder>com.foo.bar.*</class-finder>;
                    <method-finder>set*</method-finder>
                </joinpoint>
            </joinpoints>
            <invoke service="myInterceptorBean2" />
        </pointcut>

It is possible to use alternative type implementations for both a pointcut and a joinpoint. The default pointcut implementation is a method pointcut the default type is com.xenonsoft.bridgetown.aop.impl.DefaultMethodPointCut

        <pointcut name="p3" type="com.xenonsoft.bridgetown.aop.impl.DefaultMethodPointCut" >
            <description>Here is an optional description</description>
            <joinpoints>
                <joinpoint type="com.xenonsoft.bridgetown.aop.impl.DefaultMethodJoinPoint" >;
                    <class-finder>com.foo.bar.*</class-finder>
                    <method-finder>set*</method-finder>
                </joinpoint>
            </joinpoints>
            <invoke service="myInterceptorBean2" />
        </pointcut>

The XML fragments for a joinpoint can be written in shorthand notation. We can make use of XML tag attributes instead of body content tags. A word of advice: choose one form or the other. I recommend that you don't mix up the notations.

     
        <pointcut name="p4" >
             <description>Here is the short hand form</description>
             <joinpoints>
                 <joinpoint class-finder="com.foo.bar.*" 
                            method-finder="set*" />;
             </joinpoints>
              And also must define a interceptor service bean 
            <invoke service="myInterceptorBean2" />
        </pointcut>

A pointcut can have one or more (method) joinpoints.

        <pointcut name="p5" >
            <description>Multiple join points illustrated</description>
            <joinpoints>
               <joinpoint
                       class-finder="com.foo.bar.*" 
                       method-finder="set*" />
               <joinpoint
                       class-finder="org.apache.struts.action.*" 
                       method-finder="execute*" />
               <joinpoint
                       class-finder="sample.music.flavours.*" 
                       method-finder="playFormat*" />
            </joinpoints>;
            <invoke service="myDebugInterceptor" />
        </pointcut>

Joinpoints with common method or class finder expressions can be be defaulted using the joinpoints tag element

        <pointcut name="p6" >
            <description>Multiple join points illustrated</description>
            <joinpoints
               default-class-finder="sample.music.flavours.*" >
               <joinpoint
                       method-finder="set*" />
               <joinpoint
                       method-finder="execute" />
               <joinpoint
                       method-finder="get*" />
            </joinpoints>
            <invoke service="myDebugInterceptor" />
        </pointcut>

Joinpoints with a common method expression can be defaulted using the joinpoints tag element.

        <pointcut name="p6" >
            <description>Multiple join points illustrated</description>
            <joinpoints
               default-method-finder="set*"  >
               <joinpoint
                       class-finder="com.bigbank.frontoffice.*" />
               <joinpoint
                       class-finder="com.bigbank.middleoffice.*" />
               <joinpoint
                       class-finder="com.bigbank.backoffice.*" />
            </joinpoints>
            <invoke service="mySecurityInterceptor" />
        </pointcut>
    </pointcuts>

Finally an assembly configuration can have a global pointcuts. These are point cuts that refer to service beans across context. A global pointcut must establish a context name and a service bean.

  <global-pointcuts>
       The point cut has a name identifier and a type. 
      <pointcut name="p7" type="com.xenonsoft.bridgetown.aop.impl.DefaultMethodPointCut" >
          <description>Here is an optional description</description>
          <class-finder>*</class-finder>
          <method-finder>execute</method-finder>
          <invoke context="someContext" service="myInterceptorBean3" />;
      </pointcut>
  </global-pointcuts>

The user must specify a context and a service in the invoke tag for a global pointcut. The invocation bean must implement com.xenonsoft.bridgetown.aop.IMethodInterceptor.

Method Interceptor

In order to weave behaviour into service beans, an intercepting bean must implement interface com.xenonsoft.bridgetown.aop.IMethodInterceptor.

public interface IMethodInterceptor {
    public Object invocation( IInterceptionContext context ) throws Throwable ;
}

Interceptor Context

The proxy builder for AOP services will supply the intercepting bean with a context that contains the necessary information. The type of the context is com.xenonsoft.bridgetown.aop.IInterceptionContext.

This interface defines a few interesting properties.

public interface IInterceptionContext {

    public IPointCut    getPointCut();
    
    public Class        getInvocationClass();

    public Method       getInvocationMethod();

    public Object       getBean();
    
    public Object[]     getParameters();

    public Object       proceed() throws Throwable;
    
}

Specifying Advice

Here is a simple method interceptor written as a JavaBean. It simple weaves behaviour around the entry and exit points from a target method.

public class DebugInterceptor implements IMethodInterceptor {
    public DebugInterceptor() {
        super();
    }

    public Object invocation( IInterceptionContext context ) throws Throwable 
    {
        String info = context.getInvocationClass().getName()+
            " # "+context.getInvocationMethod().getName()+"()";
        System.out.println( "DEBUG:  Before "+info );
        Object retval = context.proceed();
        System.out.println( "DEBUG:  After  "+info );
        return retval;
    }

}

The above is code snippet is an example of the "around" advice.

Bridgetown defines some convenience adaptor classes for "before" and "after" advice, see com.xenonsoft.bridgetown.aop.helper.BeforeAdviceAdaptor and com.xenonsoft.bridgetown.aop.helper.AfterAdviceAdaptor respectively.

Sometimes a target method will raise an exception, in these case you might be interested in the com.xenonsoft.bridgetown.aop.helper.ThrowsExceptionAdviceAdaptor for advising on target beans only when an java.lang.Exception is raised.

 

TO BE CONTINUED *PP*



Copyright © 2005 XeNoNSoFT.com. All Rights Reserved.