This page applied to GeDA v.2.0.0+, specifically module geda.spring-integration.

This is a basic tutorial which goes thought the example given in the integration test that can be found in the geda.spring-integration module.

Annotation driven overview

To make this absolutely clear - you DO NOT have to use geda.spring-integration. All it is - is a best practice approach to spring beans setup for GeDA. You are welcome to use just geda.core if you find spring-integration not suitable for you.

  With v.2.1.1 you also have a choice of not using AOP nor @Transactional but simply through XML configuration.

So what does it actually do?

There are two options supported: dynamic binding and BeanPostProcessor.

In my view BeanPostProcessor is better since it allows Spring to create 'Advice's at startup thus creating a full list of methods from the start. Dynamic binding evaluated applicable methods during execution, so all evaluated beans are scanned on every execution with blacklisting methods which are not applicable (this is more flexible but creates huge method lookup tables). But BOTH of these are VALID approaches.

The way beans' methods are identified by GeDA annotation-driven strategy is through the @Transferable annotation, placed on the implementor of the bean.

The signature of the method should be supported by @Transferable annotation (see JavaDoc's for more details).

Once the methods are scanned by AOP module before and/or after advice is registered for applicable methods. So that either parameters or return values of the method get converted to/from DTO/Entity objects.

But it is always easier to see with an example...

Maven dependencies

To integrate GeDA with your Spring 3 project you will need the following two modules:

<!-- core module: use can you it on its own if you are not using Spring 3 -->
<dependency>
   <groupId>com.inspire-software.lib.dto.geda</groupId>
   <artifactId>geda.core</artifactId>
   <version>2.0.0</version>
</dependency>

<!-- spring integration: spring 3 AOP wrapper for core -->
<dependency>
   <groupId>com.inspire-software.lib.dto.geda</groupId>
   <artifactId>geda.spring-integration</artifactId>
   <version>2.0.0</version>
</dependency>

Spring 3 beans setup

  There is a number of different schemas which add new features, we recommend to use the latest which is:http://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/schema/geda-spring-integration-annotation-2.1.1.xsd

I am going to describe the BeanPostProcessor approach in this tutorial, so:

<!--
  ~ This code is distributed under The GNU Lesser General Public License (LGPLv3)
  ~ Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
  ~
  ~ Copyright Denis Pavlov 2009
  ~ Web: http://www.inspire-software.com
  ~ SVN: https://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto
  -->


<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:geda="http://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/schema"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/schema
                           http://geda-genericdto.svn.sourceforge.net/svnroot/geda-genericdto/schema/geda-spring-integration-1.0.xsd"
>


   <!--
        dto-factory must match the dtoFactory bean name. This is mandatory attribute.
        dto-adapters-registrar must also match bean name. This is optional attribute.
        All on-* are optional, and define even handlers to give you last minute tidy up hooks
            for processing your DTO's and Entities.
        use-bean-processor="true" defines the BeanPostProcessor method of Advice enrichment for GeDA
     -->

   <geda:annotation-driven dto-factory="dtoFactory"
                           dto-adapters-registrar="adapterRegistrar"
                           on-dto-assembly="onDto"
                           on-dto-assembled="onDtoSuccess"
                           on-dto-failed="onDtoFailure"
                           on-entity-assembly="onEntity"
                           on-entity-assembled="onEntitySuccess"
                           on-entity-failed="onEntityFailure"
                           use-bean-preprocessor="true"/>


   <!--
        Standard factory, which is used for ALL your DTO/Entity implementations.
        Judging from experience - you should not put entities in here, these should only be
                                  created manually, or through converters invoking special methods
                                  on your Entity. E.g. Order object might have Order.createOrderLine()
                                  method. And you can use on-entity-assembly event handler to invoke
                                  that.
    -->

   <bean id="dtoFactory" class="com.inspiresoftware.lib.dto.geda.impl.DTOFactoryImpl">
       <constructor-arg>
           <map>
               <entry key="entityKey" value="com.inspiresoftware.lib.dto.geda.test.impl.DomainObjectImpl"/>
               <entry key="dtoKey"    value="com.inspiresoftware.lib.dto.geda.test.impl.ExtendedDataTransferObjectImpl"/>
               <entry key="filterKey" value="com.inspiresoftware.lib.dto.geda.test.impl.DataTransferObjectImpl"/>
           </map>
       </constructor-arg>
   </bean>



   <!--
 Event handler. You are only allowed to specify one event handler per event. However, each have
        "context" parameter, which you can pass through using @Transferable. Therefore it is possible
        to create composite event handlers that use sub-handler depending on the event context.
     -->



   <bean id="onDto" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>
   <bean id="onDtoSuccess" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>
   <bean id="onDtoFailure" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>
   <bean id="onEntity" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>
   <bean id="onEntitySuccess" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>
   <bean id="onEntityFailure" class="com.inspiresoftware.lib.dto.geda.test.impl.CountingEventListener"/>

   <bean id="simpleTransferableService"
         class="com.inspiresoftware.lib.dto.geda.test.impl.TestServiceImpl"/>


   <!--
        Registrar allows to register your converters as a map of spring beans.
        If Converter/Retriever/Matcher bean implement DTOSupportAwareAdapter interface then
        they will be injected with DTOSupport object, which is the central hub service for
        the GeDA conversions. So you can bootstrap any chain of manual conversions
        through your adapters.
     -->

   <bean id="adapterRegistrar" class="com.inspiresoftware.lib.dto.geda.test.impl.ExposedDTOAdaptersRegistrarImpl">
       <constructor-arg>
           <map>
               <entry key="vc" value-ref="adapterValueConverter"/>
               <entry key="er" value-ref="adapterEntityRetriever"/>
               <entry key="ma" value-ref="adapterMatcher"/>
           </map>
       </constructor-arg>
   </bean>

   <bean id="adapterValueConverter" class="com.inspiresoftware.lib.dto.geda.test.impl.ValueConverterImpl"/>
   <bean id="adapterEntityRetriever" class="com.inspiresoftware.lib.dto.geda.test.impl.EntityRetrieverImpl"/>
   <bean id="adapterMatcher" class="com.inspiresoftware.lib.dto.geda.test.impl.DtoToEntityMatcherImpl"/>

</beans>

Spring bean service and @Transferable

So, now how do you define transferable methods. Interested reader is highly advised to look at the source code of module test classes.

Here I will demonstrate a couple of examples:

package com.inspiresoftware.lib.dto.geda.test.impl;

import com.inspiresoftware.lib.dto.geda.annotations.Direction;
import com.inspiresoftware.lib.dto.geda.annotations.Transferable;
import com.inspiresoftware.lib.dto.geda.test.DomainObject;
import com.inspiresoftware.lib.dto.geda.test.ExtendedDataTransferObject;
import com.inspiresoftware.lib.dto.geda.test.TestService;

import java.util.Collection;
import java.util.Date;

public class TestServiceImpl implements TestService {

   @Transferable(before = Direction.DTO_TO_ENTITY,
            entityKey = "entityKey", dtoKey = "dtoKey", context = "dtoToEntityBeforeExact")
   public void dtoToEntityBeforeExact(Object dto, Object entity) {
       // at this point entity will contain all values transferable from dto
   }


   @Transferable(after = Direction.DTO_TO_ENTITY,
            entityKey = "entityKey", dtoKey = "dtoKey", context = "dtoToEntityKeyAfter")
   public Object dtoToEntityKeyAfter(Object dto, Object extra) {
       // notice the return null. This is deliberate to clear confusion
       // With "after" mode the return value of method is never returned
       // A new instance of Entity is constructed (using bean factory), then
       // populated from dto and returned.
       return null;
   }

   @Transferable(before = Direction.DTO_TO_ENTITY, after = Direction.ENTITY_TO_DTO,
            entityKey = "entityKey", dtoKey = "dtoKey", dtoFilterKey = "filterKey", context = "dtoToEntityAndBackToDtoByFilter")
   public Object dtoToEntityAndBackToDtoByFilter(Object sourceD, Object targetE, Object extra) {
       // This example does both ways conversion with a new instance of dto object returned
       // from this method
       swapEntityValues(targetE);
       return null;
   }

}
  This example assumes that objects passed as dto's are valid instances of @Dto annotated or DSL classes.

Event listeners

So for each of the above examples (given the above spring config) event listeners will fire before and after each transfer from/to dto object and entity object.

  public class CountingEventListener implements DTOCountingEventListener, BeanNameAware {

   private static final Logger LOG = LoggerFactory.getLogger(CountingEventListener.class);

   private int count = 0;
   private String name;

   public void onEvent(final Object... context) {
       // Current implementation of DTOSupport provides the following context array:
       // context[0] = context string identifier (e.g. for dtoToEntityBeforeExact method above it will be "dtoToEntityBeforeExact")
       // context[1] = DTO object (e.g. for dtoToEntityBeforeExact method above it will be first argument)
       // context[2] = Entity object (e.g. for dtoToEntityBeforeExact method above it will be second argument)
       // context[3] = Exception value if an exception occurs during transfer (it is up to the event handler to re-throw it!!!)
       count++;
        LOG.debug(name + " is called " + count + " times");
   }

   public int getCount() {
       return count;
   }

   public void setBeanName(final String name) {
       this.name = name;
   }
}

Note on spring AOP

It is very important to understand how spring AOP works and that you cannot call this.dtoToEntityBeforeExact from within TestServiceImpl.

This is not a bug of GeDA, this is how spring AOP works.

One trick you can do - is to use spring lookup methods to invoke self from the spring container, which would bring the AOP proxy of the bean like so:

  public class TestServiceImpl ... {

    ...

    public void doIt(...) {
        ...
         getSelf().dtoToEntityBeforeExact()
        ...
    }

    public TestService getSelf() {
        return null;
    }

}
       <bean id="simpleTransferableService"
         class="com.inspiresoftware.lib.dto.geda.test.impl.TestServiceImpl">
              <lookup-method name="getSelf" bean="simpleTransferableService"/>
       </bean>

but make sure your beans are stateless (not that I would ever think that you would do such a thing with a service bean.

Tags:
Created by Vladyslav Pavlov on 2018/03/20 09:04
    
GeDA - Generic DTO Assembler © Denys Pavlov 2009 - 2019
v.1.0.0