Lima-Loa Version 0.6 User Manual

Chris Nappin, 05/03/2010

 

Contents

Section 1.

What is Lima-Loa?

Section 2.

The Adapter Design Pattern

Section 3.

Supported Platforms and Dependencies

Section 3.1

Supported Platforms

Section 3.2

Supported Dependencies

Section 4.

Dependencies

Section 5.

Getting Started

Section 5.1

The Direct API

Section 5.2

Spring Integration

Section 6.

Method Mapping

Section 6.1

Method Mapping with the Direct API

Section 6.2

Method Mapping with the Spring Integration

Section 7.

Parameter and Return Type Mapping

Section 7.1

Built-in Mappings

Section 7.2

Dozer Mappings

Section 8.

Exception Mapping

Section 8.1

Exception Mapping with the Direct API

Section 8.2

Exception Mapping with the Spring Integration

Section 8.3

Exception Creation

Appendix A.

License

Appendix B.

Logging

 

1. What is Lima-Loa?

Lima-Loa is an automated implementation of the Gang of Four Adapter Design Pattern, for Java. It aims to greatly reduce or get rid of the need to write dull boiler plate code when creating an adapter.

Lima-Loa is Open Source software, published under the Apache License, version 2.0.

 

2. The Adapter Design Pattern

The Gang of Four Design Patterns book describes the intent of the Adapter pattern as follows:

Convert the interface of a class into another interface interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

One implementation of the adapter pattern (class adapters) involves multiple inheritance, so is not possible using the Java programming language. Another implementation (object adapters) involves object composition, and is the typical solution when using Java. The following UML diagram shows the participants in the object adapter implementation.

Adapter Design Pattern

The participants are as follows:

In the above diagram, the client calls methodA(int} on the adapter, which then triggers the adapter to call methodB(long) on the target object. The adapter then passes the result of the target method call back to the client. In order to do this, the adapter must convert the int parameter to a long, then the boolean result to a String.

Typically an object adapter would need to be manually written by a developer. In the above simple example that would not be particularly difficult, but in a more complex example where the parameters and results are large graphs of Java objects (e.g. custom JavaBeans) and where exceptions must also be mapped, a reasonably large amount of dull yet brittle code would need to be manually written.

Lima-loa is an automated implementation of an object adapter, and intends to replace large amounts of such dull yet brittle code with a relatively small amount of configuration.

 

3. Supported Platforms and Dependencies

3.1 Supported Platforms

Lima-Loa is a pure Java library, and requires JDK 1.5 or above. It is tested with recent releases of the Sun 1.5 and 1.6 JDKs.

 

3.2 Supported Dependencies

Lima-Loa is designed to work with Dozer version 5.x, and Spring versions 2.0.x, 2.5.x and 3.0.x. It is tested with the following releases, but other compatible versions should also work.

Dependency

API Used

Version Tested

Dozer

5.x

5.2

Spring

2.0.x

2.0.8

2.5.x

2.5.SEC01

3.0.x

3.0.1

Lima-Loa requires the following list of third party Java libraries in order to work. One is a direct dependency, the rest are dependencies of Dozer or Spring. For ease these are included in the "lib" folder of the distribution, with the exception of Spring (due to the size). Other versions or combinations of these libraries may well work, they simply haven't been tested.

If only using the Direct API:

Not using Dozer

Using Dozer 5.x

commons-logging-1.1.1.jar

dozer-5.x.jar
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.4.jar
commons-logging-1.1.1.jar


If using the Spring Integration:

Not using Dozer

Using Dozer 5.x

Spring 2.0.x

spring-core-2.0.x.jar
spring-context-2.0.x.jar
spring-beans-2.0.x.jar
commons-logging-1.1.1.jar

spring-core-2.0.x.jar
spring-context-2.0.x.jar
spring-beans-2.0.x.jar
commons-logging-1.1.1.jar
dozer-5.x.jar
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.4.jar

