Tech notes for java-jersey


PART 1: Quick start a RESTful project with Jersey, Jetty and Gradle

(This is part of a series http://newfivefour.com/category_java-web-quick-start.html

Make the project directory and the directory structure for Gradle:

mkdir YOUR_PROJECT_DIR && cd YOUR_PROJECT_DIR
mkdir -p {src/main/java/com/example/YOURPROJECT/,src/main/resources/META-INF,src/main/webapp/WEB-INF,logs}

Next create the Gradle build file, including the war and eclipse plugins, and the logging, jersey and jersey json jars.

echo "
apply plugin: 'war'
apply plugin: 'eclipse'

repositories {
    mavenCentral()
    // Below needed in later tutorials
    maven {
        url 'http://download.eclipse.org/rt/eclipselink/maven.repo'
    }
}

dependencies {
    compile 'log4j:log4j:1.2.7'
    compile 'org.slf4j:slf4j-log4j12:1.6.6'
    compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.6'
    // Following aren't needed atm, but will be in later parts.
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.6'
    compile 'org.eclipse.jetty:jetty-jsp:9.1.0.M0'
    compile 'postgresql:postgresql:9.1-901-1.jdbc4'
    compile 'org.eclipse.persistence:eclipselink:2.4.0'        
}" > build.gradle

Next create the log4j.properties file.

echo "
log4j.rootCategory=INFO, rollingFile 

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=logs/YOUR_LOG_FILE.log
log4j.appender.rollingFile.MaxFileSize=10MB
log4j.appender.rollingFile.MaxBackupIndex=2
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH-mm-ss} %-5p [%t] %c %x - %m%n
" > src/main/resources/log4j.properties

Next create the web.xml file starting Jersey by pointing to a yet-to-be-created application class, specifying the package where the request classes will live, and defining a path for the REST calls.

