Sunday, June 24, 2012

Multi Module Maven Project with Spring, JPA/ Hibernate and Tapestry

Overview

Today I am going to show you how to setup a Maven Project with several modules including Spring Context configuration in conjunction with Tapestry 5 and Hibernate.

Agenda

  1. Setup Maven Parent Project
  2. Create the UI Application Project
  3. Create the Service Modules
  4. Create the Repository Modules
  5. Spring contexts from repositories, over services to the application ui
  6. Test the application
  7. Review
  8. Pitfalls
  9. Project Download




The following technology will be used:

  • Eclipse Indigo JEE 3.7 SR2
  • Maven 3
  • Spring 3.1.0.RELEASE
  • Spring Data 1.1.0.RELEASE
  • Tapestry 5.3.3
  • Hibernate 4.0.1.Final
  • HSQLDB 2.2.8
  • TestNG 5.14.9
  • Jetty 7.6.4 (embedded)


The following steps have to be taken in order to get a fully functional and ready to start coding Project:

  • Create Parent Maven Project with used dependencies
  • Create UI Project where we will use Tapestry as Frontend Framework
  • Create Services fpr repository access
  • Create Repositories for Datasource Access
  • Check dependencies
  • Create Spring Application Contexts
  • Connect UI with Services
  • Build Tests for Spring and UNit Test for Tapestry Page

This will be the final project structure:



Let's start!

1. Setup Maven Parent Project


At first we have to define a parent project with all needed dependencies for the child projects. It will look like the following content from the pom.xml:

Source file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.
     xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>de.boehme.app</groupId>
     <artifactId>mm-base-parent</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>mm-base Module Reactor</name>
     <description>Spring, Tapestry, MongoDB or MySQL</description>
     <modules>
          <module>application/mm-ui</module>
          <module>services/mm-data-service</module>
          <module>services/mm-user-service</module>
          <module>repositories/mm-data-repository</module>
          <module>repositories/mm-user-repository</module>
     </modules>

     <dependencyManagement>
          <dependencies>

               <!-- Too set up an application with a database, change the artifactId 
                    below to tapestry-hibernate, and add a dependency on your JDBC driver. You'll 
                    also need to add Hibernate configuration files, such as hibernate.cfg.xml. -->
               <dependency>
                    <groupId>org.apache.tapestry</groupId>
                    <artifactId>tapestry-core</artifactId>
                    <version>${tapestry-release-version}</version>
               </dependency>

               <!-- This adds automatic compression of JavaScript and CSS when in production 
                    mode. -->
               <dependency>
                    <groupId>org.apache.tapestry</groupId>
                    <artifactId>tapestry-yuicompressor</artifactId>
                    <version>${tapestry-release-version}</version>
               </dependency>

               <!-- Uncomment this to add support for file uploads: <dependency> <groupId>org.apache.
               tapestry</groupId> 
                    <artifactId>tapestry-upload</artifactId> <version>${tapestry-release-version}<
                    /version> 
                    </dependency> -->

               <!-- A dependency on either JUnit or TestNG is required, or the surefire 
                    plugin (which runs the tests) will fail, preventing Maven from packaging 
                    the WAR. Tapestry includes a large number of testing facilities designed 
                    for use with TestNG (http://testng.org/), so it's recommended. -->
               <dependency>
                    <groupId>org.testng</groupId>
                    <artifactId>testng</artifactId>
                    <version>${testng-release-version}</version>
                    <scope>test</scope>
               </dependency>

               <dependency>
                    <groupId>org.apache.tapestry</groupId>
                    <artifactId>tapestry-test</artifactId>
                    <version>${tapestry-release-version}</version>
                    <scope>test</scope>
               </dependency>

               <!-- Provided by the servlet container, but sometimes referenced in the 
                    application code. -->
               <dependency>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                    <version>${servlet-api-release-version}</version>
                    <scope>provided</scope>
               </dependency>

               <!-- Provide dependency to the Tapestry javadoc taglet which replaces 
                    the Maven component report -->
               <dependency>
                    <groupId>org.apache.tapestry</groupId>
                    <artifactId>tapestry-javadoc</artifactId>
                    <version>${tapestry-release-version}</version>
                    <scope>provided</scope>
               </dependency>

               <!-- mm-dependencies -->

               <dependency>
                    <groupId>de.boehme.service</groupId>
                    <artifactId>mm-data-service</artifactId>
                    <version>${mm-project-version}</version>
               </dependency>

               <dependency>
                    <groupId>de.boehme.service</groupId>
                    <artifactId>mm-user-service</artifactId>
                    <version>${mm-project-version}</version>
               </dependency>

               <dependency>
                    <groupId>de.boehme.repository</groupId>
                    <artifactId>mm-data-repository</artifactId>
                    <version>${mm-project-version}</version>
               </dependency>

               <dependency>
                    <groupId>de.boehme.repository</groupId>
                    <artifactId>mm-user-repository</artifactId>
                    <version>${mm-project-version}</version>
               </dependency>

               <!-- SPRING -->

               <dependency>
                    <groupId>org.apache.tapestry</groupId>
                    <artifactId>tapestry-spring</artifactId>
                    <version>${tapestry-release-version}</version>
               </dependency>

               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-test</artifactId>
                    <version>${org.springframework.version}</version>
                    <scope>test</scope>
               </dependency>

               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-orm</artifactId>
                    <version>${org.springframework.version}</version>
                    <exclusions>
                         <exclusion>
                              <groupId>commons-logging</groupId>
                              <artifactId>commons-logging</artifactId>
                         </exclusion>
                    </exclusions>
               </dependency>

               <dependency>
                    <groupId>org.springframework.data</groupId>
                    <artifactId>spring-data-jpa</artifactId>
                    <version>${org.springframework.data.version}</version>
               </dependency>

               <!-- DB Dependencies -->

               <dependency>
                    <groupId>org.hsqldb</groupId>
                    <artifactId>hsqldb</artifactId>
                    <version>${hsqldb.version}</version>
               </dependency>

               <dependency>
                    <groupId>org.hibernate</groupId>
                    <artifactId>hibernate-entitymanager</artifactId>
                    <version>
                    ${org.hibernate.hibernate-entitymanager.version}
                </version>
               </dependency>

          </dependencies>
     </dependencyManagement>

     <build>
          <pluginManagement>
               <plugins>
                    <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-compiler-plugin</artifactId>
                         <configuration>
                              <source>1.6</source>
                              <target>1.6</target>
                         </configuration>
                    </plugin>
               </plugins>
          </pluginManagement>

          <resources>
               <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
               </resource>
          </resources>
     </build>

     <repositories>

          <repository>
               <id>codehaus.snapshots</id>
               <url>http://snapshots.repository.codehaus.org</url>
          </repository>
          <repository>
               <id>OpenQA_Release</id>
               <name>OpenQA Release Repository</name>
               <url>http://archiva.openqa.org/repository/releases/</url>
          </repository>

          <!-- This repository is only needed when the Tapestry version is a preview 
               release, rather than a final release. -->
          <repository>
               <id>apache-staging</id>
               <url>https://repository.apache.org/content/groups/staging/</url>
          </repository>
     </repositories>

     <properties>
          <tapestry-release-version>5.3.3</tapestry-release-version>
          <servlet-api-release-version>2.5</servlet-api-release-version>
          <testng-release-version>5.14.9</testng-release-version>
          <easymock-release-version>3.0</easymock-release-version>
          <jetty-version>7.6.4.v20120524</jetty-version>
          <mm-project-version>0.0.1-SNAPSHOT</mm-project-version>
          <org.springframework.version>3.1.0.RELEASE</org.springframework.version>
          <org.springframework.data.version>1.1.0.RELEASE</org.springframework.data.version>

          <hsqldb.version>2.2.8</hsqldb.version>
          <org.hibernate.hibernate-entitymanager.version>4.0.1.Final</org.hibernate.hibernate-
          entitymanager.version>
     </properties>

</project>

Now we can add some directories for our ui application, the services and the repositories. Review the project structure image from the beginning of this post.

2. Create the UI Application Project


Now we can add ther first module to our project. This will be the application containing the UI Components. Therefore we create a new Maven Project from an archetype, called org.apache.tapestry quickstart for version 5.3.3. If you dont find this archetype in your catalogs you have to define a new remote catalog. (Go to Eclipse-->Window --> Preferences --> Maven --> Archetypes --> Add remote catalog). The address to be used is http://tapestry.apache.org .


The POM will look like the following:

Source file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
          <relativePath>../../pom.xml</relativePath>
          <artifactId>mm-base-parent</artifactId>
          <groupId>de.boehme.app</groupId>
          <version>0.0.1-SNAPSHOT</version>
     </parent>

     <artifactId>mm-ui</artifactId>
     <packaging>war</packaging>
     <name>mm-ui Tapestry 5 Application</name>
     <dependencies>
          <!-- Too set up an application with a database, change the artifactId below 
               to tapestry-hibernate, and add a dependency on your JDBC driver. You'll also 
               need to add Hibernate configuration files, such as hibernate.cfg.xml. -->
          <dependency>
               <groupId>org.apache.tapestry</groupId>
               <artifactId>tapestry-core</artifactId>
          </dependency>

          <dependency>
               <groupId>org.testng</groupId>
               <artifactId>testng</artifactId>
               <scope>test</scope>
          </dependency>

          <dependency>
               <groupId>org.apache.tapestry</groupId>
               <artifactId>tapestry-test</artifactId>
               <scope>test</scope>
          </dependency>

          <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-test</artifactId>
               <scope>test</scope>
          </dependency>

          <!-- Provided by the servlet container, but sometimes referenced in the 
               application code. -->
          <dependency>
               <groupId>javax.servlet</groupId>
               <artifactId>servlet-api</artifactId>
               <scope>provided</scope>
          </dependency>

          <!-- mm-dependencies -->
          <dependency>
               <groupId>de.boehme.service</groupId>
               <artifactId>mm-data-service</artifactId>
          </dependency>
          
          <dependency>
               <groupId>de.boehme.service</groupId>
               <artifactId>mm-user-service</artifactId>
          </dependency>

          <!-- SPRING-->
          <dependency>
               <groupId>org.apache.tapestry</groupId>
               <artifactId>tapestry-spring</artifactId>
          </dependency>

     </dependencies>
     <build>
          <finalName>mm-ui</finalName>
          <plugins>
               <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.7.2</version>
                    <configuration>
                         <systemPropertyVariables>
                              <tapestry.execution-mode>Qa</tapestry.execution-mode>
                         </systemPropertyVariables>
                    </configuration>
               </plugin>
               <!-- Run the application using "mvn jetty:run" -->
               <plugin>
                    <groupId>org.mortbay.jetty</groupId>
                    <artifactId>jetty-maven-plugin</artifactId>
                    <version>${jetty-version}</version>
                    <configuration>
                         <connectors>
                              <connector implementation="org.eclipse.jetty.server.nio.
                              SelectChannelConnector">
                                   <port>8080</port>
                              </connector>
                         </connectors>
                         <systemProperties>
                              <systemProperty>
                                   <name>tapestry.execution-mode</name>
                                   <value>development</value>
                              </systemProperty>
                         </systemProperties>
                    </configuration>
               </plugin>
          </plugins>

     </build>

</project>

The important things to notice are that we will use tapestry core, TestNG and spring-test dependencies for integration and unit testing. Furthermore we defined the dependencies for our own service modules, which we will create now. To start the whole application a jetty plugin delivers an embedded instance of the Jetty Servlet Container. This is very useful as you can start the server from within Eclipse with just a simple maven goal jetty:run.

3. Create the Service Modules


Next step will be the adding of our service modules. The DataService is just a template for other services and should give a simple idea how to create and use services in layered architecture projects. The real implementation represents the User Service which will be shown more in detail:

The POM file looks like the following:

Source file
<?xml version="1.0"?>
<project
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.
     xsd"
     xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <modelVersion>4.0.0</modelVersion>
     <parent>
          <relativePath>../../pom.xml</relativePath>
          <artifactId>mm-base-parent</artifactId>
          <groupId>de.boehme.app</groupId>
          <version>0.0.1-SNAPSHOT</version>
     </parent>
     <groupId>de.boehme.service</groupId>
     <artifactId>mm-user-service</artifactId>
     <name>mm-user-service</name>
     <url>http://maven.apache.org</url>
     <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <dependencies>
          <dependency>
               <groupId>de.boehme.repository</groupId>
               <artifactId>mm-user-repository</artifactId>
          </dependency>

          <dependency>
               <groupId>org.testng</groupId>
               <artifactId>testng</artifactId>
               <scope>test</scope>
          </dependency>

          <dependency>
               <groupId>org.apache.tapestry</groupId>
               <artifactId>tapestry-spring</artifactId>
          </dependency>
     </dependencies>
</project>

The service should only "know" the repository as dependency. Of course we need spring which comes  through the tapestry-spring dependency.

To use this service we have to define an API Interface and the corresponding implementation class. The main purpose of the user service is to create User and retrieve all User from a database.

User.java Model:

Source file
@Entity
@Table(name="USER")
public class User implements Serializable{
     
     /**
      * 
      */
     private static final long serialVersionUID = 4416477374601590502L;
     
     public User() {
     }
     public User(String firstName, String lastName) {
          this.firstName = firstName;
          this.lastName = lastName;
     }
     @Id
     @GeneratedValue
     private Long id;
     
     @Column(name="FIRSTNAME")
     private String firstName;
     @Column(name="LASTNAME")
     private String lastName;

The User Model basically consists of an Id and a First- and Lastname attribute.

API Interface IUserService.java

Source file
public interface IUserService {
     
     void createUser(User user);
     
     List<User> getAllUser();
}

The interface defines two methods. One for storing User into the database and one for retrieving all saved Users.

Implementation Class UserService.java

Source file
@Service
public class UserService implements IUserService{
     
     @Autowired
     private UserRepository repository;

     @Override
     public void createUser(User user) {
          repository.save(user);
     }

     @Override
     public List<User> getAllUser() {
          return repository.findAll();
     }
}

The implementing class is marked with the @Service annotation. This is needed in order to let the UI find this class as a bean. To actually save User into the db we acquire access to the UserRepository via injecting it here.

4. Create the Repository Modules

The repository modules are used to directly access datasources (databases etc.). As we also have here a template repository called DataRepository we will conecntrate on a real implementation the UserRepository.


The UserRepository has some dependencies to hibernate, spring-data, spring-orm and a hsqldb. The POM looks like the following:

Source file
<?xml version="1.0"?>
<project
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.
     xsd"
     xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <modelVersion>4.0.0</modelVersion>
     <parent>
          <relativePath>../../pom.xml</relativePath>
          <artifactId>mm-base-parent</artifactId>
          <groupId>de.boehme.app</groupId>
          <version>0.0.1-SNAPSHOT</version>
     </parent>
     <groupId>de.boehme.repository</groupId>
     <artifactId>mm-user-repository</artifactId>
     <name>mm-user-repository</name>
     <url>http://maven.apache.org</url>
     <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <dependencies>
          <dependency>
               <groupId>org.testng</groupId>
               <artifactId>testng</artifactId>
               <scope>test</scope>
          </dependency>

          <dependency>
               <groupId>org.apache.tapestry</groupId>
               <artifactId>tapestry-spring</artifactId>
          </dependency>

          <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-orm</artifactId>
               <scope>compile</scope>
          </dependency>

          <dependency>
               <groupId>org.springframework.data</groupId>
               <artifactId>spring-data-jpa</artifactId>
               <scope>compile</scope>
          </dependency>

          <dependency>
               <groupId>org.hibernate</groupId>
               <artifactId>hibernate-entitymanager</artifactId>
               <scope>compile</scope>
          </dependency>

          <dependency>
               <groupId>org.hsqldb</groupId>
               <artifactId>hsqldb</artifactId>
          </dependency>
     </dependencies>
</project>

We will take advantage of an embedded hsqldb, that delivers us the needs to start coding without thinking of setting up database environments etc.

Through the usage of spring-data we can just define an interface UserRepository.java that extends JpaRepository which in turn delivers us at runtime all needed CRUD operations.

Source file
@Repository
public interface UserRepository extends JpaRepository<User, Long>{

}

The interface is marked with the @Repository stereotype annotation from Spring, so that it is recognized as bean and we can inject it into other components like our UserService seen before.

Now that we have all different parts we can wire up the projects with spring configurations.

5. Spring contexts from repositories, over services to the application ui


The repository need to access a datasource. Therefore we have to define our so called jpa-context.xml.

Source file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:xsi="http://www.w3.
     org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.0.xsd 
     http://www.springframework.org/schema/jdbc 
     http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.
     xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     ">

     <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
          <property name="packagesToScan">
               <list>
                    <value>de/boehme/repository/*/model/</value>
               </list>

          </property>
          <property name="dataSource" ref="dataSource" />
          <property name="jpaVendorAdapter">
               <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                    <property name="showSql" value="true" />
                    <property name="generateDdl" value="true" />
                    <property name="database" value="HSQL" />
               </bean>
          </property>
          <property name="jpaProperties">
               <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                    <prop key="hibernate.hbm2ddl.auto">create</prop>
                    <prop key="hibernate.format_sql">true</prop>
               </props>
          </property>
     </bean>
     <jdbc:embedded-database id="dataSource" type="HSQL" />

     <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

     <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager">
     </bean>

</beans>

As you can see we use an embedded HSQL db. That means we create a new in-memory databse at each application context start-up. We also define an EntityManagerFactory, that should search for all our models (currently only one: User Model) under a given package via the property "packagesToScan". The automatic generation of then entities is set via the jpa property "hibernate.hbm2ddl.auto". The Transaction Manager is mandatory, although not used in this applciation.


To use the features of spring-data we have to define another context file that searches our packages for repositories. 


repositiories.xml:

Source file
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.
     org/schema/data/jpa"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
    http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
    ">

     <!-- Spring Data -->
     <repositories base-package="de.boehme.repository.user" />
</beans:beans>


Putting it all together we define a general repo-context.xml file that imports the repositories.xml and jpa-context.xml files which can be imported by the UserService later.

Source file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

     <context:component-scan base-package="de.boehme.repository.user" />
     
     <import resource="classpath:/de/boehme/repository/user/repositories.xml"/>
     <import resource="classpath:/de/boehme/repository/user/jpa-context.xml"/>
</beans>

Service spring context file

The service basically just tells spring to look for annotated class under the given package. It also imports the spring context we earlier defined in the repository.

Source file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

     <context:component-scan base-package="de.boehme.service.user" />
     
     <import resource="classpath:/de/boehme/repository/user/repo-context.xml"/>

</beans>

Application UI spring context


The application needs to get a reference to the UserService therefore we have to import the service-context. As we are using an embedded database we like to have data ready to be used at startup of the application. So we need to define here the context:component-scan that searches under the given package for our ApplicationContextListener (you can look into the attached sources for more details). Therefore we will use a special bean that listens for the spring context and inserts two Users into the database for us.

Source file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
           
           <context:component-scan base-package="de.boehme.app.ui" />
           
           <import resource="classpath:/de/boehme/service/user/service-context.xml"/>
           <import resource="classpath:/de/boehme/service/data/service-context.xml"/>
<!--            <import resource="classpath:/de/boehme/app/ui/spring-profiles.xml"/> -->
</beans>

Now we are ready to use the services and start coding a simple Overview Page that displays all users in a grid.

Overview.java

Source file
public class Overview {
     
     @org.apache.tapestry5.ioc.annotations.Inject
     private IUserService userService;
     
     @Property
     private List<User> users;

     void onActivate() {
          users = userService.getAllUser();
     }
     
}

As we use Tapestry we have to use their @Inject annotation in order to get the Service correctly injected. The only thing left to do is to fetch all users on page loading.

6. Test the application

So far we just have set-up the application, the services and the repositories with their maven poms and the spring contexts.

Now we want to test the application. At first a simple integration test that checks whether the Service dependencies get correctly injected.

SpringContextTest.java

Source file
@ContextConfiguration("classpath:/de/boehme/app/ui/applicationContext.xml")
public class SpringContextTest extends AbstractTestNGSpringContextTests {
     
     @javax.inject.Inject
     private IDataService dataService;
     
     @javax.inject.Inject
     private IUserService userService;
     
     @Test
     public void startUp(){
          assertNotNull(dataService);
          assertNotNull(userService);
     }
}

This test starts the applicationContext from the UI application and checks if all dependencies are injected and not null. This test should pass.

To test Tapestry pages, we can rely on the tapestry-test module which is earlier defined as dependency.
You can look into the attached sources for more details.

7. Review

We have created a multi-module application that follows a 3-Tier Architecture. It consists of an UI Application module, two services and two repositories. For rapid development we are using an embedded HSQL database. The project setup can be used as simple template for other purposes, such as any JEE application.

8. Pitfalls


If you try to run the project inside of Eclipse you have to set up the maven goals correctly:

That means:

Maven clean install--> Goals:clean install
and set "Resolve workspace artifacts" to true


Jetty Start--> Goals:jetty:run
and set "Resolve workspace artifacts" to true


9. Project Download


You can download the whole project with all its sources under the following URL:
mm-base-parent.zip