Spring 2.5.x

spring-core-2.5.x.jar
spring-context-2.5.x.jar
spring-beans-2.5.x.jar
commons-logging-1.1.1.jar

spring-core-2.5.x.jar
spring-context-2.5.x.jar
spring-beans-2.5.x.jar
commons-logging-1.1.1.jar
dozer-5.x.jar
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.4.jar

Spring 3.0.x

org.springframework.asm-3.0.x.jar
org.springframework.core-3.0.x.jar
org.springframework.context-3.0.x.jar
org.springframework.beans-3.0.x.jar
org.springframework.expression-3.0.x.jar
commons-logging-1.1.1.jar

org.springframework.asm-3.0.x.jar
org.springframework.core-3.0.x.jar
org.springframework.context-3.0.x.jar
org.springframework.beans-3.0.x.jar
org.springframework.expression-3.0.x.jar
commons-logging-1.1.1.jar
dozer-5.x.jar
commons-beanutils-1.8.0.jar
commons-collections-3.2.1.jar
commons-lang-2.4.jar

 

5. Getting Started

There are two ways to use Lima-Loa, both have exactly the same functionality:

 

5.1 The Direct API

Firstly, create an instance of AdapterFactory by calling one of the following methods on the org.limaloa.AdapterFactory class:

    public static AdapterFactory getInstance()
    
    public static AdapterFactory getInstance(List<String> mappingFiles, boolean useDozer)

The first method uses the default settings, which include not to use Dozer. These factory instances are relatively expensive objects and are intended to be re-used, if you need to create more than one adapter object.

Next, define a List of MethodMappings. See Section 6 for details of the various options available, but as long as you are not using overloaded methods then one of the following constructors can be used to create each MethodMapping instance:

     public MethodMapping(String source, String target)
     
     public MethodMapping(String source, String target, List<ExceptionMapping> exceptionMappings)

Where source is the source method name, target is the target method name, and the optional exceptionMappings convert exceptions thrown by the target method.

Next, create an adapter object by calling the following method on the factory instance:

    public <T> T createAdapter(Class<T> sourceInterface, Object target, List<MethodMapping> methodMappings)
            throws MappingCreationConfigurationException

This returns an object that implements sourceInterface, and no explicit cast is needed. The target is the object you'd like the adapter method calls to be redirected to, i.e. the object being adapted. The List of methodMappings is as created in the previous step. At runtime, all calls to the adapter are seamlessly redirected to the nominated method in the target object.

An exception is thrown if there is anything wrong with the specified configuration. As much as possible is checked at creation time, rather than at runtime.

See the "direct-api/simple" example in the "samples" folder of the distribution for a fully working example. See the ReadMe.txt file in that folder for details of the example.

5.2. Spring Integration

Lima-Loa is a Spring extension, so you simply add it to the classpath and use a new schema in your Spring Application Context xml files. Under the covers, the Lima-Loa Spring Integration simply calls the Direct API, so the functionality is identical.

Firstly, add the Lima-Loa schema to your Application Context xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:ll="http://www.limaloa.org/schema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                        http://www.limaloa.org/schema http://www.limaloa.org/schema/limaloa-0.6.xsd">

This should mean you get content-assist and element/attribute descriptions if you use a Schema-aware XML editor (e.g. Eclipse).

The next step is to create an Adapter Configuration (which corresponds to a Direct API AdapterFactory) as follows:

    <ll:adapterConfiguration/>

Note that unless you need to use Dozer then this step is actually optional - if no adapterConfiguration is defined then a default one will be created on demand. Note also that only one adapterConfiguration is allowed per Application Context (this includes any parent xml files in a hierarchical Application Context), so if more than one is found then an exception is thrown.

If you would like to use Dozer and specify some mapping files, the syntax is as follows:

    <ll:adapterConfiguration useDozer="true">
        <ll:configFiles>
            <ll:file>custom-bean-mapping1.xml</ll:file>
            <ll:file>custom-bean-mapping2.xml</ll:file>
        </ll:configFiles>
    </ll:adapterConfiguration>