echo '
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">

	<display-name>YOUR_SERVICE</display-name>
	
	<servlet>
		<servlet-name>YOUR_SERVLET_NAME</servlet-name>
		<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.example.YOURPROJECT.JerseyApplication</param-value>
        </init-param>		
	    <init-param>
	    	<param-name>jersey.config.server.provider.packages</param-name>
			<param-value>com.example.YOURPROJECT</param-value>
	    </init-param>
	    <load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>YOUR_SERVLET_NAME</servlet-name>
		<url-pattern>/YOUR_PATH/*</url-pattern>
	</servlet-mapping>
	
</web-app>
' > src/main/webapp/WEB-INF/web.xml

Now create that JerseyApplication file we referred to. (Specifying the JerseyApplication isn't strictly necessary, nor is registering Jackson for this setup, but if we're using JSON later on, it will be)

echo "
package com.example.YOURPROJECT;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class JerseyApplication extends ResourceConfig {
	public JerseyApplication() {
		register(JacksonFeature.class);
	}
}

" > src/main/java/com/example/YOURPROJECT/JerseyApplication.java

Now create a simple Jersey request.

echo '
package com.example.YOURPROJECT;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.log4j.Logger;

@Path("example")
public class ExampleRequest {
	
	@Path("{example}")
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String example(@PathParam("example") String example) {
		Logger.getLogger(getClass()).info("Working???");
		return example + "!!!!";
	}

}
' > src/main/java/com/example/YOURPROJECT/ExampleRequest.java

Build the project, which creates a WAR file, download the jetty runner, and then use the runner to run the WAR on port 8081.

gradle build
wget http://repo1.maven.org/maven2/org/eclipse/jetty/jetty-runner/9.1.0.M0/jetty-runner-9.1.0.M0.jar
java -jar jetty-runner-9.1.0.M0.jar --port 8081 build/libs/YOUR_PROJECT_DIR.war

Now you should be able envoke the request you defined earlier by visiting its url (and check the log file after you do so).

http://localhost:8081/YOUR_PATH/example/some_text

If you want to use this project in Eclipse, run 'gradle eclipse' and import the project into Eclipse.

java-jersey java java-jetty gradle java-web-quick-start

PART 2: Using JSON with Jersey 2

(This is part of a series http://blog.denevell.org/category_java-web-quick-start.html)

We previously included the import in our build.gradle

compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.6'

And created a JerseyApplication that had the line

register(JacksonFeature.class);

This allows use to use JSON via the Jackson library with Jersey.

Now create a POJO which we'll use for transmitting the JSON. (Note the @XMLRootElement annotation which means it'll be serialised, into JSON in our case).

echo '
package com.example.YOURPROJECT;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class ExampleResource {
	
	private String stuff;

	public String getStuff() {
		return stuff;
	}

	public void setStuff(String stuff) {
		this.stuff = stuff;
	}

}
' > src/main/java/com/example/YOURPROJECT/ExampleResource.java

Now let's create a new request that will return an array of these objects.

echo '
package com.example.YOURPROJECT;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("example_json")
public class ExampleJSONRequest {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<ExampleResource> example() {
        ArrayList<ExampleResource> resList = new ArrayList<ExampleResource>();
        ExampleResource exampleItem = new ExampleResource();
        exampleItem.setStuff("Some stuff");
        resList.add(exampleItem);
        ExampleResource exampleItem1 = new ExampleResource();
        exampleItem1.setStuff("Some more stuff");
        resList.add(exampleItem1);
        return resList;
    }
}
' > src/main/java/com/example/YOURPROJECT/ExampleJSONRequest.java

The differences from our previous request are that the @Path has changed, we're not longer concerned about a @PathParam, and the @Produces method now says we're returning JSON, not plain text.

Let's now build it, run it and look at the response.

gradle build
java -jar jetty-runner-9.1.0.M0.jar --port 8081 build/libs/YOUR_PROJECT_DIR.war

Then if you go to the url below you will see [{"stuff":"Some stuff"},{"stuff":"Some more stuff"}]

http://localhost:8081/YOUR_PATH/example_json
java-jersey java java-web-quick-start

PART 3: Using JPA and Postgresql in your application

(This is part of a series http://blog.denevell.org/category_java-web-quick-start.html)

Previously, in our build.gradle file we included these jars to allow us to talk to a Postgresql database through JPA.

compile 'postgresql:postgresql:9.1-901-1.jdbc4'
compile 'org.eclipse.persistence:eclipselink:2.4.0'

We needed the eclipse link repository for that.

repositories {
    maven {
        url 'http://download.eclipse.org/rt/eclipselink/maven.repo'
    }
    ...
}

Now we need to create a Postgresql database user and database to talk to. We'd normally set this up in the environment somehow before running the project.

(as root)
su - postgres
psql -c "create user test_username password 'test_password';"
psql -c "create database test_database owner test_username"
(logout from postgres and root)

Now we can create the persistence.xml file that tells JPA how to connect to our database. (Ensure there's no whitespace at the start of the file)

echo '<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
  <persistence-unit name="PERSISTENCE_UNIT_NAME" transaction-type="RESOURCE_LOCAL">
    <mapping-file>META-INF/mapping.xml</mapping-file>
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
      <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test_database" />
      <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
      <property name="javax.persistence.jdbc.user" value="test_username" />
      <property name="javax.persistence.jdbc.password" value="test_password" />
      <property name="eclipselink.logging.level" value="ALL" />
	   </properties>
	 </persistence-unit>
</persistence>
' > src/main/resources/META-INF/persistence.xml

This is doing a couple of things

  • Naming the persistence-unit which we'll use when we come to initialise JPA
  • Makeing our database transactions will be local to this machine
  • Saying there's a mapping file in META-INF/mapping.xml that maps our Objects to the database
  • Saying we're using EclipseList for the persistence provider
  • Pointing JPA to our database
  • Providing the username and password to that database
  • Making EclipseLink log everything

Next we'll create a simple Object, or Entity, which we'll persist in the database. It's just a POJO.

echo '
package com.example.YOURPROJECT;

public class ExampleEntity {
	
	private long id; 
	private String talky;

	public String getTalky() {
		return talky;
	}
	public void setTalky(String talky) {
		this.talky = talky;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}

}
' > src/main/java/com/example/YOURPROJECT/ExampleEntity.java

We could use annotations on the object to map it to the database, and leave JPA to sort out the tables etc, but that always leads to pain.

So we're creating the mapping.xml file we referred to earlier. (Ensure there's no whitespace at the start of the file)

echo '<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm    
  http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
  version="2.0">
    <entity class="com.example.YOURPROJECT.ExampleEntity"> 
      <table name="example" />
      <named-query name="list">
        <query>select p from ExampleEntity p</query>
      </named-query>       
      <attributes>
        <id name="id">
          <generated-value strategy="auto" />
        </id>
        <basic name="talky">
          <column name="talky" nullable="false"/>
        </basic>
      </attributes>
    </entity>    
</entity-mappings>
' > src/main/resources/META-INF/mapping.xml

This is saying:

  • Specifying the entity you created above
  • Specifying the database table where this entity lives
  • Creating a named query called 'list' that lists all the entities
  • Defining an primary key id attribute, relating to 'id' in entity, that is an automatically generated value in the database, using the AUTO strategy (see http://www.objectdb.com/java/jpa/entity/generated)
  • Defining an talky attribute, relating to 'talky' in the entity, which is the 'talky' column in the database which cannot be null.

We'd normally use a database migration to ensure the table 'example' above exists, but in our case here, let's just create it in the database directly. We're also creating a sequence table so JPA can create unique primary keys.

psql -h localhost -U test_username -d test_database -c "create table example (id bigserial not null primary key, talky varchar(1000) not null);"
psql -h localhost -U test_username -d test_database -c "create table sequence (seq_name varchar(50) not null primary key, seq_count int);insert into sequence (seq_name, seq_count) values('SEQ_GEN', 1);"    

Now let's create a new request that create a JPA connections, add something to our database and lists everything in it to return.

The comments should example the basics of JPA and EntityManagers.

echo '
package com.example.YOURPROJECT;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.log4j.Logger;

@Path("example_jpa")
public class ExampleJPARequest {

	@Path("{example}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<ExampleResource> example(@PathParam("example") String example) {
		// Get the EntityManager by creating an EntityManagerFactory via the persistence-unit name we provided.
		EntityManager entityManager = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME").createEntityManager();
		EntityTransaction transaction = entityManager.getTransaction(); // Not useful here, but useful to see
		List<ExampleEntity> list  = null;
		try {
			transaction.begin();
			// Add an entity
			ExampleEntity entity = new ExampleEntity();
			entity.setTalky(example);
			entityManager.persist(entity);
			// List entities, via the named query we defined in mapping.xml
			TypedQuery<ExampleEntity> nq = entityManager.createNamedQuery("list", ExampleEntity.class);
			list = nq.getResultList();
			// Commit the transaction
			transaction.commit();
		} catch (Exception e) {
			Logger.getLogger(getClass()).error("Problem persisting", e);
			transaction.rollback();
			throw e; // Ergo showing a 500 error. You may want to throw an exception that is not detailing stuff about your JPA connection
		} finally {
			entityManager.clear(); // Clears all the entities from the EntityManager
			entityManager.close();
		}
		
		// Adapt the entities into objects to return as JSON
		ArrayList<ExampleResource> resList = new ArrayList<ExampleResource>();
		for (ExampleEntity exampleEntity : list) {
			ExampleResource exampleItem = new ExampleResource();
			exampleItem.setStuff(exampleEntity.getTalky());
			resList.add(exampleItem);
		}
		return resList;
	}
}
' > src/main/java/com/example/YOURPROJECT/ExampleJPARequest.java

You should normally separate the database layer and entity adapters from the request layer, which can do nicely with Jersey's dependency injection, whic we'll come to later.

We can again run the project to see it in action:

gradle build
java -jar jetty-runner-9.1.0.M0.jar --port 8081 build/libs/YOUR_PROJECT_DIR.war

If you visit

http://localhost:8081/YOUR_PATH/example_jpa/ONE

and then visit

http://localhost:8081/YOUR_PATH/example_jpa/TWO

You should see the JSON

[{"stuff":"ONE"},{"stuff":"TWO"}]
java java-jpa java-web-quick-start java-jersey

Java: Testing a REST service with a clean database (using sqlite)

You can test REST responses like so with Jersey's client api.

	YourResponseObject result = service
		.path("somepath")
		.type(MediaType.APPLICATION_JSON)
		.put(YourResponseObject.class, yourInputObject);

	assertTrue(result.isSuccessful());

But your responses may depend on the state of your database.

And since you're not running your tests from a WAR, or what have you, you have no direct access to populate its seed or delete it.

The best way to do this is to create a rest method to clear the database to use during development, and remove in production.

The method to delete the database would look like:

	EntityTransaction trans = mEntityManager.getTransaction();
	trans.begin();
	Query q = mEntityManager.createQuery("delete from UserEntity");
	q.executeUpdate();
	trans.commit();
	closeEntityConnection();
	

It may be possible access the JPA database if the tests are run in a WAR, but I haven't tried that. Any experience would be welcomed in the comments.

java java-testing java-jersey java-jpa

Tomcat 7: Testing REST interfaces with Jersey

You can test REST services with Jersey's client library easily enough.

 public class RestTest {
  @Test
  public void addAnotherThing() {
    // Arrange
    ClientConfig config = new DefaultClientConfig();
    Client client = Client.create(config);
    WebResource service = client.resource(getBaseURI());
    // Act
    String result = service.path("rest").path("r").accept(MediaType.TEXT_PLAIN).get(String.class);
    // Assert
    assertEquals("Get should be 'rest'", "rest", result);
   }     
   private static URI getBaseURI() {
     return UriBuilder.fromUri("http://localhost:8080/hiya/rest/").build();
   }
 }

In this you use the Jersey ClientConfig, Client and WebResource to eventually make a url request that accepts plain text and get a string from that, finally asserting that to be a specific value.

java java-jersey

Page 2 of 3
prev next
Click me