Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

Chapter 2. Developing JAX-RS Web Services

JAX-RS is the Java API for RESTful web services. It provides support for building web services using REST, through the use of annotations. These annotations simplify the process of mapping Java objects to web resources.

RESTEasy is the Red Hat JBoss Enterprise Application Platform 7 implementation of JAX-RS and is fully compliant with the JSR-000339 Java API for RESTful Web Services 2.0 specification. It also provides additional features to the specification.

To get started with JAX-RS, see the helloworld-rs, jax-rs-client, and kitchensink quickstarts that ship with Red Hat JBoss Enterprise Application Platform 7.

Note

JBoss EAP does not support the resteasy-crypto, resteasy-yaml-provider, and jose-jwt modules.

2.1. JAX-RS Application

When creating providers and web resources, you have the following options for declaring them:

  • Simple subclassing of javax.ws.rs.core.Application without a web.xml
  • Using a web.xml
  • Subclassing javax.ws.rs.core.Application and providing a custom implementation

2.1.1. Simple Subclassing javax.ws.rs.core.Application

You can use the javax.ws.rs.core.Application class to create a subclass which declares those providers and web resources. This class is provided by the RESTEasy libraries included with JBoss EAP.

To configure a resource or provider using javax.ws.rs.core.Application, simply create a class that extends it and add an @ApplicationPath annotation:

Example Application Class

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/root-path")
public class MyApplication extends Application
{
}

2.1.2. Using web.xml

Alternatively, if you do not wish to create a class that extends javax.ws.rs.core.Application you can add the following to your web.xml:

Example web.xml