Next, create an adapter as a Spring Bean. See Section 6 for details of the various options available, but as long as you are not using overloaded methods then the following XML elements (known as the short syntax) can be used:

    <ll:adapter id="source" interface="source" targetObject="target">
        <ll:methodMapping source="sourceMethod1" target="targetMethod1"/>
        <ll:methodMapping source="sourceMethod2" target="targetMethod2"/>
   </ll:adapter>

This creates a Spring Bean that implements interface. The targetObject is a reference to another Spring bean you'd like any adapter method calls to be redirected to, i.e. the object being adapted. The methodMappings map source interface method names to target object method names. At runtime, all calls to the adapter are seamlessly redirected to the nominated method in the target object.

The adapter is a bean instance that can be used just as any other Spring bean, for example it can be injected as a property into another object. That other object need not know that Lima-Loa is even being used, as its only dependency is on the source interface.

As with the Direct API, an exception is thrown if there is anything wrong with the specified configuration. As much as possible is checked at creation time, rather than at runtime.

See the "spring/simple" example in the "samples" folder of the distribution for a fully working example. See the ReadMe.txt file in that folder for details of the example.

 

6. Method Mapping

Method mappings are more complete in this release, but static methods are still not supported supported. There are two ways of identifying methods:

  1. Shorter Syntax - shorter configuration, matches methods by name only (regardless of parameter types), which works fine as long as methods are not overloaded. If more than one method is found matching the method name, an exception is thrown.

  2. Longer Syntax - more verbose configuration, but matches methods precisely by name and parameter types

Note that each method mapping must use the same type of syntax to identify the source and target methods, you cannot mix the shorter and longer syntax for a single method mapping.

6.1. Method Mapping with the Direct API

To use the shorter syntax, create the MethodMapping using one the following constructors:

     public MethodMapping(String source, String target)
     
     public MethodMapping(String source, String target, List<ExceptionMapping> exceptionMappings)

To use the longer syntax, first create a Parameter instance for each of the source and target method parameters using one the following constructors:

     public Parameter(Class<?> parameterClass)
     
     public Parameter(String type) throws ClassNotFoundException

Passing the type as a String allows you to sepcify primitive parameters by type name (i.e. boolean, byte, short, int, long, float, double, or char). If not one of these primitive type names, Lima-Loa will assume the String is a fully-qualified classname and attempt to instantiate the class using Class.forName(type).

Next, put the parameters in Lists and create the source and target Method instances using the following constructor:

     public Method(String name, List<Parameter> parameters)

If the method doesn't take any parameters (i.e. it is void) then pass a null or empty list of parameters.

Lastly, create the MethodMapping using using one the following constructors:

     public MethodMapping(Method source, Method target)
     
     public MethodMapping(Method source, Method target, List<ExceptionMapping> exceptionMappings)

6.2. Method Mapping with Spring Integration

To use the shorter syntax, use the source and target attributes of the methodMapping XML element, as follows:

    <ll:adapter id="source" interface="source" targetObject="target">
        <ll:methodMapping source="sourceMethod" target="targetMethod"/>
   </ll:adapter>

To use the longer syntax, use the source and target XML elements, with nested parameter XML elements, as follows:

    <ll:adapter id="source" interface="source" targetObject="target">
        <ll:methodMapping>
            <source name="sourceMethod">
                <parameter type="java.lang.String"/>
                <parameter type="long"/>
            </source>
            
            <target name="targetMethod">
                <parameter type="int"/>
                <parameter type="boolean"/>
            </target>
        </methodMapping>
   </ll:adapter>

The parameter type is passed to the Direct API as a String, which allows sepcifying primitive parameters by type name (i.e. boolean, byte, short, int, long, float, double, or char). If not one of these primitive type names, Lima-Loa will assume the String is a fully-qualified classname and attempt to instantiate the class using Class.forName(type).

