Jersey Notes
Here are random notes on Jersey as I feel the urge. Topics:
Some links...
Serializing/deserializing POJOs
Using String in place of ObjectId
Servlet filters
Filter example: implementing per-thread local store in a servlet
Servlet listeners
Jersey steps into service code...
Phantom start-up errors...
JSR-311, JAX-RS 1.1
JSR-339, JAX-RS 2.0
This Java Specification Request (JSR) will develop an API for providing support for RESTful(Representational State Transfer) Web Services in the Java Platform.
Some links...
To keep my services very tidy, I need to begin handling errors carefully now.
Here are some examples and other excellent links that might be helpful.
Serializing/deserializing POJOs
In general the type in Java of the method parameter may:
be a primitive type;
Have a constructor that accepts a single String argument;
Have a static method named
valueOf() or
fromString()
that accepts a single String argument. See, for example,
Integer.valueOf( String ) and
java.util.UUID.fromString( String ) )
or...
Be List< T > , Set< T > or
SortedSet< T > , where T satisfies 2 or 3
above. The resulting collection is read-only.
Using String in place of ObjectId in a DTO
The reason for using String is because Jersey leaves it null
in the returned DTO. To get around this, consider the previous section?
Servlet filters
Jersey has filters it respects that can do some of what servlet filters in
other frameworks. There are three I know about so far. I have used the second
two, both in combination with the first in terms of what methods to override.
ResourceFilter
ContainerRequestFilter
ContainerResponseFilter
These filters preconise a number of methods that you should override:
ResourceFilter
getRequestFilter()
getResponseFilter()
ContainerRequestFilter
filter( ContainerRequest request )
ContainerResponseFilter
filter( ContainerRequest request, ContainerResponse response )
Usage
Assume you want to intercept requests and treat them somehow before they reach
your ReST service handling code. Maybe you want to authenticate them. You'll
create a filter that implements ContainerRequestFilter plus
ResourceFilter .
If you wish to intercept a response, which will go back out as the result of a
request, you'll create a filter that implements ContainerResponseFilter
plus ResourceFilter .
From the filter() , return the request (or response) if you want Jersey
to proceed. If you return nil, the request is considered a failure.
In both cases, you'll do your important work in the filter() method
while providing access to the filter via the other two methods. For a request
filter, this:
@Override
public ContainerRequestFilter getRequestFilter()
{
return this;
}
@Override
public ContainerResponseFilter getResponseFilter()
{
return null;
}
...and for a response filter:
@Override
public ContainerRequestFilter getRequestFilter()
{
return null;
}
@Override
public ContainerResponseFilter getResponseFilter()
{
return this;
}
Example: implementing per-thread local store in a servlet
(Or, "thread context.")
Let's imagine a way to keep state during a path through a ReST servlet.
Let's imaging that we're going to record the IP address of our HTTP
request and some other data which we won't go to the trouble of
making an example of. This aspect of what we're showing here is
part of a separate set of notes
(
per-thread local store ).
ThreadContextCreationFilter.java :
package com.acme.web.filter;
import org.apache.log4j.Logger;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.sun.jersey.spi.container.ResourceFilter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import com.acme.web.user.thread.ThreadContext;
public class ThreadContextCreationFilter implements ResourceFilter, ContainerRequestFilter
{
private static Logger log = Logger.getLogger( ThreadContextCreationFilter.class );
@Context HttpServletRequest httpRequest;
public ThreadContextCreationFilter()
{
log.info( "ThreadContextCreationFilter is installed" );
}
/**
* Set up per-thread context for gathering state, for example, that will be used
* to issue statements to the log.
*/
@Override
public ContainerRequest filter( ContainerRequest request )
{
ThreadContext ctx = ThreadContext.get(); // get our thread-local store...
URI uri = request.getRequestUri();
ctx.setIpaddress( httpRequest.getRemoteAddr() );// get the caller's IP address...
...
return request;
}
@Override
public ContainerRequestFilter getRequestFilter()
{
return this;
}
@Override
public ContainerResponseFilter getResponseFilter()
{
return null;
}
}
ThreadContextTearDownFilter.java :
package com.acme.web.filter;
import org.apache.log4j.Logger;
public class ThreadContextTearDownFilter implements ResourceFilter, ContainerResponseFilter
{
private static Logger log = Logger.getLogger( ThreadContextTearDownFilter.class );
public ThreadContextTearDownFilter()
{
log.info( "ThreadContextTearDownFilter is installed" );
}
/**
* Tear down per-thread context here (see ThreadContextCreationFilter) because
* Tomcat likely will not clear it off our thread before putting it back into its
* pool. This would otherwise result in a memory leak.
*/
@Override
public ContainerResponse filter( ContainerRequest request, ContainerResponse response )
{
ThreadContext.remove();
return response;
}
@Override
public ContainerRequestFilter getRequestFilter()
{
return null;
}
@Override
public ContainerResponseFilter getResponseFilter()
{
return this;
}
}
web.xml registration
These are registered in web.xml as init-param s. If you have
two of the same sort, i.e.: two ContainerRequestFilters , you list
them with a comma as here where we're also showing the rest of the
servlet definition of an imaginary servlet:
<servlet>
<servlet-name> Jersey REST Service </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.acme.web.user.service </param-value>
</init-param>
<!-- Do not disturb the order of these two request filters! -->
<init-param>
<param-name> com.sun.jersey.spi.container.ContainerRequestFilters </param-name>
<param-value> com.acme.web.filter.ThreadContextCreationFilter,com.acme.web.filter.HeaderDetectionFilter </param-value>
</init-param>
<init-param>
<param-name> com.sun.jersey.spi.container.ContainerResponseFilters </param-name>
<param-value> com.acme.web.filter.ThreadContextTearDownFilter </param-value>
</init-param>
<load-on-startup> 1 </load-on-startup>
</servlet>
Above, HeaderDetectionFilter is registered only to show how
two or more of these are specified (the mechanism might not have been
obvious if you've never done this before and, if you have, you're
probably not reading this).
Servlet listeners
A concrete example: implementing application start-up
In web.xml , you'll need to add:
Acme Application
com.acme.web.filter.ApplicationInitialize
This tells Tomcat there's code listening for its start-up. Here's the code. I'm
just doing an imaginary MongoDB start-up so that MongoDB's up and running before
Jersey lets the first service request through to me.
package com.acem.web.filter;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
import com.acme.web.mongodb.MongoDB;
import com.acme.web.user.configuration.ApplicationProperties;
public class ApplicationInitialize implements ServletContextListener
{
private static Logger log = Logger.getLogger( ApplicationInitialize.class );
/**
* This method is called when the servlet context is initialized (when the Web application is
* launched). You can initialize servlet context-related data here. We use this listener to
* ensure that we're all up and running before any of our services get registered.
*/
@Override
public void contextInitialized( ServletContextEvent event )
{
log.info( "################################################### Initializing acme application context here:" );
log.info( "Forcing discovery of application properties from " + ApplicationProperties.APPLICATION_PROPERTIES );
ApplicationProperties.reloadProperties();
log.info( "Setting up MongoDB connections..." );
MongoDB.startup();
log.info( "################################################### Initializing acme application complete!" );
}
/**
* This method is invoked when the Servlet Context (the Web application) is undeployed or server shuts down.
*/
@Override
public void contextDestroyed( ServletContextEvent event )
{
log.info( "Discard acme application context here!" );
log.info( "Cleaning up MongoDB connections..." );
MongoDB.cleanup();
}
}
What do you see at Tomcat start-up? In green
are the signs your servlet filters are registered. In bold black is the
listener-registered application start-up noise. (Tomcat start-up noise in the
Eclipse console is always red .)
Feb 6, 2013 6:06:45 PM org.apache.catalina.core.AprLifecycleListener init
Feb 6, 2013 6:06:46 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:accountmgr' did not find a matching property.
Feb 6, 2013 6:06:46 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8000
Feb 6, 2013 6:06:46 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 633 ms
Feb 6, 2013 6:06:46 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Feb 6, 2013 6:06:46 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.35
18:06:46,747 INFO AppInitializer:48 - ################################################### Initializing acme application context here:
18:06:46,751 INFO AppInitializer:50 - Forcing discovery of application properties from /opt/acme/application/conf/application.properties
18:06:46,761 DEBUG ApplicationProperties:88 - Reloading properties on request
18:06:46,762 INFO SecurityProperties:54 - (Re)loading security properties...
18:06:46,762 INFO AccountDatabaseProperties:96 - (Re)loading AccountDatabase properties...
18:06:46,769 INFO PaymentDatabaseProperties:78 - (Re)loading PaymentDatabase properties...
18:06:46,769 INFO ApplicationInitialize:55 - Setting up MongoDB connections...
18:06:46,793 TRACE MongoDB:67 - MongoDB getInstance()
18:06:46,795 INFO MongoDB:38 - --------------- accountdb database replica connection details -------------------------
18:06:47,039 ERROR MongoDB:61 - [ ] acmedb01:37017, Staging
18:06:47,235 ERROR MongoDB:61 - [ ] acmedb02:37018, Americas
18:06:47,506 ERROR MongoDB:61 - [ ] acmedb03:37019, Europe-Middle East-Africa
18:06:47,711 ERROR MongoDB:61 - [ ] acmedb04:37020, Asia-Pacific
18:06:48,047 ERROR MongoDB:61 - [ ] acmedb05:37021, Timbuktu
18:06:48,048 INFO MongoDB:74 - [ x ] localhost:27017, trying this hostname:port because nothing reachable or specified
18:06:48,048 INFO MongoDB:82 - ---------------------------------------------------------------------------------------
18:06:48,158 INFO MongoDB:95 - Collections present: [accounts, addresses, payments, system.indexes]
18:06:48,241 INFO AppInitializer:63 - ################################################### Initializing acme application complete!
Feb 6, 2013 6:06:48 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
com.acme.web.user.service
Feb 6, 2013 6:06:48 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
class com.acme.web.user.service.AccountService
class com.acme.web.user.service.PaymentService
class com.acme.web.user.service.AuthenticationService
class com.acme.web.user.service.PingService
class com.acme.web.user.service.AddressService
Feb 6, 2013 6:06:48 PM com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Feb 6, 2013 6:06:48 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.16 11/28/2012 02:09 PM'
18:06:48,614 INFO ThreadContextCreationFilter:29 - ThreadContextCreationFilter is installed
18:06:48,626 INFO ThreadContextTearDownFilter:18 - ThreadContextTearDownFilter is installed
Feb 6, 2013 6:06:49 PM com.sun.jersey.spi.inject.Errors processErrorMessages
Feb 6, 2013 6:06:49 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8000
Feb 6, 2013 6:06:49 PM org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
Feb 6, 2013 6:06:49 PM org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/48 config=null
Feb 6, 2013 6:06:49 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 3410 ms
Jackson resolvers
import java.text.SimpleDateFormat;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
@Provider
@Produces( "application/json" )
public class JacksonContextResolver implements ContextResolver< ObjectMapper >
{
private static final String FORMAT_STR = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
private ObjectMapper mapper = new ObjectMapper();
@SuppressWarnings( "deprecation" )
public JacksonContextResolver()
{
SerializationConfig serialConfig = mapper.getSerializationConfig();
DeserializationConfig deserialConfig = mapper.getDeserializationConfig();
serialConfig.setDateFormat( new SimpleDateFormat( FORMAT_STR ) );
deserialConfig.setDateFormat( new SimpleDateFormat( FORMAT_STR ) );
mapper.configure( SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false );
}
@Override
public ObjectMapper getContext( Class< ? > arg0 )
{
return mapper;
}
}
Jersey steps into service code...
When the HTTP(S) request makes it into the service code, it's already passed
through all filters registered in web.xml . There are 1) resource filters,
2) container request filters and 3) container response filters. The last type are
filters that run after the thread has run completely through the service
code. For example,
.
.
.
<init-param>
<param-name> com.sun.jersey.spi.container.ResourceFilters </param-name>
<param-value> com.etretatlogiciels.cloudmgr.filters.ApplicationFilter </param-value>
</init-param>
<init-param>
<param-name> com.sun.jersey.spi.container.ContainerRequestFilters </param-name>
<param-value> com.etretatlogiciels.cloudmgr.filters.ContextCreationFilter </param-value>
</init-param>
<init-param>
<param-name> com.sun.jersey.spi.container.ContainerResponseFilters </param-name>
<param-value> com.etretatlogiciels.cloudmgr.filters.ContextTearDownFilter </param-value>
</init-param>
.
.
.
Execution will step into ApplicationFilter which, in my case, will
add other filters (one to check SSL, a caller key, and an OAuth token), plus
erect (and tear down afterward) some thread context in ContextCreationFilter
and ContextTearDownFilter .
Stepping into the called service
Depending on the @Path registered with the service code, Jersey then passes
execution into the service to be called. If there is a no-argument constructor, it
calls that. (So, if you want or need arguments passed to the service's constructor,
you are out of luck as far as I know at this point.)
Phantom start-up errors in Jersey...
I found that if something like this occurred on start-up:
WARNING: Marking servlet Jersey REST Service as unavailable
SEVERE: Error loading WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@56f631
com.sun.jersey.spi.container.servlet.ServletContainer
java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer
The solution seemed to be to re-build and clean it before asking Tomcat to
start it again.
A Jersey testing example...
This is one person's way to test a Jersey entry point if that's what you wish
to do.
SnDJerseyTest.java :
package com.acme.snd;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import com.sun.jersey.test.framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory;
public class SnDJerseyTest extends JerseyTest
{
public SnDJerseyTest() throws Exception
{
super( "com.acme.snd.resource" );
}
@Override
protected TestContainerFactory getTestContainerFactory()
{
return new GrizzlyWebTestContainerFactory();
}
}
SnDResourceTest.java :
package com.acme.snd.resource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import javax.ws.rs.core.MediaType;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.acme.snd.SnDJerseyTest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class SnDResourceTest extends SnDJerseyTest
{
public SnDResourceTest() throws Exception { super(); }
@Mock
private CTenantResource mockTenantResource;
@Before
public void setup()
{
MockitoAnnotations.initMocks( this );
}
@Test
public void test_pingSndResource()
{
WebResource webResource = resource();
String responseMsg = webResource.path( "snd" ).get( String.class );
assertNotNull( responseMsg );
}
@Test
public void test_getPingResource()
{
WebResource webResource = resource();
ClientResponse response = webResource.path( "snd/ping").accept( MediaType.APPLICATION_XML ).get( ClientResponse.class );
assertNotNull( response );
assertEquals( 200, response.getStatus() );
}
}
More recent Jersey work...
Jersey libraries for a server, version 2.x (2.7). Do not be tempted by
something called, Jersey bundle.
I found this at:
https://jersey.java.net/download.html .
There is a link for the convenience of non-Maven users on that page; that link
is:
http://repo1.maven.org/maven2/org/glassfish/jersey/bundles/jaxrs-ri/2.7/jaxrs-ri-2.7.zip .
Once you get that, unzip jaxrs-ri-2.7.zip (ri stands for "reference
implementation"—Jersey is the reference implementation of JAX-RS) you'll find:
~/Downloads $ tree jaxrs-ri/
jaxrs-ri/
├── api
│ └── javax.ws.rs-api-2.0.jar
├── ext
│ ├── aopalliance-repackaged-2.2.0.jar
│ ├── asm-all-repackaged-2.2.0.jar
│ ├── hk2-api-2.2.0.jar
│ ├── hk2-locator-2.2.0.jar
│ ├── hk2-utils-2.2.0.jar
│ ├── javassist-3.18.1-GA.jar
│ ├── javax.annotation-api-1.2.jar
│ ├── javax.inject-2.2.0.jar
│ ├── javax.servlet-api-3.0.1.jar
│ ├── jaxb-api-2.2.7.jar
│ ├── jersey-guava-2.7.jar
│ ├── org.osgi.core-4.2.0.jar
│ ├── osgi-resource-locator-1.0.1.jar
│ ├── persistence-api-1.0.jar
│ └── validation-api-1.1.0.Final.jar
├── Jersey-LICENSE.txt
├── lib
│ ├── jersey-client.jar
│ ├── jersey-common.jar
│ ├── jersey-container-servlet-core.jar
│ ├── jersey-container-servlet.jar
│ └── jersey-server.jar
└── third-party-license-readme.txt
You will need all bold above as shown below. Depending on what you do in your
server application (restlet), you might need some or all of the others.
However, for the two, simple entry points illustrated in code below, you need
only these:
ext/hk2-api-2.2.0.jar
ext/hk2-locator-2.2.0.jar
ext/hk2-utils-2.2.0.jar
ext/javassist-3.18-GA.jar
ext/javax.annotation-api-1.2.jar
ext/javax.inject-2.2.0.jar
ext/validation-api-1.1.0-Final.jar
jersey-client.jar*
jersey-common.jar
jersey-container-servlet-core.jar
jersey-container-servlet.jar
jersey-guava.jar
jersey-server.jar
javax.ws.rs-api-2.0.jar
* Yes, you do have to have the Jersey client JAR! Otherwise, you can
expect the follow error out of Tomcat on start-up:
WARNING: HK2 service reification failed for [org.glassfish.jersey.server.internal.inject.WebTargetValueFactoryProvider] with an exception:
MultiException stack 1 of 4
java.lang.NoClassDefFoundError: org/glassfish/jersey/client/ClientConfig
JsonTrackService.java :
package com.mkyong;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path( "/json/metallica" )
public class JsonTrackService
{
/* GET, http://localhost:8080/mkyong-rest/rest/json/metallica/get
*
* 200 OK
* {"title":"Enter Sandman","singer":"Metallica"}
*/
@GET
@Path( "/get" )
@Produces( MediaType.APPLICATION_JSON )
public Response getTrackInJson()
{
TrackPojo track = new TrackPojo();
track.setTitle( "Enter Sandman" );
track.setSinger( "Metallica" );
return Response.ok( track.toString() ).build();
}
@POST
@Path( "/post" )
@Consumes( MediaType.APPLICATION_JSON )
public Response createTrackInJson( TrackPojo track )
{
String result = "TrackPojo saved : " + track;
return Response.status( 201 ).entity( result ).build();
}
}
TrackPojo.java :
package com.mkyong;
public class TrackPojo
{
String title;
String singer;
public String getTitle() { return title; }
public void setTitle( String title ) { this.title = title; }
public String getSinger() { return singer; }
public void setSinger( String singer ) { this.singer = singer; }
@Override
public String toString()
{
return "TrackPojo [title=" + title + ", singer=" + singer + "]";
}
}
web.xml:
The preceding JARs support this web.xml. We launch using Tomcat 6. (What's commented out are older definitions before everything was rolled into GlassFish.)
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>mkyong</display-name>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<!-- <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> -->
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<!-- <param-name>com.sun.jersey.config.property.packages</param-name> -->
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.mkyong</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
URL and output from this restlet are:
http://localhost:8080/mkyong/rest/json/metallica/get
TrackPojo [title=Enter Sandman, singer=Metallica]
POJO to JSON, JSON to POJO
See
http://stackoverflow.com/questions/17568469/jersey-2-0-equivalent-to-pojomappingfeature .
Tried...
.
.
.
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
.
.
.
jackson-jaxrs-1.9.11.jar with web.xml paragraph above
jackson-core-2.4.0-20140329.040435-28.jar without
Also, ...
http://stackoverflow.com/questions/18646747/how-to-produce-json-output-with-jersey-1-17-1-using-jaxb
http://blog.denevell.org/tomcat7-rest-jersey.html
http://www.nilzorblog.com/2013/02/a-helloworld-example-of-jersey-rest.html
http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
http://www.unknownerror.org/Problem/index/621601671/jersey-1171-pojo-mapping-feature-server-and-how-to-get-from-client/
http://crazytechbuddy.blogspot.com/2012/06/making-jersey-to-use-jackson-instead-of.html
https://gist.github.com/amolbrid/1238254
http://examples.javacodegeeks.com/enterprise-java/rest/jersey/json-example-with-jersey-jackson/
http://ffbit.com/blog/2013/10/15/upgrade-jersy-to-use-jackson-2-dot-x/
http://stackoverflow.com/questions/22208369/messagebodyreader-not-found-for-media-type-application-json-when-migrating-from
Random 1...
Please examine what's in bold below. It's the service entry point in response
to:
http://ftinit-acme-02.a.acme.net:8080/acme-write/persons?createUpdateLogEntries=true
The Acmecom is passed in as the payload.
@Path("persons")
public class PersonsWriteWebService
{
@PUT
@Consumes( { AcmecomxConstants.ACMECOMX_XML_MEDIA_TYPE, MediaType.APPLICATION_XML } )
@Produces( { AcmecomxConstants.ACMECOMX_XML_MEDIA_TYPE, MediaType.APPLICATION_XML } )
public Response setPerson( Acmecomx acmecomx,
@QueryParam( value = "createUpdateLogEntries" )
@DefaultValue( value = "true" ) boolean createUpdateLogEntries
)
throws AcmeWSException
{
if( acmecomx == null )
throw new AcmeWSException( "Invalid data when calling this endpoint. Acmecomx must be defined." );
try
{
return Response.ok( getPersonsWorker().processPersons( acmecomx, createUpdateLogEntries ) ).build();
}
catch( AcmeException e )
{
StringBuilder builder = new StringBuilder( "599 AcmexWrite " );
builder.append( e.getMessage() );
String message = builder.toString();
LOG.warn( message, e );
return Response.ok( acmecomx ).header( "Warning", message ).build();
}
}
Random 2...
http://java.dzone.com/articles/jax-rs-20-custom-content
Random 3...
Jersey ReST Streaming
@GET
@Produces( MediaType.TEXT_PLAIN )
public Response streamExample()
{
StreamingOutput stream = new StreamingOutput()
{
@Override
public void write( OutputStream os ) throws IOException,
WebApplicationException
{
Writer writer = new BufferedWriter( new OutputStreamWriter( os ) );
writer.write( "test" );
writer.flush();
}
};
return Response.ok( stream ).build();
}
From
http://stackoverflow.com/questions/12012724/jersey-example-of-using-streamingoutput-as-response-entity/12012867#12012867
and see also
http://java.dzone.com/articles/jerseyjax-rs-streaming-json
JAX-RS, JAXB (XML) and JSON
JAXB defines standardized metadata and runtime API for converting Java domain objects to/from XML.
JAX-RS defines standardized metadata and runtime API for the creation of RESTful services. By default for the application/xml media type JAX-RS will use JAXB to convert the objects to/from XML. (To get application/json is covered separately.)
Example
In the following example when a GET operation is performed the JAX-RS implementation will return a Customer . A JAXB implementation will be used to convert that instance of Customer to the XML that the client will actually receive.
package org.example.service;
import javax.ejb.*;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import org.example.model.*;
@Stateless
@LocalBean
@Path( "/customers" )
public class CustomerResource
{
@GET
@Produces( MediaType.APPLICATION_XML )
@Path( "{id}" )
public Customer read( @PathParam( "id" ) int id )
{
Customer customer = new Customer();
customer.setId( id );
customer.setFirstName( "Jane" );
customer.setLastName( null );
PhoneNumber pn = new PhoneNumber();
pn.setType( "work" );
pn.setValue( "5551111" );
customer.getPhoneNumbers().add( pn );
return customer;
}
}
JSON
You can override the default handling of application/xml by providing a custom
MessageBodyReader /MessageBodyWriter . For other media types it depends. The
application/json media type is popular but JAX-RS does not define what the default
binding should be, implementations have come up with their own defaults. Here is an example:
blog.bdoughan.com/2013/07/oracle-weblogic-1212-now-with.html .
MessageBodyReader/Writer
https://jersey.java.net/documentation/latest/message-body-workers.html#d0e4072
Jersey, via JAXB, contains internally entity providers to convert in and out of XML behind
the scenes:
POST/PUT XML request payload -> POJO
GET POJO -> response payload
(Make a table of this:)
Other types supported:
byte[] (*/*)
String (ibid)
InputStream (ibid)
Reader (ibid)
File (ibid)
DataSource (ibid)
Source (text/xml, application/xml, application/*+xml)
JAXBElement (ibid)
MultivaluedMap< K, V > (application/x-www-form-urlencoded)
Form (application/x-www-form-urlencoded)
StreamingOutput (*/*)
Boolean, Character and Number (text/plain)
CodeHaus writes a JSON provider.
org.codehaus.jackson.jaxrs.JacksonProvider.{ readFrom(), writeTo() }
Predictably good example:
http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/