<web-app version="3.0" 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_3_0.xsd">
  <servlet>
      <servlet-name>javax.ws.rs.core.Application</servlet-name>
  </servlet>
  <servlet-mapping>
      <servlet-name>javax.ws.rs.core.Application</servlet-name>
      <url-pattern>/root-path/*</url-pattern>
  </servlet-mapping>
  ...
</web-app>

2.1.3. Subclassing javax.ws.rs.core.Application with a Custom Implementation

When subclassing javax.ws.rs.core.Application you may choose to provide a custom implementation for any of the existing methods. The getClasses and getSingletons methods return a collection of classes or singletons that must be included in the published JAX-RS application.

  • If either getClasses and getSingletons returns a non-empty collection, only those classes and singletons are published in the JAX-RS application.
  • If both getClasses and getSingletons return an empty collection, then all root resource classes and providers that are packaged in the web application are included in the JAX-RS application. RESTEasy will then automatically discover those resources.

2.2. JAX-RS Client

2.2.1. JAX-RS 2.0 Client API

JAX-RS 2.0 introduces a new client API to send HTTP requests to remote RESTful web services. It is a fluent request building API with 3 main classes:

  • Client
  • WebTarget
  • Response

The Client interface is a builder of WebTarget instances. The WebTarget represents a distinct URL or URL template to build sub-resource WebTargets or invoke requests on.

There are two ways to create a client: the standard way, or using the ResteasyClientBuilder class. The advantage of using the ResteasyClientBuilder class is that it provides a few more helper methods to configure your client.

Using the Standard Way to Create a Client

The following example shows one of the standard ways to create a client:

Client client = ClientBuilder.newClient();

Alternatively, you can use another standard way to create a client as shown in the example below:

Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://foo.com/resource");
Response response = target.request().get();
String value = response.readEntity(String.class);
response.close();  // You should close connections!
Using the ResteasyClientBuilder Class to Create a Client
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://foo.com/resource");

RESTEasy automatically loads a set of default providers that includes all classes listed in the META-INF/services/javax.ws.rs.ext.Providers file. Additionally, you can manually register other providers, filters, and interceptors through the configuration object provided by the method call Client.configuration(). Configuration also lets you set configuration properties that might be needed.

Each WebTarget has a Configuration instance, which inherits the components and properties registered with the parent instance. This lets you set specific configuration options for each target resource. For example, username and password.

Using RESTEasy Client Classes

You must add the following dependency for users of the Maven project:

<dependency>
	<groupId>org.jboss.resteasy</groupId>
	<artifactId>resteasy-client</artifactId>
	<version>VERSION_IN_EAP</version>
 </dependency>
Client-side Filters

The client side has two types of filters:

ClientRequestFilter
A ClientRequestFilter runs before an HTTP request is sent over the wire to the server. The ClientRequestFilter is also allowed to abort the request execution and provide a canned response without going over the wire to the server.
ClientResponseFilter
A ClientResponseFilter runs after a response is received from the server, but before the response body is unmarshalled. The ClientResponseFilter can modify the response object before it is handed to the application code. For example:
// execute request filters
for (ClientRequestFilter filter : requestFilters) {
	filter.filter(requestContext);
	if (isAborted(requestContext)) {
    return requestContext.getAbortedResponseObject();
  }
}

// send request over the wire
response = sendRequest(request);

// execute response filters
for (ClientResponseFilter filter : responseFilters) {
	filter.filter(requestContext, responseContext);
}
Register Client-side Filters to the Client Request

The following example shows how to register the client-side filters to the client request:

client = ClientBuilder.newClient();
WebTarget base = client.target(generateURL("/") + "get");
base.register(ClientExceptionsCustomClientResponseFilter.class).request("text/plain").get();
Client-side Cache

RESTEasy has the ability to set up a client-side cache. This cache looks for cache-control headers sent back with a server response. If the cache-control headers specify that the client is allowed to cache the response, RESTEasy caches it within the local memory.

ResteasyWebTarget target = client.target(generateBaseUrl());
target.register(BrowserCacheFeature.class);

2.2.2. Implementing RESTEasy with HTTP Client

By default, network communication between the client and server in RESTEasy is handled by HttpClient (4.x) from the Apache HttpComponents project.

The interface between the RESTEasy Client Framework and network is found in an implementation of org.jboss.resteasy.client.jaxrs.ClientHttpEngine, and org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine, which uses HttpClient (4.x), is the default implementation. RESTEasy also ships with the following client engines, all found in the org.jboss.resteasy.client.jaxrs.engines package:

  • URLConnectionClientExecutor: Uses java.net.HttpURLConnection.
  • InMemoryClientExecutor: Dispatches requests to a server in the same JVM.

A client executor can be passed to a specific ClientRequest:

ResteasyClient client = new
ResteasyClientBuilder().httpEngine(engine).build();

RESTEasy and HttpClient make default decisions to use the client framework without referencing HttpClient, but for some applications it might be necessary to drill down into the HttpClient details. ApacheHttpClient4Engine can be supplied with an instance of org.apache.http.client.HttpClient and an instance of org.apache.http.protocol.HttpContext, which can carry additional configuration details into the HttpClient layer. For example, authentication can be configured as follows:

// Configure HttpClient to authenticate preemptively
// by prepopulating the authentication data cache.

// 1. Create AuthCache instance
AuthCache authCache = new BasicAuthCache();

// 2. Generate BASIC scheme object and add it to the local auth cache
AuthScheme basicAuth = new BasicScheme();
authCache.put(new HttpHost("sippycups.bluemonkeydiamond.com"), basicAuth);

// 3. Add AuthCache to the execution context
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);

// 4. Create client executor and proxy
HttpClient httpClient = HttpClientBuilder.create().build();
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient, localContext);
ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
Note

It is important to understand the difference between "releasing" a connection and "closing" a connection. Releasing a connection makes it available for reuse. Closing a connection frees its resources and makes it unusable.

RESTEasy releases the connection without notification. The only counterexample is the case in which the response is an instance of InputStream, which must be closed explicitly.

On the other hand, if the result of an invocation is an instance of Response, the Response.close() method must be used to released the connection.

WebTarget target = client.target("http://localhost:8081/customer/123");
Response response = target.request().get();
System.out.println(response.getStatus());
response.close();

You should probably execute this in a try/finally block. Again, releasing a connection only makes it available for another use. It does not normally close the socket.

ApacheHttpClient4Engine.finalize() closes any open sockets, if it created the HttpClient it has been using. It is not safe to rely on JDK to call finalize(). If an HttpClient has been passed into the ApacheHttpClient4Executor, the user is responsible for closing the connections:

HttpClient httpClient = new HttpClientBuilder.create().build();
ApacheHttpClient4Engine executor = new ApacheHttpClient4Engine(httpClient);
...
httpClient.getConnectionManager().shutdown();
Note

If ApacheHttpClient4Engine has created its own instance of HttpClient, it is not necessary to wait for finalize() to close open sockets. The ClientHttpEngine interface has a close() method for this purpose.

Finally, if the javax.ws.rs.client.Client class has created the engine automatically, call Client.close(). This call cleans up any socket connections.

2.3. URL-Based Negotiation

2.3.1. Mapping Extensions to Media Types

Some clients, such as browsers, cannot use the Accept and Accept-Language headers to negotiate the representation media type or language. RESTEasy can map file name suffixes to media types and languages to deal with this issue.

To map media types to file extensions using the web.xml file, you need to add a resteasy.media.type.mappings context param and the list of mappings as the param-value. The list is comma separated and uses colons (:) to delimit the file extension and media type.

Example web.xml Mapping File Extensions to Media Types

<context-param>
    <param-name>resteasy.media.type.mappings</param-name>
    <param-value>html : text/html, json : application/json, xml : application/xml</param-value>
</context-param>

2.3.2. Mapping Extensions to Languages

Some clients, such as browsers, cannot use the Accept and Accept-Language headers to negotiate the representation media type or language. RESTEasy can map file name suffixes to media types and languages to deal with this issue. Follow these steps to map languages to file extensions, in the web.xml file.

To map media types to file extensions using the web.xml file, you need to add a resteasy.language.mappings context param and the list of mappings as the param-value. The list is comma separated and uses colons (:) to delimit the file extension and language type.

Example web.xml Mapping File Extensions to Language Types

<context-param>
    <param-name>resteasy.language.mappings</param-name>
    <param-value> en : en-US, es : es, fr : fr</param-name>
</context-param>

2.4. Content Marshalling and Providers

2.4.1. Default Providers and Default JAX-RS Content Marshalling

RESTEasy can automatically marshal and unmarshal a few different message bodies.

Table 2.1. Supported Media Types and Java Types

Media TypesJava Types

application/* +xml, text/* +xml, application/* +json, application/* +fastinfoset, application/ atom+*

JAXB annotated classes

application/* +xml, text/* +xml

org.w3c.dom.Document

* / *

java.lang.String

* / *

java.io.InputStream

text/plain

primitives, java.lang.String, or any type that has a String constructor, or static valueOf(String) method for input, toString() for output

* / *

javax.activation.DataSource

* / *

java.io.File

* / *

byte

application/x-www-form-urlencoded

javax.ws.rs.core.MultivaluedMap

2.4.2. Content Marshalling with @Provider classes

The JAX-RS specification allows you to plug in your own request/response body reader and writers. To do this, you annotate a class with @Provider and specify the @Produces types for a writer and @Consumes types for a reader. You must also implement a MessageBodyReader/Writer interface.

The RESTEasy ServletContextLoader automatically scans the WEB-INF/lib and classes directories for classes annotated with @Provider, or you can manually configure them in the web.xml file.

2.4.3. Providers Utility Class

javax.ws.rs.ext.Providers is a simple injectable interface that allows you to look up MessageBodyReaders, Writers, ContextResolvers, and ExceptionMappers. It is very useful for implementing multipart providers and content types that embed other random content types.

public interface Providers {
  <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType);
  <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType);
  <T extends="" throwable=""> ExceptionMapper<T> getExceptionMapper(Class<T> type);
  <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType);
}

A Providers instance is injectable into MessageBodyReader or Writers:

@Provider
@Consumes("multipart/fixed")
public class MultipartProvider implements MessageBodyReader {

  private @Context Providers providers;
  ...
}

2.4.4. Configuring Document Marshalling

XML document parsers are subject to a form of attack known as the XXE (XML eXternal Entity) attack, in which expanding an external entity causes an unsafe file to be loaded. For example, the following document could cause the /etc/passwd file to be loaded.

<!--?xml version="1.0"?-->
<!DOCTYPE foo
[<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<search>
 <user>bill</user>
 <file>&xxe;<file>
</search>

By default, the RESTEasy built-in unmarshaller for org.w3c.dom.Document documents does not expand external entities. It replaces them with an empty string. You can configure it to replace external entities with values defined in the DTD. This is done by setting the resteasy.document.expand.entity.references context parameter to true in the web.xml file:

Example Setting the resteasy.document.expand.entity.references Context Parameter

<context-param>
 <param-name>resteasy.document.expand.entity.references</param-name>
 <param-value>true</param-value>
</context-param>

Another way of dealing with the problem is by prohibiting DTDs, which RESTEasy does by default. This behavior can be changed by setting the resteasy.document.secure.disableDTDs context parameter to false.

Example Setting the resteasy.document.secure.disableDTDs Context Parameter

<context-param>
 <param-name>resteasy.document.secure.disableDTDs</param-name>
 <param-value>false</param-value>
</context-param>

Documents are also subject to Denial of Service Attacks when buffers are overrun by large entities or too many attributes. For example, if a DTD defined the following entities, the expansion of &foo6; would result in 1,000,000 foos.

<!--ENTITY foo 'foo'-->
<!--ENTITY foo1 '&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;'-->
<!--ENTITY foo2 '&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;'-->
<!--ENTITY foo3 '&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;'-->
<!--ENTITY foo4 '&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;'-->
<!--ENTITY foo5 '&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;'-->
<!--ENTITY foo6 '&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;'-->

By default, RESTEasy limits the number of expansions and the number of attributes per entity. The exact behavior depends on the underlying parser. The limit can be turned off by setting the resteasy.document.secure.processing.feature context parameter to false.

Example Setting the resteasy.document.secure.processing.feature Context Parameter

<context-param>
 <param-name>resteasy.document.secure.processing.feature</param-name>
 <param-value>false</param-value>
</context-param>

2.4.5. Using MapProvider

You can use MapProvider to accept and return a map with JAX-RS resources.

Example Resource Accepting and Returning a Map

@Path("manipulateMap")
@POST
@Consumes("application/x-www-form-urlencoded")
@Produces("application/x-www-form-urlencoded")
public MultivaluedMap<String, String> manipulateMap(MultivaluedMap<String, String> map) {
  //do something
  return map;
}

You can also send and receive maps to JAX-RS resources using the client.

Example Client

MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();

//add values to the map...

Response response = client.target(generateURL("/manipulateMap"))
                          .request(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
                          .post(Entity.entity(map, MediaType.APPLICATION_FORM_URLENCODED_TYPE));

String data = response.readEntity(String.class);

//handle data...

2.4.6. Converting String Based Annotations to Objects

JAX-RS @*Param annotations, including @QueryParam, @MatrixParam, @HeaderParam, @PathParam, and @FormParam, are represented as strings in a raw HTTP request. These types of injected parameters can be converted to objects if these objects have a valueOf(String) static method or a constructor that takes one String parameter.

If you have a class where the valueOf() method or the string constructor does not exist or is inappropriate for an HTTP request, the JAX-RS 2.0 specification provides the javax.ws.rs.ext.ParamConverterProvider and javax.ws.rs.ext.ParamConverter to help convert the message parameter value to the corresponding custom Java type. ParamConverterProvider must be either programmatically registered in a JAX-RS runtime or must be annotated with @Provider annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.

For example: The steps below demonstrate how to create a custom POJO object. The conversion from message parameter value such as @QueryParam, @PathParam, @MatrixParam, @HeaderParam into POJO object is done by implementation of ParamConverter and ParamConverterProvider interfaces.

  1. Create the custom POJO class.

    public class POJO {
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    }
  2. Create the custom POJO Converter class.

    public class POJOConverter implements ParamConverter<POJO> {
      public POJO fromString(String str) {
        System.out.println("FROM STRNG: " + str);
        POJO pojo = new POJO();
        pojo.setName(str);
        return pojo;
      }
    
      public String toString(POJO value) {
        return value.getName();
      }
    }
  3. Create the custom POJO Converter Provider class.

    public class POJOConverterProvider implements ParamConverterProvider {
      @Override
      public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (!POJO.class.equals(rawType)) return null;
        return (ParamConverter<T>)new POJOConverter();
      }
    }
  4. Create the custom MyResource class.

    @Path("/")
    public class MyResource {
      @Path("{pojo}")
      @PUT
      public void put(@QueryParam("pojo") POJO q, @PathParam("pojo") POJO pp, @MatrixParam("pojo") POJO mp,
        @HeaderParam("pojo") POJO hp) {
        ...
      }
    }

2.4.7. JAXB Providers

2.4.7.1. JAXB and XML Provider

RESTEasy provides JAXB provider support for XML.

@XmlHeader and @Stylesheet

RESTEasy provides setting an XML header using the @org.jboss.resteasy.annotations.providers.jaxb.XmlHeader annotation.

Example Using the @XmlHeader Annotation

@XmlRootElement
public static class Thing {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

@Path("/test")
public static class TestService {

   @GET
   @Path("/header")
   @Produces("application/xml")
   @XmlHeader("<?xml-stylesheet type='text/xsl' href='${baseuri}foo.xsl' ?>")
   public Thing get() {
      Thing thing = new Thing();
      thing.setName("bill");
      return thing;
   }
}

The @XmlHeader ensures that the XML output has an XML-stylesheet header.

RESTEasy has a convenient annotation for stylesheet headers.

Example Using the @Stylesheet Annotation

@XmlRootElement
public static class Thing {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

@Path("/test")
public static class TestService {

   @GET
   @Path("/stylesheet")
   @Produces("application/xml")
   @Stylesheet(type="text/css", href="${basepath}foo.xsl")
   @Junk
   public Thing getStyle() {
      Thing thing = new Thing();
      thing.setName("bill");
      return thing;
   }
}

2.4.7.2. JAXB and JSON Provider

RESTEasy allows you to marshal JAXB annotated POJOs to and from JSON using the JSON provider. This provider wraps the Jackson JSON library to accomplish this task. It has a Java Beans based model and APIs similar to JAXB.

While Jackson already includes JAX-RS integration, it was expanded by RESTEasy. To include it in your project, you need to update the Maven dependencies.

Maven Dependencies for Jackson

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>${version.org.jboss.resteasy}</version>
    <scope>provided</scope>
</dependency>

Note

The default JSON provider for RESTEasy is Jackson2. Previous versions of JBoss EAP included the Jackson1 JSON provider. For more details on migrating your existing applications from the Jackson1 provider, see the JBoss EAP Migration Guide. If you still want to use the Jackson1 provider, you have to explicitly update the Maven dependencies to obtain it.

Note

The default JSON provider for RESTEasy in previous versions of JBoss EAP was Jettison, but is now deprecated in JBoss EAP 7. For more details, see the JBoss EAP Migration Guide.

Example JSON Provider

@XmlRootElement
public static class Thing {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

@Path("/test")
public static class TestService {
  @GET
  @Path("/thing")
  @Produces("application/json")
  public Thing get() {
    Thing thing = new Thing();
    thing.setName("the thing");
    return thing;
  }
}

2.4.7.2.1. Switching the Default Jackson Provider

JBoss EAP 7 includes Jackson 2.6.x or greater and resteasy-jackson2-provider is now the default Jackson provider.

To switch to the default resteasy-jackson-provider that was included in the previous release of JBoss EAP, exclude the new provider and add a dependency for the previous provider in the jboss-deployment-structure.xml application deployment descriptor file.

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
   <deployment>
       <exclusions>
           <module name="org.jboss.resteasy.resteasy-jackson2-provider"/>
       </exclusions>
       <dependencies>
           <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
       </dependencies>
   </deployment>
</jboss-deployment-structure>

2.4.8. Creating JAXB Decorators

RESTEasy’s JAXB providers have a pluggable way to decorate Marshaller and Unmarshaller instances. You can create an annotation that can trigger either a Marshaller or Unmarshaller instance, which can be used to decorate methods.

Create a JAXB Decorator with RESTEasy
  1. Create the Processor Class.

    1. Create a class that implements DecoratorProcessor<Target, Annotation>. The target is either the JAXB Marshaller or Unmarshaller class. The annotation is created in step two.
    2. Annotate the class with @DecorateTypes, and declare the MIME Types the decorator should decorate.
    3. Set properties or values within the decorate function.

      Example Processor Class

      import org.jboss.resteasy.core.interception.DecoratorProcessor;
      import org.jboss.resteasy.annotations.DecorateTypes;
      import javax.xml.bind.Marshaller;
      import javax.xml.bind.PropertyException;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.Produces;
      import java.lang.annotation.Annotation;
      
      @DecorateTypes({"text/*+xml", "application/*+xml"})
      public class PrettyProcessor implements DecoratorProcessor<Marshaller, Pretty> {
          public Marshaller decorate(Marshaller target, Pretty annotation,
            Class type, Annotation[] annotations, MediaType mediaType) {
          target.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
          }
      }

  2. Create the Annotation.

    1. Create a custom interface that is annotated with the @Decorator annotation.
    2. Declare the processor and target for the @Decorator annotation. The processor is created in step one. The target is either the JAXB Marshaller or Unmarshaller class.

      Example Annotation

      import org.jboss.resteasy.annotations.Decorator;
      
      @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      @Decorator(processor = PrettyProcessor.class, target = Marshaller.class)
      public @interface Pretty {}

  3. Add the annotation created in step two to a function so that either the input or output is decorated when it is marshaled.

You have now created a JAXB decorator, which can be applied within a JAX-RS web service.

2.4.9. Multipart Providers in JAX-RS

The multipart MIME format is used to pass lists of content bodies embedded in one message. One example of a multipart MIME format is the multipart/form-data MIME type. This is often found in web application HTML form documents and is generally used to upload files. The form-data format in this MIME type is the same as other multipart formats, except that each inlined piece of content has a name associated with it.

RESTEasy allows for the multipart/form-data and multipart/* MIME types. RESTEasy also provides a custom API for reading and writing multipart types as well as marshalling arbitrary List (for any multipart type) and Map (multipart/form-data only) objects.

Important

There are a lot of frameworks doing multipart parsing automatically with the help of filters and interceptors, such as org.jboss.seam.web.MultipartFilter in Seam or org.springframework.web.multipart.MultipartResolver in Spring. However, the incoming multipart request stream can be parsed only once. RESTEasy users working with multipart should make sure that nothing parses the stream before RESTEasy gets it.

2.4.9.1. Input with Multipart Data

When writing a JAX-RS service, RESTEasy provides the org.jboss.resteasy.plugins.providers.multipart.MultipartInput interface to allow you to read in any multipart MIME type.

package org.jboss.resteasy.plugins.providers.multipart;

public interface MultipartInput {

   List<InputPart> getParts();
   String getPreamble();

   // You must call close to delete any temporary files created
   // Otherwise they will be deleted on garbage collection or on JVM exit
   void close();
}

public interface InputPart {

   MultivaluedMap<String, String> getHeaders();
   String getBodyAsString();
   <T> T getBody(Class<T> type, Type genericType) throws IOException;
   <T> T getBody(org.jboss.resteasy.util.GenericType<T> type) throws IOException;
   MediaType getMediaType();
   boolean isContentTypeFromMessage();
}

MultipartInput is a simple interface that allows you to get access to each part of the multipart message. Each part is represented by an InputPart interface, and each part has a set of headers associated with it. You can unmarshal the part by calling one of the getBody() methods. The genericType parameter can be null, but the type parameter must be set. RESTEasy will find a MessageBodyReader based on the media type of the part as well as the type information you pass in.

2.4.9.1.1. Input with multipart/mixed

Example Unmarshalling Parts

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(MultipartInput input) {
        List<Customer> customers = new ArrayList...;
        for (InputPart part : input.getParts()) {
            Customer cust = part.getBody(Customer.class, null);
            customers.add(cust);
        }
        input.close();
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

Sometimes you may want to unmarshal a body part that is sensitive to generic type metadata. In this case you can use the org.jboss.resteasy.util.GenericType class.

Example Unmarshaling a Type Sensitive to Generic Type Metadata

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(MultipartInput input) {
        for (InputPart part : input.getParts()) {
            List<Customer> cust = part.getBody(new GenericType<List<Customer>>() {});
        }
        input.close();
    }
}

Use of GenericType is required because it is the only way to obtain generic type information at runtime.

2.4.9.1.2. Input with multipart/mixed and java.util.List

If the body parts are uniform, you do not have to manually unmarshal each and every part. You can just provide a java.util.List as your input parameter. It must have the type it is unmarshalling with the generic parameter of the List type declaration.

Example Unmarshalling a List of Customers

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/mixed")
    public void put(List<Customer> customers) {
        ...
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.1.3. Input with multipart/form-data

When writing a JAX-RS service, RESTEasy provides an interface that allows you to read in multipart/form-data MIME type. multipart/form-data is often found in web application HTML form documents and is generally used to upload files. The form-data format is the same as other multipart formats, except that each inlined piece of content has a name associated with it. The interface used for form-data input is org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput.

MultipartFormDataInput Interface

public interface MultipartFormDataInput extends MultipartInput {

    @Deprecated
    Map<String, InputPart> getFormData();
    Map<String, List<InputPart>> getFormDataMap();
    <T> T getFormDataPart(String key, Class<T> rawType, Type genericType) throws IOException;
    <T> T getFormDataPart(String key, GenericType<T> type) throws IOException;
}

It works in much the same way as MultipartInput described earlier.

2.4.9.1.4. java.util.Map with multipart/form-data

With form-data, if the body parts are uniform, you do not have to manually unmarshal each and every part. You can just provide a java.util.Map as your input parameter. It must have the type it is unmarshalling with the generic parameter of the List type declaration.

Example Unmarshalling a Map of Customer objects

@Path("/multipart")
public class MyService {

    @PUT
    @Consumes("multipart/form-data")
    public void put(Map<String, Customer> customers) {
        ...
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.2. Output with Multipart Data

RESTEasy provides a simple API to output multipart data.

package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartOutput {

    public OutputPart addPart(Object entity, MediaType mediaType)
    public OutputPart addPart(Object entity, GenericType type, MediaType mediaType)
    public OutputPart addPart(Object entity, Class type, Type genericType, MediaType mediaType)
    public List<OutputPart> getParts()
    public String getBoundary()
    public void setBoundary(String boundary)
}

public class OutputPart {

    public MultivaluedMap<String, Object> getHeaders()
    public Object getEntity()
    public Class getType()
    public Type getGenericType()
    public MediaType getMediaType()
}

To output multipart data, you need to create a MultipartOutput object and call the addPart() method. RESTEasy will automatically find a MessageBodyWriter to marshal your entity objects. Similar to MultipartInput, sometimes you may have marshalling which is sensitive to generic type metadata. In that case, use the GenericType. Usually, passing in an object and its MediaType should be enough.

Example Returning a multipart/mixed Format

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/mixed")
    public MultipartOutput get() {

        MultipartOutput output = new MultipartOutput();
        output.addPart(new Customer("bill"), MediaType.APPLICATION_XML_TYPE);
        output.addPart(new Customer("monica"), MediaType.APPLICATION_XML_TYPE);
        return output;
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.2.1. Multipart Output with java.util.List

If the body parts are uniform, you do not have to manually marshal each and every part or even use a MultipartOutput object. You can provide a java.util.List which must have the generic type it is marshalling with the generic parameter of the List type declaration. You must also annotate the method with the @PartType annotation to specify the media type of each part.

Example Returning a List of Customer Objects

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/mixed")
    @PartType("application/xml")
    public List<Customer> get(){
        ...
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.2.2. Output with multipart/form-data

RESTEasy provides a simple API to output multipart/form-data.

package org.jboss.resteasy.plugins.providers.multipart;

public class MultipartFormDataOutput extends MultipartOutput {

    public OutputPart addFormData(String key, Object entity, MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, GenericType type, MediaType mediaType)
    public OutputPart addFormData(String key, Object entity, Class type, Type genericType, MediaType mediaType)
    public Map<String, OutputPart> getFormData()
}

To output multipart/form-data, you must create a MultipartFormDataOutput object and call the addFormData() method. RESTEasy will automatically find a MessageBodyWriter to marshal your entity objects. Similar to MultipartInput, sometimes you may have marshalling which is sensitive to generic type metadata. In that case, use the GenericType. Usually, passing in an object and its MediaType should be enough.

Example Returning multipart/form-data Format

@Path("/form")
public class MyService {

    @GET
    @Produces("multipart/form-data")
    public MultipartFormDataOutput get() {

        MultipartFormDataOutput output = new MultipartFormDataOutput();
        output.addPart("bill", new Customer("bill"), MediaType.APPLICATION_XML_TYPE);
        output.addPart("monica", new Customer("monica"), MediaType.APPLICATION_XML_TYPE);
        return output;
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.2.3. Multipart FormData Output with java.util.Map

If the body parts are uniform, you do not have to manually marshal every part or use a MultipartFormDataOutput object. You can just provide a java.util.Map which must have the generic type it is marshalling with the generic parameter of the Map type declaration. You must also annotate the method with the @PartType annotation to specify the media type of each part.

Example Returning a Map of Customer Objects

@Path("/multipart")
public class MyService {

    @GET
    @Produces("multipart/form-data")
    @PartType("application/xml")
    public Map<String, Customer> get() {
        ...
    }
}

Note

The above example assumes the Customer class is annotated with JAXB.

2.4.9.3. Mapping Multipart Forms to POJOs

If you have an exact knowledge of your multipart/form-data packets, you can map them to and from a POJO class. This is accomplished using the org.jboss.resteasy.annotations.providers.multipart.MultipartForm annotation (@MultipartForm) and the JAX-RS @FormParam annotation. To do so, you need to define a POJO with at least a default constructor and annotate its fields and/or properties with @FormParams. These @FormParams must also be annotated with org.jboss.resteasy.annotations.providers.multipart.PartType (@PartType) if you are creating output.

Example POJO

public class CustomerProblemForm {

    @FormParam("customer")
    @PartType("application/xml")
    private Customer customer;

    @FormParam("problem")
    @PartType("text/plain")
    private String problem;

    public Customer getCustomer() { return customer; }
    public void setCustomer(Customer cust) { this.customer = cust; }
    public String getProblem() { return problem; }
    public void setProblem(String problem) { this.problem = problem; }
}

After defining your POJO class you can then use it to represent multipart/form-data.

Example Submit CustomerProblemForm

@Path("portal")
public interface CustomerPortal {

    @Path("issues/{id}")
    @Consumes("multipart/form-data")
    @PUT
    public void putProblem(@MultipartForm CustomerProblemForm,
                           @PathParam("id") int id);
}

// Somewhere using it:
{
    CustomerPortal portal = ProxyFactory.create(CustomerPortal.class, "http://example.com");
    CustomerProblemForm form = new CustomerProblemForm();
    form.setCustomer(...);
    form.setProblem(...);

    portal.putProblem(form, 333);
}

The @MultipartForm annotation was used to tell RESTEasy that the object has @FormParam and that it should be marshaled from that. You can also use the same object to receive multipart data.

Example Recieve CustomerProblemForm

@Path("portal")
public class CustomerPortalServer {

    @Path("issues/{id})
    @Consumes("multipart/form-data")
    @PUT
    public void putIssue(@MultipartForm CustomerProblemForm,
                         @PathParam("id") int id) {
       ... write to database...
    }
}

2.4.9.4. XML-binary Optimized Packaging (XOP)

If you have a JAXB annotated POJO that also holds some binary content, you may choose to send it in such a way where the binary does not need to be encoded in any way (neither base64 neither hex). This is accomplished using XOP and results in faster transport while still using the convenient POJO.

RESTEasy allows for XOP messages packaged as multipart/related.

To configure XOP, you first need a JAXB annotated POJO.

Example JAXB POJO

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Xop {

    private Customer bill;
    private Customer monica;

    @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM)
    private byte[] myBinary;

    @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM)
    private DataHandler myDataHandler;

    // methods, other fields ...
}

Note

@XmlMimeType tells JAXB the mime type of the binary content. This is not required to do XOP packaging but it is recommended to be set if you know the exact type.

In the above POJO myBinary and myDataHandler will be processed as binary attachments while the whole XOP object will be sent as XML. In place of the binaries, only their references will be generated. javax.activation.DataHandler is the most general supported type. If you need an java.io.InputStream or a javax.activation.DataSource, you need to go with the DataHandler. java.awt.Image and javax.xml.transform.SourceSome are available as well.

Example Client Sending Binary Content with XOP

// our client interface:
@Path("mime")
public static interface MultipartClient {
    @Path("xop")
    @PUT
    @Consumes(MultipartConstants.MULTIPART_RELATED)
    public void putXop(@XopWithMultipartRelated Xop bean);
}

// Somewhere using it:
{
    MultipartClient client = ProxyFactory.create(MultipartClient.class,
        "http://www.example.org");
    Xop xop = new Xop(new Customer("bill"), new Customer("monica"),
        "Hello Xop World!".getBytes("UTF-8"),
        new DataHandler(new ByteArrayDataSource("Hello Xop World!".getBytes("UTF-8"),
        MediaType.APPLICATION_OCTET_STREAM)));
    client.putXop(xop);
}

Note

The above example assumes the Customer class is annotated with JAXB.

The @Consumes(MultipartConstants.MULTIPART_RELATED) is used to tell RESTEasy that you want to send multipart/related packages, which is the container format that will hold the XOP message. @XopWithMultipartRelated is used to tell RESTEasy that you want to make XOP messages.

Example RESTEasy Server for Receiving XOP

@Path("/mime")
public class XopService {
    @PUT
    @Path("xop")
    @Consumes(MultipartConstants.MULTIPART_RELATED)
    public void putXopWithMultipartRelated(@XopWithMultipartRelated Xop xop) {
        // do very important things here
    }
}

@Consumes(MultipartConstants.MULTIPART_RELATED) is used to tell RESTEasy that you want to read multipart/related packages. @XopWithMultipartRelated is used to tell RESTEasy that you want to read XOP messages. You can configure a RESTEasy server to produce XOP values in a similar way by adding a @Produces annotation and returning the appropriate type.

2.4.9.5. Overwriting the Default Fallback Content Type for Multipart Messages

By default, if no Content-Type header is present in a part, text/plain; charset=us-ascii is used as a fallback. This is defined by the MIME RFC. However some web clients, such as many browsers, may send Content-Type headers for the file parts, but not for all fields in a multipart/form-data request. This can cause character encoding and unmarshalling errors on the server side. The PreProcessInterceptor infrastructure of RESTEasy can be used to correct this issue. You can use it to define another, non-RFC compliant fallback value, dynamically per request.

Example Setting * / *; charset=UTF-8 as the Default Fallback

import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
@ServerInterceptor
public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor {

    public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
            throws Failure, WebApplicationException {
        request.setAttribute(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY, "*/*; charset=UTF-8");
        return null;
    }
}

