Friday, January 9, 2015

Using Spring JPA

Introduction


Spring JPA is a quick method for providing CRUD functionality around an RDBMS.

Using Spring JPA, I can define an @Entity and then create a @Repository interface that takes the persisted entity as a generic type.  

In the repository interface, I define method names, like this:
 List<Gngram> findByCollectionNameAndFocalTermAndFocalPos(Pageable pageable, String collectionName, String focalTerm, String focalPos);   

The implementation is handled automagically by Spring.

I don't have to write a single line of SQL, JQL, or any query language.  This allows us to get basic CRUD functionality around any relational table or view in a matter of minutes.


In this example, we'll be using this table:


This table holds a Google n-Gram.  The format of each record is explained here.  As each record is read from a file, the database is updated.


Outline




Environment

  1. JDK 7
  2. Maven 3.2.3
  3. Spring
    1. Context, Core, JDBC 4.1.3
    2. Data-Commons and Data-JPA 4.1.4
  4. MySQL 5.1.27
JAR dependencies are specified in Maven and the exact version numbers given here:


The POM

My POM file looks like this:
 <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>com.mycompany.app</groupId>  
      <artifactId>mycompany-app</artifactId>  
      <version>4.1.4</version>  
      <packaging>pom</packaging>  
   
      <properties>  
           <spring-context.version>4.1.3.RELEASE</spring-context.version>  
           <spring-core.version>4.1.3.RELEASE</spring-core.version>  
           <spring-jdbc.version>4.1.3.RELEASE</spring-jdbc.version>  
           <spring-data-commons-core.version>1.4.1.RELEASE</spring-data-commons-core.version>  
           <spring-data-jpa.version>1.7.1.RELEASE</spring-data-jpa.version>  
           <hibernate-entitymanager.version>4.3.7.Final</hibernate-entitymanager.version>  
      </properties>  
   
      <dependencies>  
   
           <!-- Spring -->  
           <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-context</artifactId>  
                <version>${spring-context.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-core</artifactId>  
                <version>${spring-core.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-jdbc</artifactId>  
                <version>${spring-jdbc.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>org.springframework.data</groupId>  
                <artifactId>spring-data-commons</artifactId>  
                <!--version>1.6.3.RELEASE</version-->  
                <version>1.9.1.RELEASE</version>  
   
           </dependency>  
           <dependency>  
                <groupId>org.springframework.data</groupId>  
                <artifactId>spring-data-jpa</artifactId>  
                <version>1.7.1.RELEASE</version>  
           </dependency>  
           <dependency>  
                <groupId>org.hibernate</groupId>  
                <artifactId>hibernate-entitymanager</artifactId>  
                <version>4.3.1.Final</version>  
           </dependency>  
   
           <!-- MySQL -->  
           <dependency>  
                <groupId>mysql</groupId>  
                <artifactId>mysql-connector-java</artifactId>  
                <version>5.1.27</version>  
           </dependency>  
             
           <!-- Commons JDBC -->  
           <dependency>  
                <groupId>commons-pool</groupId>  
                <artifactId>commons-pool</artifactId>  
                <version>20030825.183949</version>  
           </dependency>  
           <dependency>  
                <groupId>commons-dbcp</groupId>  
                <artifactId>commons-dbcp</artifactId>  
                <version>20030825.184428</version>  
           </dependency>  
           <dependency>  
                <groupId>commons-collections</groupId>  
                <artifactId>commons-collections</artifactId>  
                <version>20040616</version>  
           </dependency>  
   
      </dependencies>  
   
 </project>  

The groupId and artifactId in red text above should be changed.


The Application Context

The primary configuration is in the application-context.xml file for the project. This configuration file provides the context for the Spring IoC container.

My context file looks like this:
 <?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" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:repository="http://www.springframework.org/schema/data/repository" xmlns:tx="http://www.springframework.org/schema/tx"  
      xsi:schemaLocation="http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.6.xsd  
           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-1.3.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd  
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
   
      <context:annotation-config />  
      <jpa:repositories base-package="org.swtk.ngrams.google.db.data.repositories" />  
      <context:component-scan base-package="org.swtk.ngrams.google.db.data" />  
   
      <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
           <property name="dataSource" ref="datasource" />  
           <property name="persistenceUnitName" value="swtk-gngrams" />  
           <property name="packagesToScan" value="org.swtk.ngrams.google.db" />  
           <property name="jpaVendorAdapter">  
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">  
                     <property name="showSql" value="false" />  
                     <property name="database" value="MYSQL" />  
                </bean>  
           </property>  
      </bean>  
   
      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
           <property name="entityManagerFactory" ref="entityManagerFactory" />  
      </bean>  
   
      <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">  
           <property name="url" value="jdbc:mysql://127.0.0.1:3306/gngrams" />  
           <property name="username" value="userid" />  
           <property name="password" value="password" />  
           <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
      </bean>  
   
 </beans>  

The text highlighted in red should be changed.


Package Layout


I use the following package layout:

  • com.mycompany.app.data
    • entities
    • repositories
    • service

The entities package contains the persistent POJOs that map to tables in the database.  The repositories package contains granular methods for accessing data in the database.  The service package contains coarser-grained methods for accessing data; perhaps using multiple methods from multiple repositories and returning a combined result to a consumer.


Entities

My entity class reflects the table in the image at the start of this article.

To any developer already familiar with ORM solutions, the annotations on this POJO should be straightforward:
 package org.swtk.ngrams.google.db.data.entities;  
   
 import javax.persistence.Column;  
 import javax.persistence.Entity;  
 import javax.persistence.GeneratedValue;  
 import javax.persistence.GenerationType;  
 import javax.persistence.Id;  
 import javax.persistence.Table;  
   
 import org.swtk.common.core.util.StringUtils;  
   
 @Entity  
 @Table(name = "gngram")  
 public class Gngram {  
   
      /**  
       *     Purpose:  
       *     The name of the collection file  
       *  
       *     eg. this is typically the underlying source file that contains the google n-grams  
       */  
      @Column(name = "COLLECTION_NAME")  
      private String     collectionName;  
   
      /**  
       *     Purpose:  
       *     The term with an attached part-of-speech  
       *  
       *     eg. given this n-Gram:  
       *          "had well_ADV organized"  
       *     the focal pos is  
       *          "ADV"  
       */  
      @Column(name = "FOCAL_POS")  
      private String     focalPos;  
   
      /**  
       *     Purpose:  
       *     The term with an attached part-of-speech  
       *  
       *     eg. given this n-Gram:  
       *          "had well_ADV organized"  
       *     the focal term is  
       *          "well"  
       */  
      @Column(name = "FOCAL_TERM")  
      private String     focalTerm;  
   
      /**  
       *     Purpose:  
       *     The primary key   
       */  
      @Id  
      @Column(name = "ID")  
      @GeneratedValue(strategy = GenerationType.IDENTITY)  
      private Integer     id;  
   
      /**  
       *     Purpose:  
       *     The number of times the n-Gram was found (across the entire collection)  
       */  
      @Column(name = "MATCH_COUNT")  
      private Double     matchCount;  
   
      /**  
       *     Purpose:  
       *     The number of volumes the n-Gram was found in  
       */  
      @Column(name = "VOLUME_COUNT")  
      private Double     volumeCount;  
   
      public String getCollectionName() {  
           return collectionName;  
      }  
   
      public String getFocalPos() {  
           return focalPos;  
      }  
   
      public String getFocalTerm() {  
           return focalTerm;  
      }  
   
      public Integer getId() {  
           return id;  
      }  
   
      public Double getMatchCount() {  
           return matchCount;  
      }  
   
      public Double getVolumeCount() {  
           return volumeCount;  
      }  
   
      public boolean hasFocalPos() {  
           return StringUtils.hasValue(getFocalPos());  
      }  
   
      public boolean hasFocalTerm() {  
           return StringUtils.hasValue(getFocalTerm());  
      }  
   
      public void setCollectionName(String collectionName) {  
           this.collectionName = collectionName;  
      }  
   
      public void setFocalPos(String focalPos) {  
           this.focalPos = focalPos;  
      }  
   
      public void setFocalTerm(String focalTerm) {  
           this.focalTerm = focalTerm;  
      }  
   
      public void setId(Integer id) {  
           this.id = id;  
      }  
   
      public void setMatchCount(Double matchCount) {  
           this.matchCount = matchCount;  
      }  
   
      public void setVolumeCount(Double volumeCount) {  
           this.volumeCount = volumeCount;  
      }  
 }  
   


Repositories

The Repository interface extends the JpaRepository interface, and takes the persistent entity as a generic type:

 package org.swtk.ngrams.google.db.data.repositories;  

 import java.util.List;  
 import org.springframework.data.domain.Pageable;  
 import org.springframework.data.jpa.repository.JpaRepository;  
 import org.springframework.stereotype.Repository;  
 import org.swtk.ngrams.google.db.data.entities.Gngram;  

 @Repository  
 public interface GngramRepository extends JpaRepository<Gngram, Integer> {  

      List<Gngram> findByCollectionName(Pageable pageable, String collectionName);  

      List<Gngram> findByCollectionNameAndFocalPos(Pageable pageable, String collectionName, String focalPos);  

      List<Gngram> findByCollectionNameAndFocalTerm(Pageable pageable, String collectionName, String focalTerm);  

      List<Gngram> findByCollectionNameAndFocalTermAndFocalPos(Pageable pageable, String collectionName, String focalTerm, String focalPos);  
 }  

The pageable parameter allows the specification of the page and number of results per page.  The remaining parameters are inserted into the query generated by Spring for each of these method implementations.


Test Case

The test case looks like this:
 package org.swtk.ngrams.google.db.data.repositories;  
   
 import static org.junit.Assert.assertEquals;  
 import static org.junit.Assert.assertNotNull;  
   
 import java.util.ArrayList;  
 import java.util.List;  
   
 import org.junit.Test;  
 import org.swtk.common.core.env.LogManager;  
 import org.swtk.ngrams.google.db.data.entities.Gngram;  
 import org.swtk.ngrams.google.db.util.adapter.GngramAdapter;  
 import org.swtk.ngrams.ioc.NgramsIocContainer;  
   
 public class GngramRepositoryTest {  
   
      public static LogManager     logger               = new LogManager(GngramRepositoryTest.class);  
   
      private static final String     COLLECTION_NAME     = "test-run-grt";  
   
      private void createAndSave(GngramRepository gngramRepository) throws Throwable {  
           List<Gngram> list = new ArrayList<Gngram>();  
   
           list.add(GngramAdapter.transform(COLLECTION_NAME, "had well_ADV organized     1904     11     11"));  
           list.add(GngramAdapter.transform(COLLECTION_NAME, "had well_NOUN organized     1934     22     22"));  
   
           gngramRepository.save(list);  
      }  
   
      @Test  
      public void run() throws Throwable {  
   
           GngramRepository gngramRepository = NgramsIocContainer.getGngramsContext().getBean(GngramRepository.class);  
           assertNotNull(gngramRepository);  
   
           /* clear database: test prep */  
           gngramRepository.delete(gngramRepository.findByCollectionName(null, COLLECTION_NAME));  
   
           /* create and save the entity */  
           createAndSave(gngramRepository);  
   
           /* test basic retrieval methods */  
           assertEquals(2, gngramRepository.findByCollectionNameAndFocalTerm(null, COLLECTION_NAME, "well").size());  
           assertEquals(1, gngramRepository.findByCollectionNameAndFocalPos(null, COLLECTION_NAME, "ADV").size());  
   
           /* clear database: test cleanup */  
           gngramRepository.delete(gngramRepository.findByCollectionName(null, COLLECTION_NAME));  
      }  
 }  


I don't have a services class defined for this example.


References

  1. Official Spring JPA Documentation
    1. At some point, every Spring JPA developer should read through this documentation completely.
    2. Supported Keywords Inside Method Names
      1. The complete list of keywords and samples.  
      2. Essentially an EBNF-like table for writing methods in your interface.
  2. Using Custom Queries with Query Methods
    1. Generic queries can only take you so far.
    2. [Stackoverflow] Custom Methods belong in separate interfaces
      1. Putting a @Query annotation over a method in an interface that extends JpaRepository doesn't seem to work as well as some articles suggest.

Monday, January 5, 2015

An Introduction to Java API for RESTful Web Services (JAX-RS)

Environment
  1. Eclipse Luna Service Release 1 (4.4.1)
  2. JDK 1.7.0_71
  3. Apache Maven 3.2.3 
  4. Tomcat 7


Outline
  • Configure a Maven Web Project in Eclipse
  • Create a REST resource
  • Deploy to Tomcat



Creating the Project


If you have a preferred method for creating Dynamic Web Projects in Eclipse, you may find it simpler to skip directly to the POM (below).

1. Select
File > New > Maven Project



2a. Check Create a simple project (skip archetype selection)

2b. Uncheck Use default Workspace location and enter a suitable path for the project


3. Fill out the required information for a new Maven Project
4. Click Finish


Note: Although we'll change this to WAR later, it's important to keep the packaging as a JAR file in the dialog for now.  This will trigger the configuration event for adding a web.xml file down below.

Configuring the Project


1. Right-Click the Project > Properties > Project Facets
Select the option to convert this project to "faceted form":


2a. Ensure that "Dynamic Web Module 3.0" is selected
2b. Ensure that "Java 1.7" is selected


3. Click the link at the bottom of the dialog that says "Further configuration available".
Check the box that says "Generate web.xml deployment descriptor":
4. Click Ok


Edit the POM


Replace (or merge) the generated POM with the file given below, and then update the Eclipse Project:
Right Click on the Project > Maven > Update Project

Run the Maven Clean Package command from the command line:
mvn clean package -e

My POM file looks like this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<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>com.mycompany.app</groupId>
 <artifactId>mycompany-app</artifactId>
 <version>1.0.0</version>
 <packaging>war</packaging>

 <url />
 <name>mycompany-app</name>
 <inceptionYear>2015</inceptionYear>
 <description>This is my company's app</description>

 <build>
  <plugins>

   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.5.1</version>
    <inherited>true</inherited>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
    </configuration>
   </plugin>

   <plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
     <warSourceDirectory>WebContent</warSourceDirectory>
     <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
   </plugin>

   <!-- Required for Maven-to-Tomcat Deployments -->
   <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
     <url>http://192.168.1.5:8080/manager/text</url>
     <server>TomcatServer</server>
     <path>/test</path>
     <username>tomcat</username>
     <password>tomcat</password>
    </configuration>
   </plugin>

  </plugins>
 </build>

 <repositories>
  <repository>
   <id>maven2-repository.java.net</id>
   <name>Java.net Repository for Maven</name>
   <url>http://download.java.net/maven/2/</url>
   <layout>default</layout>
  </repository>
 </repositories>

 <dependencies>
  <dependency>
   <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-server</artifactId>
   <version>1.9</version>
  </dependency>
  <dependency>
   <groupId>com.owlike</groupId>
   <artifactId>genson</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
  </dependency>
 </dependencies>

</project>
 


POM Overview:

  1. Application Packaging
    1. Must be a Web Application Archive (WAR) file
  2. Tomcat Plugin
    1. The configuration for this plugin, and for the Tomcat server, is detailed in this article.
  3. Additional Dependencies
    1. Jersey and the Genson JSON conversion library.
    2. The Javax Servlet package (optional for this tutorial, but usually necessary when additional code is added).


You should now have an error-free Eclipse Workspace:



Configuring the Deployment Description (web.xml)


If you didn't generate a deployment descriptor when you created the project in Eclipse, add it under this path:
mycompany-app\WebContent\WEB-INF\web.xml


My web.xml file looks like this:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 id="WebApp_ID" version="3.0">

 <display-name>RESTful Web App</display-name>

 <servlet>
  <servlet-name>jersey-helloworld-serlvet</servlet-name>
  <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
   <param-name>com.sun.jersey.config.property.packages</param-name>
   <param-value>com.mycompany.app</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>jersey-helloworld-serlvet</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>

</web-app>  

You might want to consider modifying the values highlighted in red above.

  1. Jersey Package Configuration value
    1. Specifies the package structure containing the REST resources.
  2. URL pattern
    1. This is a resource mapping



Creating the REST Resource


Outline:
  1. Create a User POJO
  2. Create the REST Service
We'll populate the User POJO with values passed into the REST service, and return the User object as JSON.


The User POJO
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 package com.mycompany.app;  
   
 public class User {  
   
      private Integer age;  
   
      private String firstname;  
   
      private String lastname;  
   
      public User() {  
      }  
   
      public User(String firstname, String lastname, Integer age) {  
           setFirstname(firstname);  
           setLastname(lastname);  
           setAge(age);  
      }  
   
      public Integer getAge() {  
           return age;  
      }  
   
      public String getFirstname() {  
           return firstname;  
      }  
   
      public String getLastname() {  
           return lastname;  
      }  
   
      public void setAge(Integer age) {  
           this.age = age;  
      }  
   
      public void setFirstname(String firstname) {  
           this.firstname = firstname;  
      }  
   
      public void setLastname(String lastname) {  
           this.lastname = lastname;  
      }  
 }  


The REST Service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.swtk.sandbox.rest;  
   
 import javax.ws.rs.DefaultValue;  
 import javax.ws.rs.GET;  
 import javax.ws.rs.Path;  
 import javax.ws.rs.Produces;  
 import javax.ws.rs.QueryParam;  
 import javax.ws.rs.core.MediaType;  
   
 @Path("/simpleREST")  
 public class SimpleREST {  
   
      @GET  
      @Path("/user")  
      @Produces(MediaType.APPLICATION_JSON)  
      public User responseMsg(  
                @DefaultValue("john") @QueryParam("firstname") String firstname,  
                @DefaultValue("doe") @QueryParam("lastname") String lastname,  
                @DefaultValue("21") @QueryParam("age") Integer age) {  
   
           return new User(firstname, lastname, age);  
      }  
 }  

In order to construct a URL to test this service, we need these three items:

  1. The Tomcat deployment path from the POM file:  /sandbox-rest
  2. The url-pattern from the servlet-mapping in the web.xml file: /rest
  3. The @Path annotation on the REST class:  /simpleREST
  4. The @Path annotation on the GET method in this class:  /user

We assemble these three together and get:
http://localhost:8080/sandbox-rest/rest/simpleREST/user



Deploying the Resource


Assuming both the POM file and Tomcat are configured correctly, this will be as simple as typing:
mvn tomcat7:deploy -e

To undeploy the web-app, simply type:
mvn tomcat7:undeploy -e

Using this URL
http://localhost:8080/sandbox-rest/rest/simpleREST/user

the web-app returns:



References

  1. Deploying a Web Project with Maven
    1. Describes the POM configuration for automated Maven-to-Tomcat deployments.
  2. [JavaCodeGeeks] Jersey Hello World Example
    1. The first section of my post (using Eclipse to create a Maven Web Project) was based on this tutorial.

Monday, December 15, 2014

Using the JdbcTemplate in Spring

Outline

  1. Why use the JDBC Template?
  2. Configure the JDBC Template
  3. Perform CRUD Operations with the JDBC Template
  4. Map Result Sets to Objects using RowMapper and ResultSetExtractor



Benefits of using the JDBC Template

  1. No connection management
  2. No exception handling required
    1. Exceptions are unchecked, which means you can catch them if you like, but you are not obligated to
    2. Checked vs Unchecked Exceptions
The JDBC Template is one of the core features of Spring's data access support. Using the JDBC support allows us to avoid try/catch block, support vfor iterating through result sets, mapping to sets, under trad JDBC approach.

Using JDBC Template doesn't have the auto-magicallity of a full JPA stack-implementation, but in my experience, what you lose in code in shifting to JPA, you typically pick up in configuration.  Use of the JDBC Template loses just enough of the boilerplate code, yet retains enough simplicity, that a developer accustomed to traditional JDBC development can pick it up quickly, and the slightly higher learning curve with JPA is avoided.



Proposed Architecture



Package Structure and Files:
  • src/
    • main/
      • java
        • com.mycompany.project
          • data
            • entities
              • Media.java
              • Rental.java
              • RentalLocation.java
            • repositories
              • RentalLocationRepository.java
              • RentalLocationRepositoryImpl.java
              • RentalLocationResultsExtractor.java
              • Repo.java
            • service
              • RentalService.java
      • resources
        • config
          • application-config.xml
    • test
      • java
        • com.mycompany.project
          • RentalLocationRepositoryTest
  • pom.xml



Configure the JDBC Template


src/main/resources/config/application-context.xml

 <?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.xsd  
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
   
      <context:component-scan base-package="com.mycompany.project.data" />  
   
      <bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">  
           <property name="url" value="jdbc:mysql://localhost:3306/myschema" />  
           <property name="username" value="myuser" />  
           <property name="password" value="mypassword" />  
           <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
      </bean>  
   
 </beans>  



Repository Interface

 public interface RentalLocationRepository extends Repo<RentalLocation> {   
      // add custom methods here   
 }   
   
 public interface Repo<T> {  
      void insert(T entity);  
      void update(T entity);  
      void delete(T entity);  
      T findById();  
 }  



Concrete Repository Implementation

 package com.mycompany.project.data.repositories;  
   
 import java.sql.Connection;  
 import java.sql.PreparedStatement;  
 import java.sql.ResultSet;  
 import java.sql.SQLException;  
 import java.sql.Statement;  
 import java.util.List;  
 ...  
   
 @Repository
 public class RentalLocationRepositoryImpl implements RentalLocationRepository {  
   
      private JdbcTemplate          template;  
   
      @Autowired  
      public RentalLocationRepositoryImpl(DataSource dataSource) {  
           this.template = new JdbcTemplate(dataSource);  
      }  
   
      ...  
 }  
   

Notice the basic design here.

In a jUnit test case, the RentalLocationRepositoryImpl is constructed like this:
 ApplicationContext context = new ClassPathXmlApplicationContext("config/application-context.xml");  
 assertNotNull(context);  
   
 DataSource dataSource = context.getBean("datasource", DataSource.class);  
 assertNotNull(dataSource);  
   
 RentalLocationRepositoryImpl repo = new RentalLocationRepositoryImpl(dataSource);  
 assertNotNull(repo);  

The Datasource is retrieved from the configured application-context and passed into the Repository class. That's it. Everything else is managed by Spring. No further annotation is needed

Perform CRUD Operations with the JDBC Template


Create

      @Override  
      public void insert(final RentalLocation entity) {  
           KeyHolder keyHolder = new GeneratedKeyHolder();  
           this.template.update(new PreparedStatementCreator() {  
   
                @Override  
                public PreparedStatement createPreparedStatement(Connection con) throws SQLException {  
                     PreparedStatement statement = con.prepareStatement(INSERT_STATEMENT, Statement.RETURN_GENERATED_KEYS);  
                     statement.setString(1, entity.getName());  
                     statement.setString(2, entity.getAddress1());  
                     statement.setString(3, entity.getAddress2());  
                     statement.setString(4, entity.getCity());  
                     statement.setString(5, entity.getState());  
                     statement.setString(6, entity.getPostalCode());  
                     return statement;  
                }  
   
           }, keyHolder);  
   
           entity.setRentalLocationId(keyHolder.getKey().intValue());  
      }  
   
      ...

      @Test  
      public void insert() throws Throwable {  
             
           ApplicationContext context = new ClassPathXmlApplicationContext("config/application-context.xml");  
           DataSource dataSource = context.getBean("datasource", DataSource.class);  
           RentalLocationRepositoryImpl repo = new RentalLocationRepositoryImpl(dataSource);  
   
           RentalLocation entity = dummy();  
           repo.insert(entity);  
           System.err.println("Auto Generated ID (retrieved by-reference): " + entity.getRentalLocationId());  
           assertNotNull(repo.findById(entity.getRentalLocationId()));  
   
           ((ConfigurableApplicationContext) context).registerShutdownHook();  
      }  

The insert statement is
 insert into rental_location (rental_location_id, name, address1, address2, city, state, postal_code) values (null, ?, ?, ?, ?, ?, ?)  

Read


 public RentalLocation findById(Integer id) {  
      try {  
           return this.template.queryForObject(FIND_BY_ID_QUERY, new Object[] { id }, new RentalLocationRowMapper());  
      } catch (EmptyResultDataAccessException e) {  
           return null;  
      }  
 }  
   
 public List<RentalLocation> findLocationsByState1(String state) {  
      return this.template.query(FIND_BY_STATE_QUERY_01, new Object[] { state }, new RentalLocationRowMapper());  
 }  
   
 public List<RentalLocation> findLocationsByState2(String state) {  
      return this.template.query(FIND_BY_STATE_QUERY_02, new Object[] { state }, new RentalLocationResultExtractor());  
 }  



Map Result Sets to Objects using RowMapper and ResultSetExtractor

Friday, December 12, 2014

Using Spring to Load Dictionaries from Local Resources

Introduction


Spring contains some useful design patterns for accessing local resources at runtime in a platform-independent manner.


Properties File


I debated initially whether the dictionary paths should be abstracted to a properties file. To do so without reason beyond "that's how it's always done" isn't sufficient. However, a maintenance justification may be found that any path changes will not require Spring bean changes, potentially eliminating the introduction of configuration errors.

Locations to the dictionary files are listed here:
 properties.adjectives.path          = dictionaries/adjectives.dat  
 properties.measurementWords.path    = dictionaries/measurement-words.dat  
 properties.nounPhrases.path         = dictionaries/noun-phrases.dat  
 properties.stopWords.path           = dictionaries/stop-words.dat  

I've added this properties file to this path:
commons-dict\src\main\resources\config\paths.properties
and the actual sources files (not listed here) exist at that location.


Dictionary Bean


 package org.swtk.common.dict.beans; 
 
 import java.util.Set;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.beans.factory.annotation.Value;  
 import org.springframework.context.annotation.Lazy;  
 import org.springframework.core.io.Resource;  
 import org.springframework.stereotype.Component;  
 import org.swtk.common.dict.DictionaryBase;  
 import org.swtk.common.env.LogManager;  

 @Lazy  
 @Component("nounPhrasesDictionary")  
 public class NounPhrasesDictionary extends DictionaryBase {  

      public static LogManager     logger     = new LogManager(NounPhrasesDictionary.class);  

      @Autowired  
      @Value("${properties.nounPhrases.path}")  
      private Resource               resource;  

      @Override  
      public Set<String> entries() {  
           return set(resource);  
      }  
 }  

Path:
commons-dict\src\main\java\org\swtk\common\dict\beans\NounPhrasesDictionary.java



Dictionary Base


All dictionary beans extend this base class:
 package org.swtk.common.dict;  
   
 import java.io.BufferedReader;  
 import java.io.IOException;  
 import java.io.InputStreamReader;  
 import java.util.Set;  
 import java.util.TreeSet;  
   
 import org.springframework.core.io.Resource;  
 import org.swtk.common.env.LogManager;  
   
 public abstract class DictionaryBase implements Dictionary {  
   
      public static LogManager     logger     = new LogManager(DictionaryBase.class);  
   
      private String     beanName;  
   
      public String getBeanName() {  
           return beanName;  
      }  
   
      public Set<String> set(Resource resource) {  
           Set<String> set = new TreeSet<String>();  
   
           try {  
   
                BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));  
                for (String line = reader.readLine(); line != null; line = reader.readLine())  
                     set.add(line);  
   
           } catch (IOException e) {  
                logger.error(e, "Failed to Load Dictionary (name = %s)", getBeanName());  
           }  
   
           return set;  
      }  
   
      @Override  
      public void setBeanName(String beanName) {  
           this.beanName = beanName;  
      }  
 }  
   



application-config.xml


 <?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"  
      xmlns:jpa="http://www.springframework.org/schema/data/jpa"  
      xmlns:repository="http://www.springframework.org/schema/data/repository"  
      xmlns:tx="http://www.springframework.org/schema/tx"  
      xsi:schemaLocation="http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.6.xsd  
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">  

      <context:component-scan base-package="org.swtk.common.dict.beans" />  
      <context:property-placeholder location="classpath:config/paths.properties" />  

 </beans>  

Path:
commons-dict\src\main\resources\config\application-context.xml



Container Access


If you have other projects in your workspace that are not Spring-enabled, you may find it useful to manage the application-context transparently.

I've encapsulated the context in a factory, and exposed each dictionary via an enum parameter:
 package org.swtk.common.dict;  

 import org.springframework.context.ApplicationContext;  
 import org.springframework.context.ConfigurableApplicationContext;  
 import org.springframework.context.support.ClassPathXmlApplicationContext;  
 import org.swtk.common.env.LogManager;  

 public class DictionaryContext {  

      private static DictionaryContext     instance;  

      public static LogManager               logger     = new LogManager(DictionaryContext.class);  

      public static DictionaryContext getInstance() {  
           if (null == instance) instance = new DictionaryContext();  
           return instance;  
      }  

      private ApplicationContext     context;  

      private DictionaryContext() {}  

      public void close() {  
           ((ConfigurableApplicationContext) getContext()).close();  
           setContext(null);  
      }  

      private ApplicationContext getContext() {  
           if (null == context) setContext(new ClassPathXmlApplicationContext("config/application-context.xml"));  
           return context;  
      }  

      public Dictionary getDictionary(DictionaryName dictionary) {  
           Dictionary dict = getContext().getBean(dictionary.toString(), Dictionary.class);  
           return dict;  
      }  

      private void setContext(ApplicationContext context) {  
           this.context = context;  
      }  
 }  



Dictionary Name Enum


Unfortunately, we are maintaining the beanId in two places now: this enum, and within the @Component annotation on each Spring Bean. This increases our maintenance burden slightly, but does make life easier for the consumer. Ultimately, I think this is worth it.

 package org.swtk.common.dict; 
 
 public enum DictionaryName {  

      ADJECTIVES("adjectivesDictionary"),  
      MEASUREMENT_WORDS("measurementWordsDictionary"),  
      NOUN_PHRASES("nounPhrasesDictionary"),  
      STOP_WORDS("stopWordsDictionary");  

      private String     beanId;  

      private DictionaryName(String beanId) {  
           setBeanId(beanId);  
      }  

      private String getBeanId() {  
           return beanId;  
      }  

      private void setBeanId(String beanId) {  
           this.beanId = beanId;  
      }  

      @Override  
      public String toString() {  
           return getBeanId();  
      }  
 }  



Test Client


The test client is simple, and the use of Spring transparent to the consumer:
 Dictionary dictionary = DictionaryContext.getInstance().getDictionary(DictionaryName.NOUN_PHRASES);  
 assertNotNull(dictionary.entries());  
 assertFalse(dictionary.entries().isEmpty());  

Wednesday, December 10, 2014

Introduction to Inversion of Control and Dependency Injection

Developers of software strive to reduce dependencies between components in software for various reasons.

This leads to a new problem:
How can a component know all the other components it needs to fulfill its purpose?

Dependency Injection (DI) is a technique that supplies external dependencies to a software component.

An alternative to DI is to hard-code dependencies:
 public class Requestor {  
   public void operation() {  
    DatabaseServiceImplementation dbs = new DatabaseServiceImplementation();  
    dbs.performOperation1();  
   }  
 }  


DI is a form of Inversion of Control (IoC) where the concern being inverted is the process of obtaining the needed dependency:
 public class Requestor {  
   public void operation() {  
    DatabaseServiceInterface dbs = beanFactory.getClass("databaseServices", DatabaseServiceInterface.class);  
    dbs.performOperation1();  
   }  
 }  


This line:
 beanFactory.getClass(“databaseServices”, DatabaseServiceInterface.class);  

returns an interface (or, specification) that contains operations (or, services) that are defined in DatabaseServiceInterface.java.

The name (or, key) used to retrieve this interface is contained in an XML file:
  <bean   
       id="databaseServices"   
       class="com.mycompany.test.ioc.DatabaseServiceImplementation" />   


This mapping tells Spring to instantiate and return an instance of “DatabaseServiceImplementation” whenever the class is called:
 beanFactory.getClass(“databaseServices”, <Class>)   


It is possible to cast “databaseServices” to its concrete implementation as well:
  DatabaseServiceInterface dibs = beanFactory.getClass(  
       "databaseServices",   
       Reimplementation);   

You can also write code like this:
  DatabaseServiceImplementation idbs = beanFactory.getClass(  
       "databaseServices",   
       DatabaseServiceImplementation.class);   

In this case we're using the concrete implementation to instantiate the class, and at first glance there would appear to be little benefit over using an interface. However, we still gain the value of injected dependencies being made available via this approach
In both cases, there is now a dependency to DatabaseServiceImplementation.

Scenarios 3 and 4 are identical to Scenario 1, in terms of introducing a dependency on DatabaseServiceImplementation to the Requestor.

No Interface; No Dependency Injection:

Scenarios 1,3, 4


Coding to an Interface with Dependency Injection:

Scenario 2


The term “Dependency Injection” refers to the container (in this case, Spring) injecting a dependency to a class (in this case, DatabaseServiceImplementation) into the requesting code:
 beanFactory.getBean(<id>, <class>)  

Spring injects a dependency to <id> into the Requestor.

Monday, December 8, 2014

Session Management

Stateful and Stateless


HTTP is stateless

Each request is treated as an independent transaction that is unrelated to any previous request.
Request attributes live on the request object. As soon as that request is gone, the attribute is gone.

Sessions are stateful


A session is a semi-permanent interactive information interchange, also known as a dialogue, between two or more communicating devices.

Should data be stored on the client side or server side?

You don't necessarily want to store data client side (in a cookie), because you don't want the client to manipulate that data. The solution is to store the data server side, and store the unique identifier to that data on the client. This becomes a stateful session.






References

  1. What are Sessions and how do they work?
    1. The user id is stored in the session data, server-side, after successful identification. Then for every HTTP request you get from the client, the session id (given by the client) will point you to the correct session data (stored by the server) that contains the authenticated user id.

Servlet Parameters

Request Parameters


A request parameter is a parameter passed to a servlet through a request. Request parameters are the result of submitting an HTTP request with a query string that specifies the name/value pairs, or of submitting an HTML form that specifies the name/value pairs. The name and the values are always strings.

The request attribute lives on the request object. As soon as that request is gone, the attribute is gone.

In the example below, we'll perform a POST using HTML and the data will be retrieved by using request.getParameter(). Parameters are Strings, and generally can be retrieved, but not set.

Servlet Definition


 package com.swtk.web.servlets;  

 import java.io.IOException;  
 import java.io.PrintWriter;  
 import javax.servlet.ServletException;  
 import javax.servlet.annotation.WebServlet;  
 import javax.servlet.http.HttpServlet;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  

 @WebServlet({ "/SurveyPost", "/post/survey.do" })  

 public class SurveyPost extends HttpServlet {  

      private static final long     serialVersionUID     = 1L;  

      public SurveyPost() {  
           super();  
      }  

      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}  

      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
           PrintWriter out = response.getWriter();  
           out.print(request.getParameter("menuChoice"));  
           out.close();  
      }  
 }  