If the method doesn't take any parameters (i.e. it is void) then simply ommit any parameter XML elements, as follows:

    <ll:adapter id="source" interface="source" targetObject="target">
        <ll:methodMapping>
            <source name="sourceMethod"/>
            <target name="targetMethod"/>
        </methodMapping>
   </ll:adapter>

 

7. Parameter and Return Type Mapping

Mapping of "basic" types is handled directly by a built-in mapper, and anything else is delegated to Dozer (if enabled) - which is an Open Source library intended for mapping custom JavaBeans.

 

7.1 Built-in Mappings

The built-in mappings handle the following situations:

Destination
boolean
Boolean
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
char
Character
String
Source boolean
Boolean
Yes No No No No No No No Yes
byte
Byte
No Yes Yes Yes Yes Yes Yes Yes Yes
short
Short
No Yes*5 Yes Yes Yes Yes Yes Yes Yes
int
Integer
No Yes*5 Yes*5 Yes Yes Yes Yes Yes*5 Yes
long
Long
No Yes*5 Yes*5 Yes*5 Yes Yes Yes Yes*5 Yes
float
Float
No No No No No Yes Yes No Yes
double
Double
No No No No No No Yes No Yes
char
Character
No Yes*5 Yes*3 Yes*3 Yes*3 Yes*3 Yes*3 Yes Yes
String Yes *1 Yes*2 Yes*2 Yes*2 Yes*2 Yes*2 Yes*2 Yes*4 Yes

Any use of unsupported combinations (marked as "No" in the table above) results in a org.limaloa.object.UnsupportedObjectMappingException thrown at runtime.

*1 - Treats "true" (case-insensitive) as true, anything else (including null) as "false", using Boolean.valueOf(String).

*2 - Uses the appropriate Wrapper.valueOf(String) method, which throws java.lang.NumberFormatException if invalid, null or empty.

*3 - Converts the raw 16-bit character value to a number, for example 'A' becomes 65.

*4 - Expects single character length Strings, org.limaloa.object.UnsupportedObjectMappingException thrown at runtime if the String is empty or length is more than 1.

*5 - Checks the numeric value is not too large or too negative to fit into the destination type, org.limaloa.object.InvalidNumericMappingException thrown at runtime if the value cannot fit into the destination type. For example, if converting from a short to byte, the source value must be from -128 to +127.

 

7.2 Dozer Mappings