2.4.9.6. Overwriting the Content Type for Multipart Messages

Using an interceptor and the InputPart.DEFAULT_CONTENT_TYPE_PROPERTY attribute allows you to set a default Content-Type. You can also override the Content-Type, if any, in any input part by calling org.jboss.resteasy.plugins.providers.multipart.InputPart.setMediaType().

Example Overriding the Content-Type

@POST
@Path("query")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public Response setMediaType(MultipartInput input) throws IOException {

    List<InputPart> parts = input.getParts();
    InputPart part = parts.get(0);
    part.setMediaType(MediaType.valueOf("application/foo+xml"));
    String s = part.getBody(String.class, null);
    ...
}

2.4.9.7. Overwriting the Default Fallback charset for Multipart Messages

In some cases, part of a multipart message may have a Content-Type header with no charset parameter. If the InputPart.DEFAULT_CONTENT_TYPE_PROPERTY property is set and the value has a charset parameter, that value will be appended to an existing Content-Type header that has no charset parameter.

You can also specify a default charset using the constant InputPart.DEFAULT_CHARSET_PROPERTY (resteasy.provider.multipart.inputpart.defaultCharset).

Example Specifying a Default charset

import org.jboss.resteasy.plugins.providers.multipart.InputPart;

@Provider
@ServerInterceptor
public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor {

    public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
            throws Failure, WebApplicationException {
        request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8");
        return null;
    }
}

Note

If both InputPart.DEFAULT_CONTENT_TYPE_PROPERTY and InputPart.DEFAULT_CHARSET_PROPERTY are set, then the value of InputPart.DEFAULT_CHARSET_PROPERTY will override any charset in the value of InputPart.DEFAULT_CONTENT_TYPE_PROPERTY.

2.4.9.8. Send Multipart Entity with RESTEasy Client

In addition to configuring multipart providers, you can also configure the RESTEasy client to send multipart data.

Using RESTEasy Client Classes

To use RESTEasy client classes in your application, you must add the Maven dependencies to your project’s POM file.

Maven Dependencies

<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>resteasy-client</artifactId>
  <version>${version.org.jboss.resteasy}</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>resteasy-multipart-provider</artifactId>
  <version>${version.org.jboss.resteasy}</version>
  <scope>provided</scope>
</dependency>

Sending Multipart Data Using the RESTEasy Client

To send multipart data, you first need to configure a RESTEasy Client and construct a org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput object to contain your multipart data. You can then use the client to send that MultipartFormDataOutput object as a javax.ws.rs.core.GenericEntity.

Example RESTEasy Client

ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://foo.com/resource");

MultipartFormDataOutput formOutputData = new MultipartFormDataOutput();
formOutputData.addFormData("part1", "this is part 1", MediaType.TEXT_PLAIN);
formOutputData.addFormData("part2", "this is part 2", MediaType.TEXT_PLAIN);

GenericEntity<MultipartFormDataOutput> data = new GenericEntity<MultipartFormDataOutput>(formOutputData) { };

Response response = target.request().put(Entity.entity(data, MediaType.MULTIPART_FORM_DATA_TYPE));

response.close();

2.4.10. RESTEasy Atom Support

The RESTEasy Atom API and Provider is a simple object model that RESTEasy defines to represent Atom. The main classes for the API are in the org.jboss.resteasy.plugins.providers.atom package. RESTEasy uses JAXB to marshal and unmarshal the API. The provider is JAXB based, and is not limited to sending atom objects using XML. All JAXB providers that RESTEasy has can be reused by the Atom API and provider, including JSON.

import org.jboss.resteasy.plugins.providers.atom.Content;
import org.jboss.resteasy.plugins.providers.atom.Entry;
import org.jboss.resteasy.plugins.providers.atom.Feed;
import org.jboss.resteasy.plugins.providers.atom.Link;
import org.jboss.resteasy.plugins.providers.atom.Person;

@Path("atom")
public class MyAtomService {

   @GET
   @Path("feed")
   @Produces("application/atom+xml")
   public Feed getFeed() throws URISyntaxException {
      Feed feed = new Feed();
      feed.setId(new URI("http://example.com/42"));
      feed.setTitle("My Feed");
      feed.setUpdated(new Date());
      Link link = new Link();
      link.setHref(new URI("http://localhost"));
      link.setRel("edit");
      feed.getLinks().add(link);
      feed.getAuthors().add(new Person("John Brown"));
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setType(MediaType.TEXT_HTML_TYPE);
      content.setText("Nothing much");
      entry.setContent(content);
      feed.getEntries().add(entry);
      return feed;
   }
}

2.4.10.1. Using JAXB with Atom Provider

The org.jboss.resteasy.plugins.providers.atom.Content class allows you to unmarshal and marshal JAXB annotated objects that are the body of the content.

Example Entry with a Customer