mapped to this location
"/post/survey.do"

HTML Form Definition


 <html>  
      <head>  
           <title>Insert title here</title>  
      </head>  
      <body>  
           <form id="form1" method="post" action="/HelloWorldServlet/post/survey.do">  
                <select id="menuChoice" name="menuChoice">  
                     <option id="1" value="CA">CA</option>  
                     <option id="2" value="OR">OR</option>  
                     <option id="3" value="AZ">AZ</option>  
                </select>  
                <input type="submit" />  
           </form>  
      </body>  
 </html>  
deployed to this location in my Web Project:
WebContent/PostExample1.html

Fiddler Output


When I run the HTML page on Tomcat, and hit submit, Fiddler will show this:
 POST http://localhost:8080/HelloWorldServlet/post/survey.do HTTP/1.1  
 Host: localhost:8080  
 Connection: keep-alive  
 Content-Length: 13  
 Cache-Control: max-age=0  
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8  
 Origin: http://localhost:8080  
 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36  
 Content-Type: application/x-www-form-urlencoded  
 Referer: http://localhost:8080/HelloWorldServlet/PostExample1.html  
 Accept-Encoding: gzip, deflate  
 Accept-Language: en-US,en;q=0.8,fr;q=0.6,nb;q=0.4  
 menuChoice=OR  


Web Output


The Web output shows the attribute value:




Servlet Context

Unlike the request context, the servlet context is at the application level. This is very powerful, but requires thread safety. Why is this named servlet context if this is application level? Servlet context is application level. When we do things in the container, it generally occurs in the servlet.


Initialization Parameters (init-params)




Global Parameters (context-param)