Dozer (http://dozer.sourceforge.net/) is an object mapper for custom JavaBeans. It expects objects to follow the JavaBean conventions (have a no arguments constructor, and have getter and setter methods for each attribute), and can handle "graphs" of nested objects of any depth. By default it will map attributes in any source type to attributes in any destination type by attribute name, with any attributes that don't have a matching name in the destination object being left at null. To define attribute mappings when the attribute name differs, or the object graph structure itself differs, you simply define a Dozer XML mapping file.

When using Dozer within Lima-Loa, XML mapping files are defined in the parameter to the org.limaloa.AdapterFactory getInstance(List mappingFiles) method (if using the Direct API), or are defined by <configFiles> XML elements within the <adapterConfiguration> XML element (if using the Spring Integration).

See the "direct-api/custom-bean" example in the "samples" folder of the distribution for a fully working example of using Dozer with Lima-Loa via the Direct API. See the ReadMe.txt file in that folder for details of the example.

See the "spring/custom-bean" example in the "samples" folder of the distribution for a fully working example of using Dozer with Lima-Loa via Spring Integration. See the ReadMe.txt file in that folder for details of the example.

 

8. Exception Mapping

Any exceptions (checked or unchecked) thrown by the target method can be caught and mapped to an alternative exception that will then be propogated back to the calling client code. Any exceptions thrown by a target method that don't have any explicit mapping defined will be handled by Lima-Loa as follows:

 

8.1 Exception Mapping with the Direct API

For each exception to map, create an ExceptionMapping instance using one of the following constructors:

    public ExceptionMapping(String sourceClassname, String targetClassname)
    
    public ExceptionMapping(Class<? extends Throwable> sourceClass,
            Class<? extends Throwable> targetClass)
            
    public ExceptionMapping(String sourceClassname, String targetClassname, boolean wrap)
	
    public ExceptionMapping(Class<? extends Throwable> sourceClass, 
			Class<? extends Throwable> targetClass, boolean wrap)

The sourceClassname or sourceClass is the exception thrown by the source method, and the targetClassname or targetClass is the exception thrown by the target method, i.e. the mapping is from target back to source. If defining exceptions as a String, Lima-Loa will assume it is a fully-qualified classname and attempt to instantiate it using Class.forName(classname). The wrap parameter indicates whether or not to pass the original exception as the cause in the mapped exception, by default Lima-Loa will do this.

Next, add all of your ExceptionMapping instances into a List. At runtime, exceptions thrown by the target method will be mapped by comparing whether it is the same type or a subtype, in the order defined by the list. This means that if using both base types and sub types, the sub types must come earlier in the list otherwise the base type mapping will match instead. This is deliberately identical to the constraints of ordering catch blocks in Java code.

If using the shorter syntax, pass the List of ExceptionMappings using the following constructor:

     public MethodMapping(String source, String target, List<ExceptionMapping> exceptionMappings)

If using the longer syntax, pass the List of ExceptionMappings using the following constructor:

     public MethodMapping(Method source, Method target, List<ExceptionMapping> exceptionMappings)

 

8.2 Exception Mapping with the Spring Integration

Regardless of whether using the shorter or longer syntax, simply add any number of exception XML elements as appropriate as follows:

    <ll:adapter id="source" interface="source" targetObject="target">
        <!-- shorter syntax -->
        <ll:methodMapping source="sourceMethod1" target="targetMethod2">
            <ll:exception source="sourceEx1" target="sourceEx1"/>
        </ll:methodMapping>
        
        <!-- longer syntax -->
        <ll:methodMapping>
            <source name="sourceMethod2"/>
            <target name="targetMethod2"/>
            <ll:exception source="sourceEx2" target="sourceEx2" wrap="false"/>
            <ll:exception source="sourceEx3" target="sourceEx3"/>
        </methodMapping>
   </ll:adapter>

The source attribute is the fully-qualified classname of the exception thrown by the source method, and the target is the fully-qualified classname of the exception thrown by the target method, i.e. the mapping is from target back to source. The optional wrap attribute indicates whether or not to pass the original exception as the cause in the mapped exception, the default is true.

At runtime, exceptions thrown by the target method will be mapped by comparing whether it is the same type or a subtype, in the order that the exception XML elements are defined. This means that if using both base types and sub types, the sub types must be defined before base types otherwise the base type mapping will match instead. This is deliberately identical to the constraints of ordering catch blocks in Java code.

 

8.3 Exception Creation

When mapping exceptions and instantiating the source exceptions, Lima-Loa chooses which constructor to use by using the following prioritised list:

If wrapping the original target exception is enabled (the default):

  1. public Exception(String message, Throwable ex)
  2. public Exception(Throwable ex, String message)
  3. public Exception(Throwable ex)
  4. public Exception(String message)
  5. public Exception()

If wrapping the original target exception is disabled:

  1. public Exception(String message)
  2. public Exception()

If Lima-Loa cannot find any appropriate constructors at all, it will throw a org.limaloa.mapping.exception.ExceptionMappingConfigurationException upon startup.

 

Appendix A. License

Copyright 2009 Chris Nappin

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

 

 

Appendix B. Logging

Lima-Loa uses the Jakarta Commons Logging (JCL) library for runtime logging, as does the Spring framework itself. JCL can be configured to log in a variety of ways, see http://commons.apache.org/logging for further details.

Lima-Loa uses log levels as follows: