Spring Data JPA & Spring 3 Transactions Through JTA and Atomikos

In this article I will discuss about Spring Transactions with JTA.

The underlying JTA implementation with Atomikos.

The server platform is jBoss7.

The XA-DataSource configured in jBoss AS7 is of the type Oracle 10g.

The Underlyimg Persistence Provider in Hibernate 4.

I have used the Hibernate Module already available with jBoass AS7, but during

Startup of my application I faced some issues for which i have to

Externally plug in the jar "hibernate-core-3.6.0.Final.jar", Tried to

find out the reason behind but remain undone.

Now before going into the discussion, I will first try note down the jars that

I have used, as this portion has Given me Immense pain due to

lack of proper Documentation.

jar List

---------------------------------

1) hibernate-core-3.6.0.Final.jar

2) transactions-jta-3.6.4.jar

3) atomikos-util-3.6.4.jar

4) transactions-3.6.4.jar

5) transactions-api-3.6.4.jar

6) transactions-hibernate3-3.6.4.jar

7) transactions-jdbc-3.6.4.jar

8) concurrent-1.3.4.jar

9) cglib-nodep-2.2.3.jar

--------------------------------

Now apart from the above mentioned jars, the jars required to up and run a

Spring application is also required to be taken care of.

Please note: Various custom JTA properties can be set through Atomikos with the help of a property file

known as jta.properties.

But I jave not used it as I have gone with the default properties.

If anybody wishes to use it Proper documentation is alreay there at Atomikos Site.

During Startup an error will crop p saying that the file jta.properties is

not available in the classpath, which can be ignored.

First I will discuss about the configuration part and the issues that I have faced.

Before going into the aforementioned details of the previously mentioned post, lets dive into

the Spring-Data-JPA and Spring-Data-Commons API which I have used for JPA implementation in Spring.

The basic Idea behind Spring-Data-JPA and Spring-Data-Commons implementation, is to keep aside all the boilerplate JPA

implementation code and provide a clean interface to the common DAO methods, in other

word we can say that we can bid goodbye on the DAO layer.

In addition to the common DAO methods which are provided by the API by default custom DATA methods as per

business requirements can be defined very easily without any added overhead, Not only this, another feature of

these api which seems to be very interesting to me is the JPA Criteria API.

The main catch behind this API is the java representation of any type of SQL query (Simple.... Complex.....)

any type. Further the Queries can be made type safe with Java MetaModel classes.

Metamodel classes represents the Domain classes (Entity classes... i.e. classes representing

tables in underlying databases) along with their state and relationship with the other Domain entities.

These metamodel classes can be generated very easily with the help of Ant Task.

All the above mentioned features, Which I have implemented at a very Basic level, with Custom

configuration which can be very helpful in Real Time projects will be Discussed next.

For Furthur Information and all the API related details Spring-Data-JPA and Spring-Data-Commons

documentation should be consulted. They are very much precise,accurate and easy to implement.

So lets, start Now.At the heart of the Spring-Data-JPA lies the Repository implementation,

which contains all the Data Manupulation workarounds.

I have implemented a Generic Repository interface which will be implemented by Domain Specific Repositories,

So that all the Data Access methods lies within the Repository and hence can be accessed from that, including Custom

methods if required.

A bit confusing......... So lets go with the examples to make things a bit Bright.

The Generic Repository used is:


@NoRepositoryBean
public interface SpringrestRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {

 /*
  * Basic Entity Persistent Specific Methods Declaration
  */
 
 public void testCustomtoAllRepo();
 
}

See that our Generic Repository extends JpaRepository which contains all generic (By Default)the Data Access and Manipulative methods

(can be verified from the API documentation).

Here T represents the Domain Entity class and

ID represents the Primary Key (Simple or Composite) of the corresponding

Entity class.

The Custom method method for example which I have used here is:testCustomtoAllRepo()

The annotation @NoRepositoryBean has been used here so that no Repository

corresponding to this Bean should be generated by the API as this should be the Base

Repository all our Domain Entity Specific Repository.

This would become more clear as we Progress.

Lets us consider and Domain Entity Car.

  @Entity
 @Table(name="car")

public class Car implements Serializable {
 @SequenceGenerator(name="car_seq", sequenceName="car_seq")
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO,generator="car_seq")
 @Column(name="ID")
 private Long id;
 
 @Column(name="LICENSEPLATE")
 private String licensePlate;

 @Column(name="MANUFACTURER")
 private String manufacturer;
 
 @Column(name="MANUFACTUREDATE")
 private Date manufactureDate;
 
 @Column(name="AGE")
 private int age;
 
 @Version
 private int version;
 
 /*@OneToOne
 @JoinColumn(name="owner_id")
 private CAR_OWNER carOWNER;*/
 
 public Long getId() {
  return id;
 }
 
 
 
 public void setId(Long id) {
  this.id = id;
 }



 
 public String getLicensePlate() {
  return licensePlate;
 }



 public void setLicensePlate(String licensePlate) {
  this.licensePlate = licensePlate;
 }
 
 
 
 
 
 public String getManufacturer() {
  return manufacturer;
 }
 
 public Date getManufactureDate() {
  return manufactureDate;
 }
 
 
 public int getAge() {
  return age;
 }
 
 public void setAge(int age)
 {
  this.age = age;
 }
 
 public int getVersion() {
  return version;
 }
 
 
 



 public void setManufacturer(String manufacturer) {
  this.manufacturer = manufacturer;
 }



 public void setManufactureDate(Date manufactureDate) {
  this.manufactureDate = manufactureDate;
 }



 public void setVersion(int version) {
  this.version = version;
 }

 

 /*public CAR_OWNER getCarOWNER() {
  return carOWNER;
 }



 public void setCarOWNER(CAR_OWNER carOWNER) {
  this.carOWNER = carOWNER;
 }
*/


 public String toString()
 {
  return "ID:"+id+" licensePlate:"+licensePlate+"  manufacturer:"+manufacturer+" manufactureDate"+manufactureDate+" age:"+age+" version:"+version/*+"  Car Owner:"+carOWNER*/;
 }
 
 /*public String toString()
 {
  return "The car is:"+id+"---> "+licensePlate;
 }*/
 
}

  
 
The Repository corresponding to the Above Domain Entity is:
 public interface CarRepository extends  SpringrestRepository{

 }
 
 
See Our CarRepository extends the base Repository ---- SpringrestRepository

So this is in Short the Repository skeleton Structure, and here is only the interfaces

where lies the implementation?

For Custom configuration we would provide the implementation to the interface

and the have API engine to generate the Implementation Bean object and use this object to

access the repository specific methods.

Again a bit confusing and believe me We will clarify it soon as progress.

The Implementation Bean is:

 
 public class SpringrestRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T,ID> implements SpringrestRepository<T, ID> {

 private  JpaEntityInformation<T, ?> entityInformation ;
 private EntityManager em;
 private  SpringRESTPersistenceProvider provider ;
 private MyLogger myLogger = MyLogFactory.getLogger(this.getClass().getName());
 
 private  Class<?> springDataRepositoryInterface;
 public Class<?> getSpringDataRepositoryInterface() {
 return springDataRepositoryInterface;
 }
  
 public void setSpringDataRepositoryInterface(
 Class<?> springDataRepositoryInterface) {
 this.springDataRepositoryInterface = springDataRepositoryInterface;
 }
 
 public SpringrestRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager,Class<?> repositoryInterface)
 {
  
  super(entityInformation,entityManager);
  //Assert.notNull(entityInformation);
     //Assert.notNull(entityManager);
     this.entityInformation = entityInformation;
     this.em = entityManager;
     this.provider = SpringRESTPersistenceProvider.fromEntityManager(entityManager);
     this.springDataRepositoryInterface = repositoryInterface;
     myLogger.log(ILoggerLevel.DEBUG, " *** In SpringRESTRepositoryImpl Cons *** repositoryInterface:"+repositoryInterface);
  
 }
 
 
 public SpringrestRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
  super(domainClass, entityManager);
  // This is the recommended method for accessing inherited class dependencies.
  this.em = entityManager;
 }
  private Class<T> getDomainClass()
   {
     return this.entityInformation.getJavaType();
   }
 
  
 
  


 @Override
 public void testCustomtoAllRepo() {
  // TODO Auto-generated method stub
  myLogger.log(ILoggerLevel.DEBUG, " *** testCustomtoAllRepo() is on Way *** ");
  
 }
  
  


 
 
}

 
 
As said before SimpleJpaRepository class contains the default Data Access Methods.

So, From the Implementation Class, things are Very little clear now.

Now How the API engine Knows that the Domain Specific Repository interface

would use this Implementation?????

This can be done through custom Factory Bean and Factory classes and these are configured

in Spring-jpa.xml i.e. configuration file.

The Factory Bean class is:

 
 public class SpringrestRepositoryFactoryBeanltT extends JpaRepository<S, ID>, S, ID extends Serializable> extends  JpaRepositoryFactoryBean<T, S, ID>  {

 @PersistenceContext
 private EntityManager entityManager;
 private MyLogger myLogger = MyLogFactory.getLogger(this.getClass().getName());
 
 
  
 
 @Override
 public void afterPropertiesSet() /*throws Exception*/ {
  // TODO Auto-generated method stub
  myLogger.log(ILoggerLevel.DEBUG, " *** In afterPropertiesSet() of:"+this.getClass().getName());
  Assert.notNull(this.entityManager, "EntityManager must not be null!");
     super.afterPropertiesSet();
  
 }
 
  protected RepositoryFactorySupport doCreateRepositoryFactory()
   {
  myLogger.log(ILoggerLevel.DEBUG, " *** In doCreateRepositoryFactory():"+this.getClass().getName());
     return createRepositoryFactory(this.entityManager);
   }

 

 protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)
   {
    myLogger.log(ILoggerLevel.DEBUG, " *** In createRepositoryFactory() :"+this.getClass().getName());
    //entityManager = this.entityManager;
    myLogger.log(ILoggerLevel.DEBUG, "entityManager:"+entityManager);
    return new SpringrestRepositoryFactory(entityManager);
   }
 
 

}

 
 
And the Factory Bean class is:

 
 public class SpringrestRepositoryFactory<S,ID extends Serializable> extends JpaRepositoryFactory {

 private EntityManager entityManager;
 private  QueryExtractor extractor;
 private MyLogger myLogger = MyLogFactory.getLogger(this.getClass().getName());
 public SpringrestRepositoryFactory(EntityManager entityManager)
 {
  super(entityManager);
  this.entityManager = entityManager;
  Assert.notNull(this.entityManager, " Entity Manager in SpringRESTRepositoryFactory must not be NULL ");
  
  extractor = SpringRESTPersistenceProvider.fromEntityManager(entityManager);
 }
 
 

 @Override
 protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata ) {
  // TODO Auto-generated method stub
  myLogger.log(ILoggerLevel.DEBUG, " *** In getRepositoryBaseClass() :"+this.getClass().getName());
  getRepositoryMetaDataInfo(repositoryMetadata);
  return SpringrestRepository.class;
 }

 @Override
 protected Object getTargetRepository(RepositoryMetadata repositoryMetadata) {
  // TODO Auto-generated method stub
  myLogger.log(ILoggerLevel.DEBUG, " *** In getTargetRepository() :"+this.getClass().getName());
  getRepositoryMetaDataInfo(repositoryMetadata);
  return getTargetRepository(repositoryMetadata, this.entityManager);
 
 }
 
  protected <T, ID extends Serializable> JpaRepository<?, ?> getTargetRepository(RepositoryMetadata repositoryMetadata, EntityManager entityManager)
   {
   
   myLogger.log(ILoggerLevel.DEBUG, " *** In  getTargetRepository():"+this.getClass().getName()); 
   getRepositoryMetaDataInfo(repositoryMetadata);
     Class repositoryInterface = repositoryMetadata.getRepositoryInterface();
     JpaEntityInformation entityInformation = getEntityInformation(repositoryMetadata.getDomainType());
     getEntityInformation(entityInformation);
    
     return new SpringrestRepositoryImpl(entityInformation, entityManager,repositoryInterface);
   }
  
  @Override
  public <T,ID extends Serializable> JpaEntityInformation<T,ID> getEntityInformation(Class<T> domainClass)
   {
   myLogger.log(ILoggerLevel.DEBUG, " *** In  JpaEntityInformation():"+this.getClass().getName()); 
     return  (JpaEntityInformation<T, ID>) JpaEntityInformationSupport.getMetadata(domainClass, this.entityManager);
   }
  
  
  private void getRepositoryMetaDataInfo(RepositoryMetadata repositoryMetadata)
  {
   myLogger.log(ILoggerLevel.DEBUG, repositoryMetadata.getDomainType()+"-->"+repositoryMetadata.getIdType()+" --->"+repositoryMetadata.getRepositoryInterface());
  }

  
  private void getEntityInformation(JpaEntityInformation entityInformation)
  {
   myLogger.log(ILoggerLevel.DEBUG,entityInformation.getClass()+"-->"+entityInformation.getIdType()+"--->"+entityInformation.getJavaType());
  }
}

 
 
 
So the Flow can be described as, The Factory Bean which is being Configured in the .xml file

Generated the Factory and the Factory finally creates an object of the Repository Implmentation Bean.

Any Custom methods if required can be defined here and hence is avilable application wide.

Cool Isn't it.

For Spring Spring JPA we are using the JPA namespace in the configuration file.

The configuration related details are:

 
   <bean id="entityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" abstract="true">
     
     <property name="jpaVendorAdapter">
     
     
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
       <!--  property name="showSql" value="true" />
    <property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" /-->
      </bean>
     </property>
  
  <property name="packagesToScan" value="com.springRest.domainEntity"></property>
     <property name="jpaProperties">
      <props>
        <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
     <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
     <prop key="hibernate.max_fetch_depth">3</prop>    
     <prop key="hibernate.jdbc.fetch_size">50</prop>    
     <prop key="hibernate.jdbc.batch_size">10</prop>    
     <prop key="hibernate.show_sql">true</prop> 
     <!-- prop key="hibernate.format_sql">true</prop-->
     <!--  prop key="hibernate.current_session_context_class">jta</prop-->
     <prop key="javax.persistence.transactionType">jta</prop>
   </props>
     </property>
  
   <bean id="emfA" parent="entityManager">
     <property name="dataSource" ref="dataSourceBean"></property>
    </bean>
    
 
 <jpa:repositories  base-package="com.springRest.repo.repository" 
          entity-manager-factory-ref="emfA"
          transaction-manager-ref="transactionManager"
         factory-class="com.springRest.config.repository.SpringrestRepositoryFactoryBean"
         >
         
         
     </jpa:repositories>
  
    <context:component-scan base-package="com.springRest.service.impl"></context:component-scan>
    
 
Now, lets describe the JPA namespace Configuration

The base-package should contain all the domain Specific Repository.

entity-manager-factory-ref defines the entity amanger Factory to be used in the

Specific Repository Implementation.

and the factory-class is the Factory Bean implelemntation.

So till now things are little more clear.

Let me get all we have dicussed till Now through an example. which will clarify things more.

Lets us define a Service class containing domain specific Repository (CarRepository) REFERENCE.

The service classes beans are generated through auto detection thorough annotation and with the

help of component-scan base-package element as described in the above configuration,

i.e. all the service classes described in the package define in the component-scan base-package element.

The service class is:

  
  @Service("carService")
  @Repository
  @Transactional
public class ICarServiceImpl implements ICarService {

 
 /*
  * Here Service Layer is Implemented Through Spring Data Repository Concept.

*

* (Normal Entity Manager as fetched from Entity Manager Factory can also be used.)

*

*/ MyLogger logger = MyLogFactory.getLogger(this.getClass().getName()); @Autowired CarRepository carRepository; @Override @Transactional(readOnly=true) public List findAll() { // TODO Auto-generated method stub List carList = new ArrayList(); Iterable carIterable = carRepository.findAll(); Iterator carIterator = carIterable.iterator(); while(carIterator.hasNext()) { carList.add(carIterator.next()); } return carList; } @Override @Transactional public Car save(Car car) { logger.log(ILoggerLevel.DEBUG, " *** In save method of Service *** "); Car newCar = null; // TODO Auto-generated method stub try{ logger.log(ILoggerLevel.DEBUG," **** The car Repository is:"+carRepository+" The car is:"+car); newCar =carRepository.save(car); logger.log(ILoggerLevel.DEBUG," **** After Save is called ****"); } catch(Exception e) { logger.log(ILoggerLevel.ERROR,e); e.printStackTrace(); } //return carRepository.save(car); return newCar; } public CarRepository getCarRepository() { return carRepository; } public void setCarRepository(CarRepository carRepository) { this.carRepository = carRepository; } }
Lets describe the annotations as Bit:

@Service: This annotation describes this Bean to be a Service Layer Bean and registers the bean with the name as defined.

@Autowired

CarRepository carRepository: Spring DATA JPA Injects the proper implementation class (as DESCRIBED ABOVE) in the reference,

and with this reference we have preformed various methods, as evident from various methods.

So, till Now what we have dicussed, almost all the things are out of Shadow, regarding various Spring JPA Config and Usage.

So, the DAO layer are no more a requirement now, and we can thus proceed with Spring JPA.

Just Feel free to share any doubts with me, Just Post Your doubts...

I will try to answer ... and till then happy coding and feel the magic of Spring JPA and Spring DATA ;-)

It is really been long since I added up anything and I really apologize for that. Lets continue the above topic and bring it to conclusion.

Next interesting thing that I came across while going through Spring DATA JPA is the criteria API. In a single word it is the java representation of the SQL queries. Every bit of action that can be done through SQL queries is possible through it. This topic is really vast, lets feel some taste of it.

The first and foremost thing in CRITERIA API is the metamodel classes of Domain entity classes, which can be generated through a ANT task.

Say, The Domain entity class for CAR is:

 @Entity
@Table(name="car")

public class Car implements Serializable {
 @SequenceGenerator(name="car_seq", sequenceName="car_seq")
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO,generator="car_seq")
 @Column(name="ID")
 private Long id;
 
 @Column(name="LICENSEPLATE")
 private String licensePlate;

 @Column(name="MANUFACTURER")
 private String manufacturer;
 
 @Column(name="MANUFACTUREDATE")
 private Date manufactureDate;
 
 @Column(name="AGE")
 private int age;
 
 @Version
 private int version;
 
 /*@OneToOne
 @JoinColumn(name="owner_id")
 private CAR_OWNER carOWNER;*/
 
 public Long getId() {
  return id;
 }
 
 
 
 public void setId(Long id) {
  this.id = id;
 }



 
 public String getLicensePlate() {
  return licensePlate;
 }



 public void setLicensePlate(String licensePlate) {
  this.licensePlate = licensePlate;
 }
 
 
 
 
 
 public String getManufacturer() {
  return manufacturer;
 }
 
 public Date getManufactureDate() {
  return manufactureDate;
 }
 
 
 public int getAge() {
  return age;
 }
 
 public void setAge(int age)
 {
  this.age = age;
 }
 
 public int getVersion() {
  return version;
 }
 
 
 



 public void setManufacturer(String manufacturer) {
  this.manufacturer = manufacturer;
 }



 public void setManufactureDate(Date manufactureDate) {
  this.manufactureDate = manufactureDate;
 }



 public void setVersion(int version) {
  this.version = version;
 }

 

 /*public CAR_OWNER getCarOWNER() {
  return carOWNER;
 }



 public void setCarOWNER(CAR_OWNER carOWNER) {
  this.carOWNER = carOWNER;
 }
*/


 public String toString()
 {
  return "ID:"+id+" licensePlate:"+licensePlate+"  manufacturer:"+manufacturer+" manufactureDate"+manufactureDate+" age:"+age+" version:"+version/*+"  Car Owner:"+carOWNER*/;
 }
 
 /*public String toString()
 {
  return "The car is:"+id+"---> "+licensePlate;
 }*/
 
}

It's MetaModel representation is:


@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Car.class)
public abstract  class Car_ {

 public static volatile SingularAttribute id;
 public static volatile SingularAttribute manufacturer;
 public static volatile SingularAttribute age;
 public static volatile SingularAttribute manufactureDate;
 public static volatile SingularAttribute licensePlate;
 public static volatile SingularAttribute version;

}

The Metamodel class "Car_" is generated through the ANT task:

<target name="CREATE-METAMODEL">

<copyfile dest="F:/My_prog/Spring Workspaces/SpringREST/AntLib" src="C:/Users/hcl/Desktop/Spring Related/hibernate-jpamodelgen-1.3.0.Final-dist/hibernate-jpamodelgen-1.3.0.Final/hibernate-jpamodelgen-1.3.0.Final.jar"/>

<javac srcdir="${src}/com/springRest/domainEntity" destdir="${src}/metamodel" failonerror="false" fork="true">

<classpath refid="classpath"/>

<compilerarg value="-proc:only"/>

</javac>

Now using criteria query we are going to query for all cars having owners.

The foreign key in Car_Owner is id of Car.


public String fetchAllCarData()
 {
  try{
  
  int i =1;
  List carList = carService.findAll();
  for(Car car : carList)
  {
   myLogger.log(ILoggerLevel.DEBUG,i+": "+car );
  }
  
  /*
  * First we have to create a CriteriaBuilder object, this object
  * is going to perform all the query building functions.
  */
  
  CriteriaBuilder cb = em.getCriteriaBuilder();
  
  myLogger.log(ILoggerLevel.DEBUG, " **** Criteria Query using Predicates and Static MetaModel - 123 **** ");
  
  /*
  * From CriteriaBuilder object we are creating an instance of CriteriaQuery with
  * expected result Type as the Type Parameter.
  * The expected result type is CarDesc, custom java bean
  * Any custom java type when expected as a result type of any query
  * should conform to java beans spefication
  */
  
  CriteriaQuery criteriaQuery = cb.createQuery(CarDesc.class);
  
  /*
  * Next we are going to create indivudual roots on which
  * join is to be performed.
  */
  
  Root car = criteriaQuery.from(Car.class);
  Root carOwner = criteriaQuery.from(Car_Owner.class);
  
  /*
  * Creating Metamodel instance
  */
  
  Metamodel metamodel = em.getMetamodel();
  
  /*
  * Join is being performed and a Predicate object is created
  */
  
  
  Predicate predicate1 = cb.equal(car.get(Car_.id), carOwner.get(Car_Owner_.carOwnerPK).get(Car_Owner_PK_.carId));
  
  
   * 
   * While mapping Query results to Result Domain Objects,  
   * Provide appropiate constructor, for Initialization
   * 
   
  
  criteriaQuery.select(cb.construct(CarDesc.class, car.get("licensePlate"),car.get("manufacturer")));//.where(cb.equal(car.get("id"), carOwner.get("carOwnerPK").get("carId")));
  
  
   * Predicates ar appended using And, we can use Or or other logical
   * Operators in case of Multiple Predicates.
   * 
   * In case of Multiple Predicates all the predicates, that are
   * to be logically grouped can be kept in a List Structure,
   * and then AND or OR operators can be applied
   * 
   
  
  criteriaQuery.where(cb.and(predicate1));
  
  /*
  * Now a TypedQuery instance is being created by executing the CriteriaQuery 
  * object as created in above steps and finally result set is fetched
  * from TypedQuery instance
  */
  
  TypedQuery query =  em.createQuery(criteriaQuery);
  List carList = query.getResultList();
  for(CarDesc obj:carList)
  {
   myLogger.log(ILoggerLevel.DEBUG, " The Data in Domain Form:"+obj);
  }
   
  return "JPASuccess";
  }
  catch(Exception e)
  {
   myLogger.log(ILoggerLevel.ERROR, e);
   return null;
  }
 }
 
 
In the above method we are have tried to fetch the car details having car owners.

The car details are encapsulated in custom java bean "CarDesc".

This is in very short a glimpse of the Criteria API, please go through its documentation , It's really nice and interesting.

Comments

Popular posts from this blog

Use of @Configurable annotation.

Spring WS - Part 5

Spring WS - Part 4