@XmlRootElement(namespace = "http://jboss.org/Customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
   @XmlElement
   private String name;

   public Customer() {
   }

   public Customer(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}

@Path("atom")
public static class AtomServer {
   @GET
   @Path("entry")
   @Produces("application/atom+xml")
   public Entry getEntry() {
      Entry entry = new Entry();
      entry.setTitle("Hello World");
      Content content = new Content();
      content.setJAXBObject(new Customer("bill"));
      entry.setContent(content);
      return entry;
   }
}

The Content.setJAXBObject() method lets you specify the content object you send to JAXB to marshal appropriately. If you are using a different base format other than XML, that is application/atom+json, the attached JAXB object is marshalled in the same format. If you have an atom document as input, you can also extract JAXB objects from Content using the Content.getJAXBObject(Class clazz) method.

Example Atom Document Extracting a Customer Object

@Path("atom")
public static class AtomServer {
   @PUT
   @Path("entry")
   @Produces("application/atom+xml")
   public void putCustomer(Entry entry) {
      Content content = entry.getContent();
      Customer cust = content.getJAXBObject(Customer.class);
   }
}

2.4.11. YAML Provider

Warning

The resteasy-yaml-provider module is not supported. Its use is not recommended due to a security issue in the SnakeYAML library used by RESTEasy for unmarshalling.

RESTEasy has a provider for YAML using the SnakeYAML library. To enable this, you must update the following dependencies into the project POM file of your application:

Maven Dependencies for YAML

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-yaml-provider</artifactId>
    <version>${version.org.jboss.resteasy}</version>
    <scope>provided</scope>
</dependency>

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

YAML provider recognizes three mime types:

  • text/x-yaml
  • text/yaml
  • application/x-yaml

Example Resource Producing YAML

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/yaml")
public class YamlResource {

  @GET
  @Produces("text/x-yaml")
  public MyObject getMyObject() {
    return createMyObject();
  }
...
}

2.5. Using the JSON API for JSON Processing (JSON-P)

The JSON API for JSON Processing (JSON-P) is a part of the Java EE 7 specification and is defined in JSR 353. JSON-P defines an API to process JSON. JBoss EAP has support for javax.json.JsonObject, javax.json.JsonArray, and javax.json.JsonStructure as request or response entities.

Note

The JSON API for JSON Processing (JSON-P) is different than JSON with Padding (JSONP).

Note

JSON-P will not conflict with Jackson if they are on the same classpath.

To create a JsonObject, use the JsonObjectBuilder by calling Json.createObjectBuilder() and building the JSON object:

Example Create javax.json.JsonObject

JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build();

Corresponding JSON

{
  "name":"Bill"
}

To create a JsonArray, use the JsonArrayBuilder by calling Json.createArrayBuilder() and building the JSON array:

Example Create javax.json.JsonArray

JsonArray array =
  Json.createArrayBuilder()
    .add(Json.createObjectBuilder().add("name", "Bill").build())
    .add(Json.createObjectBuilder().add("name", "Monica").build()).build();

Corresponding JSON

[
  {
  "name":"Bill"
  },
  {
  "name":"Monica"
  }
]

JsonStructure is a parent class of JsonObject and JsonArray:

Example Create javax.json.JsonStructure

JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build();

JsonArray array =
  Json.createArrayBuilder()
    .add(Json.createObjectBuilder().add("name", "Bill").build())
    .add(Json.createObjectBuilder().add("name", "Monica").build()).build();

JsonStructure sObj = (JsonStructure) obj;
JsonStructure sArray = (JsonStructure) array;

You can use JsonObject, JsonArray, and JsonStructure directly in JAX-RS resources:

Example JAX-RS Resources with JSON-P

@Path("object")
@POST
@Produces("application/json")
@Consumes("application/json")
public JsonObject object(JsonObject obj) {
  // do something
  return obj;
 }

@Path("array")
@POST
@Produces("application/json")
@Consumes("application/json")
public JsonArray array(JsonArray array) {
  // do something
  return array;
}

@Path("structure")
@POST
@Produces("application/json")
@Consumes("application/json")
public JsonStructure structure(JsonStructure structure) {
  // do something
  return structure;
}

You can also use JSON-P from a client to send JSON:

Example Client using JSON-P

WebTarget target = client.target(...);
JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build();
JsonObject newObj = target.request().post(Entity.json(obj), JsonObject.class);

2.6. RESTEasy/EJB Integration

To integrate RESTEasy with EJB, add JAX-RS annotations to the EJB classes that you want to expose as JAX-RS endpoints. You can also apply the annotations on the bean’s business interface. There are two ways to activate the beans as endpoints:

  • using the web.xml file, or
  • using javax.ws.rs.core.Application.

To make an EJB function as a JAX-RS resource, annotate a stateless session bean’s @Remote or @Local interface with JAX-RS annotations:

@Local
@Path("/Library")
public interface Library {
   @GET
   @Path("/books/{isbn}")
   public String getBook(@PathParam("isbn") String isbn);
}
@Stateless
public class LibraryBean implements Library {
...
}
Note

Note that the Library interface is referenced by fully qualified name, whereas LibraryBean is referenced only by the simple class name.

Then, manually register EJB with RESTEasy using the resteasy.jndi.resources context parameter in the RESTEasy web.xml file:

<web-app>
   <display-name>Archetype Created Web Application</display-name>
   <context-param>
      <param-name>resteasy.jndi.resources</param-name>
      <param-value>java:module/LibraryBean!org.app.Library</param-value>
   </context-param>
   <listener>
      <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
   </listener>
   <servlet>
      <servlet-name>Resteasy</servlet-name>
      <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>Resteasy</servlet-name>
      <url-pattern>/*</url-pattern>
   </servlet-mapping>
</web-app>

You can also specify multiple JNDI names of EJBs, separated by commas, for the resteasy.jndi.resources context parameter.

An alternate Java EE-standard way to activate EJBs as RESTEasy endpoints is by using javax.ws.rs.core.Application. This is achieved by including the EJB implementation class into the set returned by the application’s getClasses() method. This approach does not need anything to be specified in the web.xml file.

2.7. Spring Integration

Note

Your application must have an existing JAX-WS service and client configuration.

RESTEasy integrates with Spring 4.2.x.

Maven users must use the resteasy-spring artifact. Alternatively, the JAR is available as a module in JBoss EAP.

RESTEasy comes with its own Spring ContextLoaderListener that registers a RESTEasy specific BeanPostProcessor that processes JAX-RS annotations when a bean is created by a BeanFactory. This means that RESTEasy automatically scans for @Provider and JAX-RS resource annotations on your bean class and registers them as JAX-RS resources.

Add the following to your web.xml file to enable the RESTEasy/Spring integration functionality:

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
  </listener>
  <listener>
    <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>Resteasy</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Resteasy</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

The SpringContextLoaderListener must be declared after ResteasyBootstrap as it uses ServletContext attributes initialized by it.

2.8. CDI Integration

Integration between RESTEasy and CDI is provided by the resteasy-cdi module.

Both the JAX-RS and CDI specifications introduce their own component models. Every class placed in a CDI archive, which fulfills a set of basic constraints, is implicitly a CDI bean. Explicit declaration of your Java class with @Path or @Provider is required for it to become a JAX-RS component. Without the integration code, annotating a class suitable for being a CDI bean with JAX-RS annotations gives a faulty result: the JAX-RS component not managed by the CDI. The resteasy-cdi module is a bridge that allows RESTEasy to work with class instances obtained from the CDI container.

During a web service invocation, the resteasy-cdi module asks the CDI container for the managed instance of a JAX-RS component. Then, this instance is passed to RESTEasy. If a managed instance is not available for some reason, such as the class being placed in a JAR file that is not a bean deployment archive, RESTEasy falls back to instantiating the class itself.

As a result, CDI services like injection, lifecycle management, events, decoration and interceptor bindings can be used in JAX-RS components.

2.8.1. Default Scope

A CDI bean that does not explicitly define a scope is @Dependent scoped by default. This pseudo-scope means that the bean adapts to the lifecycle of the bean that it is injected into. Normal scopes including request, session, application are more suitable for JAX-RS components as they designate the component’s lifecycle boundaries explicitly. Therefore, the resteasy-cdi module alters the default scoping in the following way:

  • If a JAX-RS root resource does not define a scope explicitly, it is bound to the request scope.
  • If a JAX-RS provider or javax.ws.rs.Application subclass does not define a scope explicitly, it is bound to the application scope.
Warning

Since the scope of all beans that do not declare a scope is modified by the resteasy-cdi module, this affects session beans as well. As a result, a conflict occurs if the scope of a stateless session bean or singleton is changed automatically as the specification prohibits these components to be @RequestScoped. Therefore, you need to explicitly define a scope when using stateless session beans or singletons. This requirement is likely to be removed in future releases.

The resteasy-cdi module is bundled with JBoss EAP. Therefore, there is no need to download the module separately or add any additional configuration. See the kitchensink quickstart that ships with JBoss EAP for a working example of using CDI beans with a JAX-RS resource.

2.9. RESTEasy Filters and Interceptors

JAX-RS 2.0 has two different concepts for interceptions: Filters and Interceptors. Filters are mainly used to modify or process incoming and outgoing request headers or response headers. They execute before and after request and response processing.

2.9.1. Server-side Filters

On the server side, you have two different types of filters: ContainerRequestFilters and ContainerResponseFilters. ContainerRequestFilters run before your JAX-RS resource method is invoked. ContainerResponseFilters run after your JAX-RS resource method is invoked.

In addition, there are two types of ContainerRequestFilters: pre-matching and post-matching. Pre-matching ContainerRequestFilters are designated with the @PreMatching annotation and will execute before the JAX-RS resource method is matched with the incoming HTTP request. Post-matching ContainerRequestFilters are designated with the @PostMatching annotation and will execute after the JAX-RS resource method is matched with the incoming HTTP request

Pre-matching filters often are used to modify request attributes to change how it matches to a specific resource method, for example to strip .xml and add an Accept header. ContainerRequestFilters can abort the request by calling ContainerRequestContext.abortWith(Response). For example, a filter might want to abort if it implements a custom authentication protocol.

After the resource class method is executed, JAX-RS will run all ContainerResponseFilters. These filters allow you to modify the outgoing response before it is marshalled and sent to the client.

Example Request Filter

public class RoleBasedSecurityFilter implements ContainerRequestFilter {
  protected String[] rolesAllowed;
  protected boolean denyAll;
  protected boolean permitAll;

  public RoleBasedSecurityFilter(String[] rolesAllowed, boolean denyAll, boolean permitAll) {
    this.rolesAllowed = rolesAllowed;
    this.denyAll = denyAll;
    this.permitAll = permitAll;
  }

  @Override
  public void filter(ContainerRequestContext requestContext) throws IOException  {
    if (denyAll) {
       requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build());
       return;
    }
    if (permitAll) return;
    if (rolesAllowed != null) {
       SecurityContext context = ResteasyProviderFactory.getContextData(SecurityContext.class);
       if (context != null) {
          for (String role : rolesAllowed) {
             if (context.isUserInRole(role)) return;
          }
          requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build());
          return;
       }
    }
    return;
  }
}

Example Response Filter

public class CacheControlFilter implements ContainerResponseFilter {
   private int maxAge;

   public CacheControlFilter(int maxAge) {
      this.maxAge = maxAge;
   }

   public void filter(ContainerRequestContext req, ContainerResponseContext res)
           throws IOException {
      if (req.getMethod().equals("GET")) {
         CacheControl cc = new CacheControl();
         cc.setMaxAge(this.maxAge);
         res.getHeaders().add("Cache-Control", cc);
      }
   }
}

2.9.2. Client-side Filters

More information on client-side filters can be found in the JAX-RS 2.0 Client API section.

2.9.3. RESTEasy Interceptors

2.9.3.1. Intercept JAX-RS Invocations

RESTEasy can intercept JAX-RS invocations and route them through listener-like objects called interceptors.

While filters modify request or response headers, interceptors deal with message bodies. Interceptors are executed in the same call stack as their corresponding reader or writer. ReaderInterceptors wrap around the execution of MessageBodyReaders. WriterInterceptors wrap around the execution of MessageBodyWriters. They can be used to implement a specific content-encoding. They can be used to generate digital signatures or to post or pre-process a Java object model before or after it is marshalled.

ReaderInterceptors and WriterInterceptors can be used on either the server or client side. They are annotated with @Provider, as well as either @ServerInterceptor or @ClientInterceptor so that RESTEasy knows whether or not to add them to the interceptor list.

These interceptors wrap around the invocation of MessageBodyReader.readFrom() or MessageBodyWriter.writeTo(). They can be used to wrap the Output or Input streams.

RESTEasy GZIP support has interceptors that create and override the default Output and Input streams with a GzipOutputStream or GzipInputStream so that gzip encoding can work. They can also be used to append headers to the response, or the outgoing request on the client side.

Example Interceptor

@Provider
public class BookReaderInterceptor implements ReaderInterceptor {
    @Inject private Logger log;
    @Override
    @ReaderInterceptorBinding
    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
        log.info("*** Intercepting call in BookReaderInterceptor.aroundReadFrom()");
        VisitList.add(this);
        Object result = context.proceed();
        log.info("*** Back from intercepting call in BookReaderInterceptor.aroundReadFrom()"); return result;
    }
}

The interceptors and the MessageBodyReader or Writer are invoked in one big Java call stack. ReaderInterceptorContext.proceed() or WriterInterceptorContext.proceed() are called in order to go to the next interceptor or, if there are no more interceptors to invoke, the readFrom() or writeTo() method of the MessageBodyReader or MessageBodyWriter. This wrapping allows objects to be modified before they get to the Reader or Writer, and then cleaned up after proceed() returns.

The example below is a server-side interceptor that adds a header value to the response.

@Provider
public class BookWriterInterceptor implements WriterInterceptor {
   @Inject private Logger log;

   @Override
   @WriterInterceptorBinding
   public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
      log.info("*** Intercepting call in BookWriterInterceptor.aroundWriteTo()");
      VisitList.add(this);
      context.proceed();
      log.info("*** Back from intercepting call in BookWriterInterceptor.aroundWriteTo()");
   }
}

2.9.3.2. Registering an Interceptor

To register a RESTEasy JAX-RS interceptor in an application, list it in the web.xml file under the resteasy.providers parameter in the context-param element, or return it as a class or as an object in the Application.getClasses() or Application.getSingletons() method.

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>my.app.CustomInterceptor</paramvalue>
</context-param>
package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

  public java.util.Set<java.lang.Class<?>> getClasses() {
    Set<Class<?>> resources = new HashSet<Class<?>>();
    resources.add(MyResource.class);
    resources.add(MyProvider.class);
    return resources;
  }
}
package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

    protected Set<Object> singletons = new HashSet<Object>();

    public MyApp() {
        singletons.add(new MyResource());
        singletons.add(new MyProvider());
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

2.9.4. Per-Resource Method Filters and Interceptors

Sometimes you want a filter or interceptor to only run for a specific resource method. You can do this in two different ways:

Implement DynamicFeature Interface

The DynamicFeature interface includes a callback method, configure(ResourceInfo resourceInfo, FeatureContext context), which is invoked for each and every deployed JAX-RS method. The ResourceInfo parameter contains information about the current JAX-RS method being deployed. FeatureContext is an extension of the Configurable interface. You can use the register() method of this parameter to bind the filters and interceptors that you want to assign to this method.

Example Using DynamicFeature Interface

@Provider
public class AnimalTypeFeature implements DynamicFeature {
    @Override
    public void configure(ResourceInfo info, FeatureContext context) {
        if (info.getResourceMethod().getAnnotation(GET.class) != null)
            AnimalFilter filter = new AnimalFilter();
            context.register(filter);
        }
    }
}

In the example above, the provider that you register using AnimalTypeFeature must implement one of the interfaces. In this example, we register the provider AnimalFilter that must implement one of the following interfaces: ContainerRequestFilter, ContainerResponseFilter, ReaderInterceptor, WriterInterceptor, or Feature. In this case AnimalFilter will be applied to all resource methods annotated with GET annotation. See DynamicFeature Documentation for details.

Use the @NameBinding Annotation

@NameBinding works a lot like CDI interceptors. You annotate a custom annotation with @NameBinding and then apply that custom annotation to your filter and resource method.

Example Using @NameBinding

@NameBinding
public @interface DoIt {}

@DoIt
public class MyFilter implements ContainerRequestFilter {...}

@Path("/root")
public class MyResource {

   @GET
   @DoIt
   public String get() {...}
}

See NameBinding Documentation for details.

2.9.5. Ordering

Ordering is accomplished by using the @Priority annotation on your filter or interceptor class.

2.9.6. Exception Handling with Filters and Interceptors

Exceptions associated with filters or interceptors can occur on either the client side or the server side. On the client side, there are two types of exceptions you will have to handle: javax.ws.rs.client.ProcessingException and javax.ws.rs.client.ResponseProcessingException. A javax.ws.rs.client.ProcessingException will be thrown on the client side if there was an error before a request is sent to the server. A javax.ws.rs.client.ResponseProcessingException will be thrown on the client side if there was an error in processing the response received by the client from the server.

On the server side, exceptions thrown by filters or interceptors are handled in the same way as other exceptions thrown from JAX-RS methods, which tries to find an ExceptionMapper for the exception being thrown. More details on how exceptions are handled in JAX-RS methods can be found in the Exception Handling section.

2.10. Exception Handling

2.10.1. Creating an Exception Mapper

Exception mappers are custom components provided by applications that catch thrown exceptions and write specific HTTP responses.

When you create an exception mapper, you create a class that is annotated with the @Provider annotation and implements the ExceptionMapper interface.

An example exception mapper is provided:

@Provider
public class EJBExceptionMapper implements ExceptionMapper<javax.ejb.EJBException> {
  public Response toResponse(EJBException exception) {
    return Response.status(500).build();
  }
}

To register an exception mapper, list it in the web.xml file, under the resteasy.providers context-param, or register it programmatically through the ResteasyProviderFactory class.

2.10.2. Managing Internally Thrown Exceptions

Table 2.2. Exception List

ExceptionHTTP CodeDescription

BadRequestException

400

Bad Request. The request was not formatted correctly, or there was a problem processing the request input.

UnauthorizedException

401

Unauthorized. Security exception thrown if you are using RESTEasy’s annotation-based role-based security.

InternalServerErrorException

500

Internal Server Error.

MethodNotAllowedException

405

There is no JAX-RS method for the resource to handle the invoked HTTP operation.

NotAcceptableException

406

There is no JAX-RS method that can produce the media types listed in the Accept header.

NotFoundException

404

There is no JAX-RS method that serves the request path/resource.

ReaderException

400

All exceptions thrown from MessageBodyReaders are wrapped within this exception. If there is no ExceptionMapper for the wrapped exception, or if the exception is not a WebApplicationException, then by default, RESTEasy returns a 400 code.

WriterException

500

All exceptions thrown from MessageBodyWriters are wrapped within this exception. If there is no ExceptionMapper for the wrapped exception, or if the exception is not a WebApplicationException, then by default, RESTEasy returns a 400 code.

JAXBUnmarshalException

400

The JAXB providers (XML and Jackson) throw this exception on reads which may wrap JAXBExceptions. This class extends ReaderException.

JAXBMarshalException

500

The JAXB providers (XML and Jackson) throw this exception on writes which may wrap JAXBExceptions. This class extends WriterException.

ApplicationException

N/A

Wraps all exceptions thrown from application code, and it functions in the same way as InvocationTargetException. If there is an ExceptionMapper for wrapped exception, then that is used to handle the request.

Failure

N/A

Internal RESTEasy error. Not logged.

LoggableFailure

N/A

Internal RESTEasy error. Logged.

DefaultOptionsMethodException

N/A

If the user invokes HTTP OPTIONS and no JAX-RS method for it, RESTEasy provides a default behavior by throwing this exception.

2.11. Securing JAX-RS Web Services

RESTEasy supports the @RolesAllowed, @PermitAll, and @DenyAll annotations on JAX-RS methods. However, you must enable role-based security in order for these annotations to be recognized.

2.11.1. Enable Role-Based Security

Follow these steps to configure the web.xml file to enable role-based security.

Warning

Do not activate role-based security if the application uses EJBs. The EJB container will provide the functionality, instead of RESTEasy.

Enable Role-Based Security for a RESTEasy JAX-RS Web Service
  1. Open the web.xml file for the application in a text editor.
  2. Add the following <context-param> to the file, within the <web-app> tags.

    <context-param>
      <param-name>resteasy.role.based.security</param-name>
      <param-value>true</param-value>
    </context-param>
  3. Declare all roles used within the RESTEasy JAX-RS WAR file, using the <security-role> tags.

    <security-role>
      <role-name>ROLE_NAME</role-name>
    </security-role>
    <security-role>
      <role-name>ROLE_NAME</role-name>
    </security-role>
  4. Authorize access to all URLs handled by the JAX-RS runtime for all roles.

    <security-constraint>
      <web-resource-collection>
        <web-resource-name>Resteasy</web-resource-name>
        <url-pattern>/PATH</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>ROLE_NAME</role-name>
        <role-name>ROLE_NAME</role-name>
      </auth-constraint>
    </security-constraint>
  5. Define the appropriate login configuration for this application.

    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>jaxrs</realm-name>
    </login-config>

Role-based security has been enabled within the application, with a set of defined roles.

Example Role-Based Security Configuration

<web-app>

  <context-param>
    <param-name>resteasy.role.based.security</param-name>
    <param-value>true</param-value>
  </context-param>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Resteasy</web-resource-name>
      <url-pattern>/security</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
      <role-name>user</role-name>
    </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>jaxrs</realm-name>
  </login-config>

  <security-role>
    <role-name>admin</role-name>
  </security-role>
  <security-role>
    <role-name>user</role-name>
  </security-role>

</web-app>

2.11.2. Securing JAX-RS Web Services Using Annotations

To secure JAX-RS web services using an annotation, complete the following steps.

  1. Enable role-based security.
  2. Add security annotations to the JAX-RS web service. RESTEasy supports the following annotations:

    @RolesAllowed
    Defines which roles can access the method. All roles should be defined in the web.xml file.
    @PermitAll
    Allows all roles defined in the web.xml file to access the method.
    @DenyAll
    Denies all access to the method.

Below is an example that uses the @RolesAllowed annotation to specify that the admin role can access the web service.

@RolesAllowed("admin")
@Path("/test")
public class TestService {
  ...
}

2.11.3. Setting Programmatic Security

JAX-RS includes a programmatic API for gathering security information about a secured request. The javax.ws.rs.core.SecurityContext interface has a method for determining the identity of the user making the secured HTTP invocation. It also has a method that allows you to check whether or not the current user belongs to a certain role:

public interface SecurityContext {

   public Principal getUserPrincipal();
   public boolean isUserInRole(String role);
   public boolean isSecure();
   public String getAuthenticationScheme();
}

You can access a SecurityContext instance by injecting it into a field, setter method, or resource method parameter using the @Context annotation.

@Path("test")
public class SecurityContextResource {
    @Context
    SecurityContext securityContext;

    @GET
    @Produces("text/plain")
    public String get() {
        if (!securityContext.isUserInRole("admin")) {
            throw new WebApplicationException(Response.serverError().status(HttpResponseCodes.SC_UNAUTHORIZED)
                    .entity("User " + securityContext.getUserPrincipal().getName() + " is not authorized").build());
        }
        return "Good user " + securityContext.getUserPrincipal().getName();
    }
}

2.12. RESTEasy Asynchronous Job Service

The RESTEasy Asynchronous Job Service is designed to add asynchronous behavior to the HTTP protocol. While HTTP is a synchronous protocol it is aware of asynchronous invocations. The HTTP 1.1 response code 202, "Accepted" means that the server has received and accepted the response for processing, but the processing has not yet been completed. The Asynchronous Job Service builds around this.

2.12.1. Enabling the Asynchronous Job Service

Enable the asynchronous job service in the web.xml file:

<context-param>
    <param-name>resteasy.async.job.service.enabled</param-name>
    <param-value>true</param-value>
</context-param>

2.12.2. Configuring Asynchronous Jobs

This topic covers examples of the query parameters for asynchronous jobs with RESTEasy.

Warning

Role based security does not work with the Asynchronous Job Service, as it cannot be implemented portably. If the Asynchronous Job Service is used, application security must be done through XML declarations in the web.xml file instead.

Important

While GET, DELETE, and PUT methods can be invoked asynchronously, this breaks the HTTP 1.1 contract of these methods. While these invocations may not change the state of the resource if invoked more than once, they do change the state of the server as new Job entries with each invocation.

The asynch query parameter is used to run invocations in the background. A 202 Accepted response is returned, as well as a Location header with a URL pointing to where the response of the background method is located.

POST http://example.com/myservice?asynch=true

The example above will return a 202 Accepted response. It will also return a Location header with a URL pointing to where the response of the background method is located. An example of the location header is shown below:

HTTP/1.1 202 Accepted
Location: http://example.com/asynch/jobs/3332334

The URI will take the form of:

/asynch/jobs/{job-id}?wait={milliseconds}|nowait=true

GET, POST and DELETE operations can be performed on this URL.

  • GET returns the JAX-RS resource method invoked as a response if the job was completed. If the job has not been completed, this GET will return a 202 Accepted response code. Invoking GET does not remove the job, so it can be called multiple times.
  • POST does a read of the job response and removes the job if it has been completed.
  • DELETE is called to manually clean up the job queue.
Note

When the Job queue is full, it evicts the earliest job from memory automatically, without needing to call DELETE.

The GET and POST operations allow for the maximum wait time to be defined, using the wait and nowait query parameters. If the wait parameter is not specified, the operation will default to nowait=true, and will not wait at all if the job is not complete. The wait parameter is defined in milliseconds.

POST http://example.com/asynch/jobs/122?wait=3000

RESTEasy supports fire and forget jobs, using the oneway query parameter.

POST http://example.com/myservice?oneway=true

The example above will return a 202 Accepted response, but no job is created.

Note

The configuration parameters for the asynchronous job service can be found in the RESTEasy Asynchronous Job Service section in the appendix.

2.13. RESTEasy JavaScript API

2.13.1. About the RESTEasy JavaScript API

RESTEasy can generate a JavaScript API that uses AJAX calls to invoke JAX-RS operations. Each JAX-RS resource class will generate a JavaScript object of the same name as the declaring class or interface. The JavaScript object contains each JAX-RS method as properties.

@Path("foo")
public class Foo {

  @Path("{id}")
  @GET
  public String get(@QueryParam("order") String order, @HeaderParam("X-Foo") String header,
    @MatrixParam("colour") String colour, @CookieParam("Foo-Cookie") String cookie) {
  }

  @POST
  public void post(String text) {
  }
}

The following JavaScript code uses the JAX-RS API that was generated in the previous example.

var text = Foo.get({order: 'desc', 'X-Foo': 'hello', colour: 'blue', 'Foo-Cookie': 123987235444});
Foo.post({$entity: text});

Each JavaScript API method takes an optional object as single parameter where each property is a cookie, header, path, query or form parameter as identified by its name, or the API parameter properties. For details about the API parameter properties see RESTEasy Javascript API Parameters appendix.

2.13.1.1. Enable the RESTEasy JavaScript API Servlet

The RESTEasy JavaScript API is disabled by default. Follow these steps to enable it by updating the web.xml file.

  1. Open the web.xml file of the application in a text editor.
  2. Add the following configuration to the file, inside the web-app tags:

    <servlet>
        <servlet-name>RESTEasy JSAPI</servlet-name>
        <servlet-class>org.jboss.resteasy.jsapi.JSAPIServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>RESTEasy JSAPI</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

2.13.1.2. Build AJAX Queries

The RESTEasy JavaScript API can be used to manually construct requests. The following are some examples of this behavior.

REST Object Used to Override RESTEasy JavaScript API Client Behavior

// Change the base URL used by the API:
REST.apiURL = "http://api.service.com";

// log everything in a div element
REST.log = function(text) {
  jQuery("#log-div").append(text);
};

The REST object contains the following read-write properties:

  • apiURL: Set by default to the JAX-RS root URL. Used by every JavaScript client API functions when constructing the requests.
  • log: Set to function(string) in order to receive RESTEasy client API logs. This is useful if you want to debug your client API and place the logs where you can see them.

REST.Request Class Used to Build Custom Requests

var r = new REST.Request();
r.setURI("http://api.service.com/orders/23/json");
r.setMethod("PUT");
r.setContentType("application/json");
r.setEntity({id: "23"});
r.addMatrixParameter("JSESSIONID", "12309812378123");
r.execute(function(status, request, entity) {
  log("Response is " + status);
});