|
||||||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||||
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. |
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)
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.
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.
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.
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 );
}
}
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.
IConfigLoader).
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>
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 );
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
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.
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.
com.xenonsoft.bridgetown.soa.IStartable an interface for
the start of the life cycle of the service bean
public interface IStartable {
public void start();
}
com.xenonsoft.bridgetown.soa.IDisposable an interface for
the end of the life cycle of the service bean. (Prototype service bean may
not necessarily receive notification of disposal, unless the assembly
factory keeps references to the all service bean invocations.)
public interface IDisposable {
public void dispose();
}
<?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>
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();
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.
com.xenonsoft.bridgetown.soa.impl.ServiceAssemblerImpl -
This is a basic assembly built with proxy builder based on Common BeanUtils.
com.xenonsoft.bridgetown.soa.impl.AOPServiceAssemblerImpl
This is an assembly built with proxy builder based on CGLIB, and specifically
supports Aspect Oriented dependency injection.
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.
Bridgetown IoC Framework supports three types of dependency 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 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.
| Text | Class |
|---|---|
| byte | java.lang.Byte.TYPE |
| char | java.lang.Character.TYPE |
| short | java.lang.Short.TYPE |
| int | java.lang.Integer.TYPE |
| long | java.lang.Long.TYPE |
| float | java.lang.Float.TYPE |
| double | java.lang.Double.TYPE |
| boolean | java.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 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>
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.
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.
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.
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 ;
}
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;
}
getPointCut() - returns the pointcut type that
is responsible for this interception call.
getInvocationClass() - returns the class of the
bean that is being intercepted
getInvocationMethod() - returns the method of the
bean that is being intercepted
getBean() - return the object instance the target
bean that is being intercepted
getParameters() - returns the argument list that
is being supplied to the method on the target bean.
proceed() - this is very important that invokes
the intercepted method on the target bean. It will return an Object
if the method returns a object (or it will return null
if the target method returns void), or it can raise an exception.
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* |
|
||||||||||
| PREV NEXT | FRAMES NO FRAMES | |||||||||