Resteasy Reference Guide En US

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 346 [warning: Documents this large are best viewed by clicking the View PDF Link!]

RESTEasy JAX-RS
RESTFul Web Services for Java
3.6.2.Final
iii
Preface ............................................................................................................................. ix
1. Overview ...................................................................................................................... 1
2. License ........................................................................................................................ 3
3. Installation/Configuration ............................................................................................ 5
3.1. RESTEasy modules in WildFly ............................................................................ 5
3.1.1. Other RESTEasy modules ........................................................................ 7
3.1.2. Upgrading RESTEasy within WildFly ......................................................... 7
3.2. Deploying a RESTEasy application to WildFly ...................................................... 7
3.3. Deploying to other servlet containers ................................................................... 8
3.3.1. Servlet 3.0 containers .............................................................................. 9
3.3.2. Older servlet containers ............................................................................ 9
3.4. Configuration switches ...................................................................................... 10
3.5. javax.ws.rs.core.Application ............................................................................... 14
3.6. RESTEasy as a ServletContextListener .............................................................. 15
3.7. RESTEasy as a Servlet Filter ............................................................................ 16
3.8. Client side ........................................................................................................ 16
4. Using @Path and @GET, @POST, etc. ...................................................................... 19
4.1. @Path and regular expression mappings ........................................................... 20
4.2. JAX-RS 2.0 Matching Algorithm ......................................................................... 21
5. @PathParam .............................................................................................................. 23
5.1. Advanced @PathParam and Regular Expressions .............................................. 24
5.2. @PathParam and PathSegment ........................................................................ 24
6. @QueryParam ............................................................................................................ 27
7. @HeaderParam .......................................................................................................... 29
8. Linking resources ...................................................................................................... 31
8.1. Link Headers .................................................................................................... 31
8.2. Atom links in the resource representations ......................................................... 31
8.2.1. Configuration .......................................................................................... 31
8.2.2. Your first links injected ........................................................................... 31
8.2.3. Customising how the Atom links are serialised ......................................... 34
8.2.4. Specifying which JAX-RS methods are tied to which resources .................. 34
8.2.5. Specifying path parameter values for URI templates ................................. 35
8.2.6. Securing entities .................................................................................... 38
8.2.7. Extending the UEL context ..................................................................... 38
8.2.8. Resource facades .................................................................................. 40
9. @MatrixParam ............................................................................................................ 43
10. @CookieParam ........................................................................................................ 45
11. @FormParam ........................................................................................................... 47
12. @Form ..................................................................................................................... 49
13. Improved @…Param annotations ............................................................................ 53
14. @DefaultValue .......................................................................................................... 55
15. @Encoded and encoding ......................................................................................... 57
16. @Context ................................................................................................................. 59
17. JAX-RS Resource Locators and Sub Resources ..................................................... 61
RESTEasy JAX-RS
iv
18. Resources metadata configuration .......................................................................... 65
19. JAX-RS Content Negotiation .................................................................................... 69
19.1. URL-based negotiation .................................................................................... 70
19.2. Query String Parameter-based negotiation ........................................................ 71
20. Content Marshalling/Providers ................................................................................. 73
20.1. Default Providers and default JAX-RS Content Marshalling ................................ 73
20.2. Content Marshalling with @Provider classes ..................................................... 74
20.3. Providers Utility Class ..................................................................................... 75
20.4. Configuring Document Marshalling ................................................................... 78
20.5. Text media types and character sets ................................................................ 79
21. JAXB providers ........................................................................................................ 81
21.1. JAXB Decorators ............................................................................................ 82
21.2. Pluggable JAXBContext's with ContextResolvers .............................................. 83
21.3. JAXB + XML provider ...................................................................................... 84
21.3.1. @XmlHeader and @Stylesheet ............................................................. 84
21.4. JAXB + JSON provider .................................................................................... 86
21.5. JAXB + FastinfoSet provider ............................................................................ 90
21.6. Arrays and Collections of JAXB Objects ........................................................... 90
21.6.1. Retrieving Collections on the client side ................................................. 93
21.6.2. JSON and JAXB Collections/arrays ....................................................... 94
21.7. Maps of JAXB Objects .................................................................................... 95
21.7.1. Retrieving Maps on the client side ......................................................... 97
21.7.2. JSON and JAXB maps ......................................................................... 98
21.7.3. Possible Problems with Jettison Provider ............................................... 98
21.8. Interfaces, Abstract Classes, and JAXB ............................................................ 99
21.9. Configurating JAXB Marshalling ....................................................................... 99
22. RESTEasy Atom Support ....................................................................................... 101
22.1. RESTEasy Atom API and Provider ................................................................. 101
22.2. Using JAXB with the Atom Provider ............................................................... 102
23. JSON Support via Jackson .................................................................................... 105
23.1. Using Jackson 1.9.x Outside of WildFly .......................................................... 105
23.2. Using Jackson 1.9.x Inside WildFly 8 ............................................................. 105
23.3. Using Jackson 2 Outside of WildFly ............................................................... 105
23.4. Using Jackson 2 Inside WildFly 9 and above .................................................. 106
23.5. Additional RESTEasy Specifics ...................................................................... 106
23.6. Possible Conflict With JAXB Provider ............................................................. 107
23.7. JSONP Support ............................................................................................ 107
23.8. Jackson JSON Decorator .............................................................................. 109
23.9. JSON Filter Support ...................................................................................... 109
24. JSON Support via Java EE 7 JSON-P API .............................................................. 113
25. Multipart Providers ................................................................................................. 115
25.1. Input with multipart/mixed .............................................................................. 115
25.2. java.util.List with multipart data ....................................................................... 117
25.3. Input with multipart/form-data ......................................................................... 117
v
25.4. java.util.Map with multipart/form-data .............................................................. 118
25.5. Input with multipart/related ............................................................................. 118
25.6. Output with multipart ..................................................................................... 119
25.7. Multipart Output with java.util.List ................................................................... 120
25.8. Output with multipart/form-data ...................................................................... 120
25.9. Multipart FormData Output with java.util.Map .................................................. 122
25.10. Output with multipart/related ......................................................................... 123
25.11. @MultipartForm and POJOs ........................................................................ 124
25.12. XML-binary Optimized Packaging (Xop) ........................................................ 128
25.13. Note about multipart parsing and working with other frameworks ..................... 130
25.14. Overwriting the default fallback content type for multipart messages ................ 130
25.15. Overwriting the content type for multipart messages ...................................... 131
25.16. Overwriting the default fallback charset for multipart messages ....................... 131
26. YAML Provider ....................................................................................................... 133
27. JAX-RS 2.1 Additions ............................................................................................. 135
27.1. CompletionStage support ............................................................................... 135
27.2. Reactive Clients API ...................................................................................... 135
27.3. Server-Sent Events (SSE) ............................................................................. 135
27.3.1. SSE Server ........................................................................................ 135
27.3.2. SSE Broadcasting .............................................................................. 137
27.3.3. SSE Client ......................................................................................... 138
27.4. Java API for JSON Binding ............................................................................ 138
28. String marshalling for String based @*Param ....................................................... 141
28.1. Simple conversion ......................................................................................... 141
28.2. ParamConverter ............................................................................................ 142
28.3. StringParameterUnmarshaller ......................................................................... 143
28.4. Collections .................................................................................................... 145
28.4.1. @QueryParam ................................................................................... 145
28.4.2. @MatrixParam ................................................................................... 146
28.4.3. @HeaderParam .................................................................................. 146
28.4.4. @CookieParam .................................................................................. 147
28.4.5. @PathParam ...................................................................................... 147
28.5. Extension to ParamConverter semantics ......................................................... 149
29. Responses using javax.ws.rs.core.Response ........................................................ 155
30. Exception Handling ................................................................................................ 157
30.1. Exception Mappers ........................................................................................ 157
30.2. RESTEasy Built-in Internally-Thrown Exceptions ............................................. 158
30.3. Overriding RESTEasy Builtin Exceptions ........................................................ 159
31. Configuring Individual JAX-RS Resource Beans ................................................... 161
32. Content encoding ................................................................................................... 163
32.1. GZIP Compression/Decompression ................................................................ 163
32.1.1. Configuring GZIP compression / decompression ................................... 163
32.2. General content encoding .............................................................................. 165
33. CORS ..................................................................................................................... 169
RESTEasy JAX-RS
vi
34. Content-Range Support ......................................................................................... 171
35. RESTEasy Caching Features ................................................................................. 173
35.1. @Cache and @NoCache Annotations ............................................................ 173
35.2. Client "Browser" Cache ................................................................................. 174
35.3. Local Server-Side Response Cache ............................................................... 175
35.4. HTTP preconditions ....................................................................................... 177
36. Filters and Interceptors .......................................................................................... 179
36.1. Server Side Filters ........................................................................................ 179
36.1.1. Asynchronous filters ............................................................................ 180
36.2. Client Side Filters .......................................................................................... 180
36.3. Reader and Writer Interceptors ...................................................................... 181
36.4. Per Resource Method Filters and Interceptors ................................................. 181
36.5. Ordering ....................................................................................................... 182
37. Asynchronous HTTP Request Processing ............................................................. 183
37.1. Using the @Suspended annotation ................................................................ 183
37.2. Using Reactive return types ........................................................................... 184
37.3. Asynchronous filters ...................................................................................... 185
38. Asynchronous Job Service .................................................................................... 187
38.1. Using Async Jobs ......................................................................................... 187
38.2. Oneway: Fire and Forget ............................................................................... 188
38.3. Setup and Configuration ................................................................................ 188
39. Reactive programming support ............................................................................. 191
39.1. CompletionStage ........................................................................................... 191
39.2. CompletionStage in JAX-RS .......................................................................... 194
39.3. Beyond CompletionStage ............................................................................... 198
39.4. Pluggable reactive types: RxJava 2 in RESTEasy ........................................... 199
39.5. Proxies ......................................................................................................... 211
39.6. Adding extensions ......................................................................................... 213
40. Embedded Containers ............................................................................................ 217
40.1. Undertow ...................................................................................................... 217
40.2. Sun JDK HTTP Server .................................................................................. 219
40.3. TJWS Embeddable Servlet Container ............................................................. 220
40.4. Netty ............................................................................................................ 221
40.5. Vert.x ............................................................................................................ 222
41. Server-side Mock Framework ................................................................................. 225
42. Securing JAX-RS and RESTEasy ........................................................................... 227
43. JSON Web Signature and Encryption (JOSE-JWT) ................................................ 229
43.1. JSON Web Signature (JWS) .......................................................................... 229
43.2. JSON Web Encryption (JWE) ........................................................................ 229
44. Doseta Digital Signature Framework ...................................................................... 233
44.1. Maven settings .............................................................................................. 235
44.2. Signing API ................................................................................................... 235
44.2.1. @Signed annotation ........................................................................... 236
44.3. Signature Verification API .............................................................................. 237
vii
44.3.1. Annotation-based verification ............................................................... 239
44.4. Managing Keys via a KeyRepository .............................................................. 240
44.4.1. Create a KeyStore .............................................................................. 240
44.4.2. Configure Restreasy to use the KeyRepository ..................................... 240
44.4.3. Using DNS to Discover Public Keys ..................................................... 242
45. Body Encryption and Signing via SMIME .............................................................. 245
45.1. Maven settings .............................................................................................. 245
45.2. Message Body Encryption ............................................................................. 245
45.3. Message Body Signing .................................................................................. 248
45.4. application/pkcs7-signature ............................................................................ 250
46. EJB Integration ...................................................................................................... 251
47. Spring Integration .................................................................................................. 253
48. CDI Integration ....................................................................................................... 261
48.1. Using CDI beans as JAX-RS components ...................................................... 261
48.2. Default scopes .............................................................................................. 261
48.3. Configuration within WildFly ........................................................................... 262
48.4. Configuration with different distributions .......................................................... 262
49. Guice 3.0 Integration .............................................................................................. 263
49.1. Request Scope ............................................................................................. 264
49.2. Binding JAX-RS utilities ................................................................................. 265
49.3. Configuring Stage ......................................................................................... 265
49.4. Custom Injector creation ................................................................................ 266
50. RESTEasy Client API ............................................................................................. 269
50.1. JAX-RS 2.0 Client API ................................................................................... 269
50.2. RESTEasy Proxy Framework ......................................................................... 270
50.2.1. Abstract Responses ............................................................................ 272
50.2.2. Response proxies ............................................................................... 272
50.2.3. Giving client proxy an ad hoc URI ....................................................... 276
50.2.4. Sharing an interface between client and server ..................................... 278
50.3. Apache HTTP Client 4.x and other backends .................................................. 278
50.3.1. HTTP redirect ..................................................................................... 280
50.3.2. Apache HTTP Client pre-4.3 APIs ....................................................... 281
50.3.3. Apache HTTP Client 4.3 APIs ............................................................. 282
50.3.4. Asynchronous HTTP Request Processing ............................................ 283
50.3.5. Jetty Client Engine ............................................................................. 284
51. MicroProfile Rest Client ......................................................................................... 285
51.1. Client proxies ................................................................................................ 285
51.2. Beyond RESTEasy ........................................................................................ 288
52. AJAX Client ............................................................................................................ 293
52.1. Generated JavaScript API .............................................................................. 293
52.1.1. JavaScript API servlet ......................................................................... 293
52.1.2. JavaScript API usage ......................................................................... 294
52.1.3. Work with @Form .............................................................................. 296
52.1.4. MIME types and unmarshalling. ........................................................... 297
RESTEasy JAX-RS
viii
52.1.5. MIME types and marshalling. .............................................................. 299
52.2. Using the JavaScript API to build AJAX queries .............................................. 300
52.2.1. The REST object ................................................................................ 300
52.2.2. The REST.Request class .................................................................... 301
52.3. Caching Features .......................................................................................... 302
53. RESTEasy WADL Support ..................................................................................... 303
53.1. RESTEasy WADL Support for Servlet Container ............................................. 303
53.2. RESTEasy WADL support for Sun JDK HTTP Server ...................................... 303
53.3. RESTEasy WADL support for Netty Container ................................................ 305
53.4. RESTEasy WADL Support for Undertow Container ......................................... 305
54. Validation ............................................................................................................... 307
54.1. Violation reporting ......................................................................................... 308
54.2. Validation Service Providers .......................................................................... 312
55. Internationalization and Localization ..................................................................... 317
55.1. Internationalization ......................................................................................... 317
55.2. Localization ................................................................................................... 319
56. Maven and RESTEasy ............................................................................................ 321
57. Deprecated Security Modules ................................................................................ 325
58. Migration to RESTEasy 3.5 series .......................................................................... 327
59. Migration to RESTEasy 3.1 series .......................................................................... 329
59.1. Upgrading with RESTEasy 3 API ................................................................... 330
59.2. Upgrading with RESTEasy 2 API ................................................................... 331
60. Migration from older versions ................................................................................ 333
60.1. Migrating from RESTEasy 2 to RESTEasy 3 ................................................... 333
60.2. Migrating from 3.0.x to 4.0.0 .......................................................................... 333
61. Books You Can Read ............................................................................................. 335
ix
Preface
Commercial development support, production support and training for RESTEasy JAX-RS is avail-
able through JBoss, a division of Red Hat Inc. (see http://www.jboss.com/).
In some of the example listings, what is meant to be displayed on one line does not fit inside the
available page width. These lines have been broken up. A '\' at the end of a line means that a
break has been introduced to fit in the page, with the following lines indented. So:
Let's pretend to have an extremely \
long line that \
does not fit
This one is short
Is really:
Let's pretend to have an extremely long line that does not fit
This one is short
x
Chapter 1.
1
Chapter 1. Overview
JAX-RS 2.0 (JSR-339) and JAX-RS 2.1 (JSR-370), are JCP specifications that provide a Java API
for RESTful Web Services over the HTTP protocol. RESTEasy is a portable implementation of
these specifications which can run in any Servlet container. Tighter integration with WildFly appli-
cation server is also available to make the user experience nicer in that environment. RESTEasy
also comes with additional features on top of plain JAX-RS functionalities.
2
Chapter 2.
3
Chapter 2. License
RESTEasy is distributed under the ASL 2.0 license. It does not distribute any thirdparty libraries
that are GPL. It does ship thirdparty libraries licensed under Apache ASL 2.0 and LGPL.
4
Chapter 3.
5
Chapter 3. Installation/
Configuration
RESTEasy is installed and configured in different ways depending on which environment you are
running in. If you are running in WildFly, RESTEasy is already bundled and integrated completely
so there is very little you have to do. If you are running in a different environment, there is some
manual installation and configuration you will have to do.
3.1. RESTEasy modules in WildFly
In WildFly, RESTEasy and the JAX-RS API are automatically loaded into your deployment's class-
path if and only if you are deploying a JAX-RS application (as determined by the presence of JAX-
RS annotations). However, only some RESTEasy features are automatically loaded. See Table
3.1. If you need any of those libraries which are not loaded automatically, you'll have to bring them
in with a jboss-deployment-structure.xml file in the WEB-INF directory of your WAR file. Here's
an example:
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.jboss.resteasy.resteasy-jackson-provider"
services="import"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
The services attribute must be set to "import" for modules that have default providers in a META-
INF/services/javax.ws.rs.ext.Providers file.
To get an idea of which RESTEasy modules are loaded by default when JAX-RS services are
deployed, please see the table below, which refers to a recent WildFly ditribution patched with the
current RESTEasy distribution. Clearly, future and unpatched WildFly distributions might differ a
bit in terms of modules enabled by default, as the container actually controls this too.
Table 3.1.
Module Name Loaded by Default Description
org.jboss.resteasy.resteasy-
atom-provider
yes RESTEasy's atom library
org.jboss.resteasy.resteasy-
cdi
yes RESTEasy CDI integration
Chapter 3. Installation/Confi...
6
Module Name Loaded by Default Description
org.jboss.resteasy.resteasy-
crypto
yes S/MIME, DKIM, and support
for other security formats.
org.jboss.resteasy.resteasy-
jackson-provider
no Integration with the JSON
parser and object mapper
Jackson (deprecated)
org.jboss.resteasy.resteasy-
jackson2-provider
yes Integration with the JSON
parser and object mapper
Jackson 2
org.jboss.resteasy.resteasy-
jaxb-provider
yes XML JAXB integration.
org.jboss.resteasy.resteasy-
jaxrs
yes Core RESTEasy libraries for
server and client. You will
need to include this in your de-
ployment if you are only using
JAX-RS client.
org.jboss.resteasy.resteasy-
jettison-provider
no Alternative JAXB-like parser
for JSON (deprecated)
org.jboss.resteasy.jose-jwt no JSON Web Token support.
org.jboss.resteasy.resteasy-
jsapi
yes RESTEasy's Javascript API
org.jboss.resteasy.resteasy-
json-p-provider
yes JSON parsing API
org.jboss.resteasy.resteasy-
json-binding-provider
yes JSON binding API
javax.json.bind-api yes JSON binding API
org.eclipse.yasson yes RI implementation of JSON
binding API
org.jboss.resteasy.resteasy-
multipart-provider
yes Support for multipart formats
org.jboss.resteasy.skeleton-
key
no OAuth2 support.
org.jboss.resteasy.resteasy-
spring
no Spring provider
org.jboss.resteasy.resteasy-
validator-provider-11
yes RESTEasy's interface to Hi-
bernate Bean Validation 1.1
org.jboss.resteasy.resteasy-
yaml-provider
yes YAML marshalling
Other RESTEasy modules
7
3.1.1. Other RESTEasy modules
Not all RESTEasy modules are bundled with WildFly. For example, resteasy-fastinfoset-provider
and resteasy-wadl are not included among the modules listed in Section 3.1, “RESTEasy modules
in WildFly”. If you want to use them in your application, you can include them in your WAR as
you would if you were deploying outside of WildFly. See Section 3.3, “Deploying to other servlet
containers” for more information.
3.1.2. Upgrading RESTEasy within WildFly
RESTEasy is bundled with WildFly, but you may want to upgrade RESTEasy in WildFly to
the latest version. The RESTEasy distribution comes with a zip file called resteasy-jboss-mod-
ules-<version>.zip. Unzip this file within the modules/system/layers/base/ directory of the WildFly
distribution. This will configure WildFly to use new versions of the modules listed in Section 3.1,
“RESTEasy modules in WildFly”.
3.2. Deploying a RESTEasy application to WildFly
RESTEasy is bundled with WildFly and completely integrated as per the requirements of Java EE.
You can use it with EJB and CDI and you can rely completely on WildFly to scan for and deploy
your JAX-RS services and providers. All you have to provide is your JAX-RS service and provider
classes packaged within a WAR either as POJOs, CDI beans, or EJBs. A simple way to configure
an application is by simply providing an empty web.xml file. You can of course deploy any custom
servlet, filter or security constraint you want to within your web.xml, but none of them are required:
<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">
</web-app>
Also, RESTEasy context-params (see Section 3.4, “Configuration switches”) are available if you
want to tweak or turn on/off any specific RESTEasy feature.
Since we're not using a <servlet-mapping> element, we must define a
javax.ws.rs.core.Application class (see Section 3.5, “javax.ws.rs.core.Application”) that is
annotated with the javax.ws.rs.ApplicationPath annotation. If you return any empty set for
classes and singletons, which is the behavior inherited from Application, your WAR will be
scanned for resource and provider classes as indicated by the presence of JAX-RS annotations.
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
Chapter 3. Installation/Confi...
8
@ApplicationPath("/root-path")
public class MyApplication extends Application
{
}
Note. Actually, if the application jar contains an Application class (or a subclass thereof) which
is annotated with an ApplicationPath annotation, a web.xml file isn't even needed. Of course,
even in this case it can be used to specify additional information such as context parameters.
If there is an Application class but it doesn't have an @ApplicationPath annotation, then a
web.xml file with at least a <servlet-mapping> element is required.
Note. As mentioned in Section 3.1.1, “Other RESTEasy modules”, not all RESTEasy modules
are bundled with WildFly. For example, resteasy-fastinfoset-provider and resteasy-wadl are not
included among the modules listed in Section 3.1, “RESTEasy modules in WildFly”. If you want
to use them in your application, you can include them in your WAR as you would if you were
deploying outside of WildFly. See Section 3.3, “Deploying to other servlet containers” for more
information.
3.3. Deploying to other servlet containers
If you are using RESTEasy outside of WildFly, in a standalone servlet container like Tomcat or
Jetty, for example, you will need to include the appropriate RESTEasy jars in your WAR file. You
will need the core classes in the resteasy-jaxrs module, and you may need additional facilities like
the resteasy-jaxb-provider module. We strongly suggest that you use Maven to build your WAR
files as RESTEasy is split into a bunch of different modules:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
You can see sample Maven projects in https://github.com/resteasy/resteasy-examples.
If you are not using Maven, you can include the necessary jars by hand. If you download
RESTEasy (from http://resteasy.jboss.org/downloads.html, for example) you will get a file like
resteasy-jaxrs-<version>-all.zip. If you unzip it you will see a lib/ directory that contains the libraries
needed by RESTEasy. Copy these, as needed, into your /WEB-INF/lib directory. Place your JAX-
Servlet 3.0 containers
9
RS annotated class resources and providers within one or more jars within /WEB-INF/lib or your
raw class files within /WEB-INF/classes.
3.3.1. Servlet 3.0 containers
RESTEasy uses the ServletContainerInitializer integration interface in Servlet 3.0 contain-
ers to initialize an application, automatically scanning for resources and providers. To enable au-
tomatic scanning, you must also include the resteasy-servlet-initializer artifact in your
WAR file as well:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-servlet-initializer</artifactId>
<version>${resteasy.version}</version>
</dependency>
3.3.2. Older servlet containers
The resteasy-servlet-initializer artifact will not work in Servlet versions older than
3.0. You'll then have to manually declare the RESTEasy servlet in your WEB-INF/web.xml
file of your WAR project, and you'll have to use an Application class (see Section 3.5,
“javax.ws.rs.core.Application”) which explicitly lists resources and providers. For example:
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.restfully.shop.services.ShoppingApplication</param-
value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Chapter 3. Installation/Confi...
10
</web-app>
The RESTEasy servlet is responsible for initializing some basic components of RESTEasy.
Note. It is likely that support for pre-3.0 Servlet specifications will be deprecated and eliminated
eventually.
3.4. Configuration switches
RESTEasy receives configuration options from <context-param> elements.
Table 3.2.
Option Name Default Value Description
resteasy.servlet.mapping.prefix no default If the url-pattern for the
RESTEasy servlet-mapping is
not /*
resteasy.scan false Automatically scan WEB-INF/
lib jars and WEB-INF/classes
directory for both @Provider
and JAX-RS resource class-
es (@Path, @GET, @POST
etc..) and register them.
This property is deprecat-
ed; please use a Servlet
3.0 container or higher and
the ResteasyServletInitializer
instead.
resteasy.scan.providers false Scan for @Provider classes
and register them. This prop-
erty is deprecated; please use
a Servlet 3.0 container or high-
er and the ResteasyServletIni-
tializer instead.
resteasy.scan.resources false Scan for JAX-RS resource
classes. This property is dep-
recated; please use a Servlet
3.0 container or higher and
the ResteasyServletInitializer
instead.
resteasy.providers no default A comma delimited list of ful-
ly qualified @Provider class
names you want to register
Configuration switches
11
Option Name Default Value Description
resteasy.use.builtin.providers true Whether or not to register de-
fault, built-in @Provider class-
es
resteasy.resources no default A comma delimited list of ful-
ly qualified JAX-RS resource
class names you want to reg-
ister
resteasy.jndi.resources no default A comma delimited list of JNDI
names which reference ob-
jects you want to register as
JAX-RS resources
javax.ws.rs.Application no default Fully qualified name of Appli-
cation class to bootstrap in a
spec portable way
resteasy.media.type.mappings no default Replaces the need for an Ac-
cept header by mapping file
name extensions (like .xml
or .txt) to a media type. Used
when the client is unable
to use an Accept header to
choose a representation (i.e.
a browser). See Chapter 19,
JAX-RS Content Negotiation
for more details.
resteasy.language.mappings no default Replaces the need for an
Accept-Language header by
mapping file name extensions
(like .en or .fr) to a language.
Used when the client is un-
able to use an Accept-Lan-
guage header to choose a lan-
guage (i.e. a browser). See
Chapter 19, JAX-RS Content
Negotiation for more details.
resteasy.media.type.param.mappingno default Names a query parameter that
can be set to an acceptable
media type, enabling content
negotiation without an Accept
header. See Chapter 19, JAX-
RS Content Negotiation for
more details.
Chapter 3. Installation/Confi...
12
Option Name Default Value Description
resteasy.role.based.security false Enables role based securi-
ty. See Chapter 42, Secur-
ing JAX-RS and RESTEasy for
more details.
resteasy.document.expand.entity.referencesfalse Expand external entities in
org.w3c.dom.Document docu-
ments and JAXB object repre-
sentations
resteasy.document.secure.processing.featuretrue Impose security con-
straints in processing
org.w3c.dom.Document docu-
ments and JAXB object repre-
sentations
resteasy.document.secure.disableDTDstrue Prohibit DTDs in
org.w3c.dom.Document docu-
ments and JAXB object repre-
sentations
resteasy.wider.request.matchingfalse Turns off the JAX-RS spec
defined class-level expression
filtering and instead tries to
match version every method's
full path.
resteasy.use.container.form.paramsfalse Obtain form para-
meters by using
HttpServletRequest.getParameterMap().
Use this switch if you are call-
ing this method within a servlet
filter or eating the input stream
within the filter.
resteasy.rfc7232preconditions false Enables RFC7232 compliant
HTTP preconditions handling.
resteasy.gzip.max.input 10000000 Imposes maximum size on de-
compressed gzipped .
resteasy.secure.random.max.use100 The number of times a Se-
cureRandom can be used be-
fore reseeding.
resteasy.buffer.exception.entity true Upon receiving an exception,
the client side buffers any re-
sponse entity before closing
the connection.
Configuration switches
13
Option Name Default Value Description
resteasy.add.charset true If a resource method returns
a text/* or application/xml* me-
dia type without an explicit
charset, RESTEasy will add
"charset=UTF-8" to the re-
turned Content-Type header.
Note that the charset defaults
to UTF-8 in this case, indepen-
dent of the setting of this para-
meter.
jaxrs.2.0.request.matching false In searching for a matching re-
source method with which to
respond to a request, consid-
er only resource methods with
the best match for the request
path. See JAX-RS 2.0 Match-
ing Algorithm for discussion.
resteasy.disable.html.sanitizer false Normally, a response with me-
dia type "text/html" and a sta-
tus of 400 will be processed
so that the characters "/", "<",
">", "&", """ (double quote), and
"'" (single quote) are escaped
to prevent an XSS attack. If
this parameter is set to "true",
escaping will not occur.
Note. The resteasy.servlet.mapping.prefix <context param> variable must be set if your servlet-
mapping for the RESTEasy servlet has a url-pattern other than /*. For example, if the url-pattern is
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/restful-services/*</url-pattern>
</servlet-mapping>
Then the value of resteasy.servlet.mapping.prefix must be:
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/restful-services</param-value>
Chapter 3. Installation/Confi...
14
</context-param>
3.5. javax.ws.rs.core.Application
The javax.ws.rs.core.Application class is a standard JAX-RS class that you may implement
to provide information on your deployment. It is simply a class the lists all JAX-RS root resources
and providers.
/**
* Defines the components of a JAX-RS application and supplies additional
* metadata. A JAX-RS application or implementation supplies a concrete
* subclass of this abstract class.
*/
public abstract class Application
{
private static final Set<Object> emptySet = Collections.emptySet();
/**
* Get a set of root resource and provider classes. The default lifecycle
* for resource class instances is per-request. The default lifecycle for
* providers is singleton.
* <p/>
* <p>Implementations should warn about and ignore classes that do not
* conform to the requirements of root resource or provider classes.
* Implementations should warn about and ignore classes for which
* {@link #getSingletons()} returns an instance. Implementations MUST
* NOT modify the returned set.</p>
*
* @return a set of root resource and provider classes. Returning null
* is equivalent to returning an empty set.
*/
public abstract Set<Class<?>> getClasses();
/**
* Get a set of root resource and provider instances. Fields and properties
* of returned instances are injected with their declared dependencies
* (see {@link Context}) by the runtime prior to use.
* <p/>
* <p>Implementations should warn about and ignore classes that do not
* conform to the requirements of root resource or provider classes.
* Implementations should flag an error if the returned set includes
* more than one instance of the same class. Implementations MUST
* NOT modify the returned set.</p>
* <p/>
* <p>The default implementation returns an empty set.</p>
*
RESTEasy as a ServletContextListener
15
* @return a set of root resource and provider instances. Returning null
* is equivalent to returning an empty set.
*/
public Set<Object> getSingletons()
{
return emptySet;
}
}
Note. If your web.xml file does not have a <servlet-mapping> element, you must use an Appli-
cation class annotated with @ApplicationPath.
3.6. RESTEasy as a ServletContextListener
This section is pretty much deprecated if you are using a Servlet 3.0 container or higher. Skip it if
you are and read the configuration section above on installing in Servlet 3.0. The initialization of
RESTEasy can be performed within a ServletContextListener instead of within the Servlet. You
may need this if you are writing custom Listeners that need to interact with RESTEasy at boot
time. An example of this is the RESTEasy Spring integration that requires a Spring ServletCon-
textListener. The org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap class is a Servlet-
ContextListener that configures an instance of an ResteasyProviderFactory and Registry. You can
obtain instances of a ResteasyProviderFactory and Registry from the ServletContext attributes
org.jboss.resteasy.spi.ResteasyProviderFactory and org.jboss.resteasy.spi.Registry. From these
instances you can programmatically interact with RESTEasy registration interfaces.
<web-app>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<!-- ** INSERT YOUR LISTENERS HERE!!!! -->
<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>
Chapter 3. Installation/Confi...
16
<url-pattern>/Resteasy/*</url-pattern>
</servlet-mapping>
</web-app>
3.7. RESTEasy as a Servlet Filter
This section is pretty much deprecated if you are using a Servlet 3.0 container or higher. Skip it
if you are and read the configuration section above on installing in Servlet 3.0. The downside of
running RESTEasy as a Servlet is that you cannot have static resources like .html and .jpeg files
in the same path as your JAX-RS services. RESTEasy allows you to run as a Filter instead. If
a JAX-RS resource is not found under the URL requested, RESTEasy will delegate back to the
base servlet container to resolve URLs.
<web-app>
<filter>
<filter-name>Resteasy</filter-name>
<filter-class>
org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.restfully.shop.services.ShoppingApplication</param-
value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Resteasy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3.8. Client side
JAX-RS 2.0 conforming implementations such as RESTEasy support a client side framework
which simplifies communicating with restful applications. In RESTEasy, the minimal set of modules
needed for the client framework consists of resteasy-jaxrs and resteasy-client. You can access
them by way of maven:
<dependency>
Client side
17
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version}</version>
</dependency>
Other modules, such as resteasy-jaxb-provider, may be brought in as needed.
18
Chapter 4.
19
Chapter 4. Using @Path and @GET,
@POST, etc.
@Path("/library")
public class Library {
@GET
@Path("/books")
public String getBooks() {...}
@GET
@Path("/book/{isbn}")
public String getBook(@PathParam("isbn") String id) {
// search my database and get a string representation and return it
}
@PUT
@Path("/book/{isbn}")
public void addBook(@PathParam("isbn") String id, @QueryParam("name") String
name) {...}
@DELETE
@Path("/book/{id}")
public void removeBook(@PathParam("id") String id {...}
}
Let's say you have the RESTEasy servlet configured and reachable at a root path of http://
myhost.com/services. The requests would be handled by the Library class:
GET http://myhost.com/services/library/books
GET http://myhost.com/services/library/book/333
PUT http://myhost.com/services/library/book/333
DELETE http://myhost.com/services/library/book/333
The @javax.ws.rs.Path annotation must exist on either the class and/or a resource method. If it
exists on both the class and method, the relative path to the resource method is a concatenation
of the class and method.
In the @javax.ws.rs package there are annotations for each HTTP method. @GET, @POST,
@PUT, @DELETE, and @HEAD. You place these on public methods that you want to map to
Chapter 4. Using @Path and @G...
20
that certain kind of HTTP method. As long as there is a @Path annotation on the class, you do
not have to have a @Path annotation on the method you are mapping. You can have more than
one HTTP method as long as they can be distinguished from other methods.
When you have a @Path annotation on a method without an HTTP method, these are called
JAXRSResourceLocators.
4.1. @Path and regular expression mappings
The @Path annotation is not limited to simple path expressions. You also have the ability to insert
regular expressions into @Path's value. For example:
@Path("/resources)
public class MyResource {
@GET
@Path("{var:.*}/stuff")
public String get() {...}
}
The following GETs will route to the getResource() method:
GET /resources/stuff
GET /resources/foo/stuff
GET /resources/on/and/on/stuff
The format of the expression is:
"{" variable-name [ ":" regular-expression ] "}"
The regular-expression part is optional. When the expression is not provided, it defaults to a
wildcard matching of one particular segment. In regular-expression terms, the expression defaults
to
"([]*)"
For example:
JAX-RS 2.0 Matching Algorithm
21
@Path("/resources/{var}/stuff")
will match these:
GET /resources/foo/stuff
GET /resources/bar/stuff
but will not match:
GET /resources/a/bunch/of/stuff
4.2. JAX-RS 2.0 Matching Algorithm
The resource method matching algorithm mandated by JAX-RS 2.1 is more inclusive that that of
JAX-RS 2.0. There are three stages in each of the matching algorithms:
1. Use the request path to choose possible resource classes.
2. Use the request path to choose possible resource methods.
3. Use the HTTP verb and media types, coming and going, to choose a final resource method.
In JAX-RS 2.1, step 2 determines the set of matching resource methods and passes it on to step
3. However, in JAX-RS 2.0, that set is sorted, based on properties of @Path values like number
of literals, and only the maximal elements are passed on to step 3. It follows that in some cases,
the newer algorithm will find some matches that the earlier algoritm misses. For example,
@Path("/")
public static class TestResource
{
@GET
@Path("complex/match")
public String get()
{
return "content";
}
@POST
@Path("complex/{param}")
Chapter 4. Using @Path and @G...
22
public String post(@PathParam("param") String param)
{
return "<" + param + "/>";
}
}
Both methods can match a request with path "complex/match", but get() comes out ahead of
post() in the JAX-RS 2.0 sort because it has more literal characters, and only get() is considered
in step 3. [For more details about the sort, see the specification for JAX-RS 2.0.] Therefore, a
request that expects a POST method will fail.
On the other hand, both methods are passed on to step 3 in the JAX-RS 2.1 algorithm, so post()
is available as a potential match.
The algorithm from JAX-RS 2.1 would seem to be preferable, but, in case the earlier be-
havior is expected for backwards compatibility, RESTEasy provides a configuration switch,
"jaxrs.2.0.request.matching", which, if set to "true", will cause the JAX-RS 2.0 matching to be
used. The default value, of course, is "false".
Chapter 5.
23
Chapter 5. @PathParam
Note
RESTEasy supports @PathParam annotations with no parameter name..
@PathParam is a parameter annotation which allows you to map variable URI path fragments
into your method call.
@Path("/library")
public class Library {
@GET
@Path("/book/{isbn}")
public String getBook(@PathParam("isbn") String id) {
// search my database and get a string representation and return it
}
}
What this allows you to do is embed variable identification within the URIs of your resources. In
the above example, an isbn URI parameter is used to pass information about the book we want to
access. The parameter type you inject into can be any primitive type, a String, or any Java object
that has a constructor that takes a String parameter, or a static valueOf method that takes a String
as a parameter. For example, lets say we wanted isbn to be a real object. We could do:
@GET
@Path("/book/{isbn}")
public String getBook(@PathParam("isbn") ISBN id) {...}
public class ISBN {
public ISBN(String str) {...}
}
Or instead of a public String constructors, have a valueOf method:
public class ISBN {
public static ISBN valueOf(String isbn) {...}
Chapter 5. @PathParam
24
}
5.1. Advanced @PathParam and Regular Expressions
There are a few more complicated uses of @PathParams not discussed in the previous section.
You are allowed to specify one or more path params embedded in one URI segment. Here are
some examples:
1. @Path("/aaa{param}bbb")
2. @Path("/{name}-{zip}")
3. @Path("/foo{name}-{zip}bar")
So, a URI of "/aaa111bbb" would match #1. "/bill-02115" would match #2. "foobill-02115bar" would
match #3.
We discussed before how you can use regular expression patterns within @Path values.
@GET
@Path("/aaa{param:b+}/{many:.*}/stuff")
public String getIt(@PathParam("param") String bs, @PathParam("many") String
many) {...}
For the following requests, lets see what the values of the "param" and "many" @PathParams
would be:
Table 5.1.
Request param many
GET /aaabb/some/stuff bb some
GET /aaab/a/lot/of/stuff b a/lot/of
5.2. @PathParam and PathSegment
The specification has a very simple abstraction for examining a fragment of the URI path being
invoked on javax.ws.rs.core.PathSegment:
@PathParam and PathSegment
25
public interface PathSegment {
/**
* Get the path segment.
* <p>
* @return the path segment
*/
String getPath();
/**
* Get a map of the matrix parameters associated with the path segment
* @return the map of matrix parameters
*/
MultivaluedMap<String, String> getMatrixParameters();
}
You can have RESTEasy inject a PathSegment instead of a value with your @PathParam.
@GET
@Path("/book/{id}")
public String getBook(@PathParam("id") PathSegment id) {...}
This is very useful if you have a bunch of @PathParams that use matrix parameters. The idea
of matrix parameters is that they are an arbitrary set of name-value pairs embedded in a uri path
segment. The PathSegment object gives you access to these parameters. See also MatrixParam.
A matrix parameter example is:
GET http://host.com/library/book;name=EJB 3.0;author=Bill Burke
The basic idea of matrix parameters is that it represents resources that are addressable by their
attributes as well as their raw id.
26
Chapter 6.
27
Chapter 6. @QueryParam
Note
RESTEasy supports @QueryParam annotations with no parameter name..
The @QueryParam annotation allows you to map a URI query string parameter or url form en-
coded parameter to your method invocation.
GET /books?num=5
@GET
public String getBooks(@QueryParam("num") int num) {
...
}
Currently since RESTEasy is built on top of a Servlet, it does not distinguish between URI query
strings or url form encoded parameters. Like PathParam, your parameter type can be an String,
primitive, or class that has a String constructor or static valueOf() method.
28
Chapter 7.
29
Chapter 7. @HeaderParam
Note
RESTEasy supports @HeaderParam annotations with no parameter name..
The @HeaderParam annotation allows you to map a request HTTP header to your method invo-
cation.
GET /books?num=5
@GET
public String getBooks(@HeaderParam("From") String from) {
...
}
Like PathParam, your parameter type can be an String, primitive, or class that has a String con-
structor or static valueOf() method. For example, MediaType has a valueOf() method and you
could do:
@PUT
public void put(@HeaderParam("Content-Type") MediaType contentType, ...)
30
Chapter 8.
31
Chapter 8. Linking resources
There are two mechanisms available in RESTEasy to link a resource to another, and to link re-
sources to operations: the Link HTTP header, and Atom links inside the resource representations.
8.1. Link Headers
RESTEasy has both client and server side support for the Link
header specification [http://tools.ietf.org/html/draft-nottingham-http-link-header-06]. See
the javadocs for org.jboss.resteasy.spi.LinkHeader, org.jboss.resteasy.spi.Link, and
org.jboss.resteasy.client.ClientResponse.
The main advantage of Link headers over Atom links in the resource is that those links are avail-
able without parsing the entity body.
8.2. Atom links in the resource representations
RESTEasy allows you to inject Atom links [http://tools.ietf.org/html/rfc4287#section-4.2.7] directly
inside the entity objects you are sending to the client, via auto-discovery.
Warning
This is only available when using the Jettison or JAXB providers (for JSON and
XML).
The main advantage over Link headers is that you can have any number of Atom links directly
over the concerned resources, for any number of resources in the response. For example, you
can have Atom links for the root response entity, and also for each of its children entities.
8.2.1. Configuration
There is no configuration required to be able to inject Atom links in your resource representation,
you just have to have this maven artifact in your path:
Table 8.1. Maven artifact for Atom link injection
Group Artifact Version
org.jboss.resteasy resteasy-links 3.6.2.Final
8.2.2. Your first links injected
You need three things in order to tell RESTEasy to inject Atom links in your entities:
Annotate the JAX-RS method with @AddLinks to indicate that you want Atom links injected in
your response entity.
Chapter 8. Linking resources
32
Add RESTServiceDiscovery fields to the resource classes where you want Atom links injected.
Annotate the JAX-RS methods you want Atom links for with @LinkResource, so that RESTEasy
knows which links to create for which resources.
The following example illustrates how you would declare everything in order to get the Atom links
injected in your book store:
@Path("/")
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
public interface BookStore {
@AddLinks
@LinkResource(value = Book.class)
@GET
@Path("books")
public Collection<Book> getBooks();
@LinkResource
@POST
@Path("books")
public void addBook(Book book);
@AddLinks
@LinkResource
@GET
@Path("book/{id}")
public Book getBook(@PathParam("id") String id);
@LinkResource
@PUT
@Path("book/{id}")
public void updateBook(@PathParam("id") String id, Book book);
@LinkResource(value = Book.class)
@DELETE
@Path("book/{id}")
public void deleteBook(@PathParam("id") String id);
}
And this is the definition of the Book resource:
@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace = "http://
www.w3.org/2005/Atom"))
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
Your first links injected
33
public class Book {
@XmlAttribute
private String author;
@XmlID
@XmlAttribute
private String title;
@XmlElementRef
private RESTServiceDiscovery rest;
}
If you do a GET /order/foo you will then get this XML representation:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book xmlns:atom="http://www.w3.org/2005/Atom" title="foo" author="bar">
<atom:link href="http://localhost:8081/books" rel="list"/>
<atom:link href="http://localhost:8081/books" rel="add"/>
<atom:link href="http://localhost:8081/book/foo" rel="self"/>
<atom:link href="http://localhost:8081/book/foo" rel="update"/>
<atom:link href="http://localhost:8081/book/foo" rel="remove"/>
</book>
And in JSON format:
{
"book":
{
"@title":"foo",
"@author":"bar",
"atom.link":
[
{"@href":"http://localhost:8081/books","@rel":"list"},
{"@href":"http://localhost:8081/books","@rel":"add"},
{"@href":"http://localhost:8081/book/foo","@rel":"self"},
{"@href":"http://localhost:8081/book/foo","@rel":"update"},
{"@href":"http://localhost:8081/book/foo","@rel":"remove"}
]
}
}
Chapter 8. Linking resources
34
8.2.3. Customising how the Atom links are serialised
Because the RESTServiceDiscovery is in fact a JAXB type which inherits from List you are free
to annotate it as you want to customise the JAXB serialisation, or just rely on the default with
@XmlElementRef.
8.2.4. Specifying which JAX-RS methods are tied to which re-
sources
This is all done by annotating the methods with the @LinkResource annotation. It supports the
following optional parameters:
Table 8.2.
@LinkResource parameters
Parameter Type Function Default
value Class Declares an Atom link
for the given type of re-
sources.
Defaults to the en-
tity body type (non-
annotated parameter),
or the method's re-
turn type. This default
does not work with
Response or Collec-
tion types, they need
to be explicitly speci-
fied.
rel String The Atom link relation list
For GET methods
returning a Col-
lection
self
For GET meth-
ods returning a
non-Collection
remove
For DELETE meth-
ods
update
For PUT methods
add
For POST methods
Specifying path parameter values for URI tem-
plates
35
You can add several @LinkResource annotations on a single method by enclosing them in a
@LinkResources annotation. This way you can add links to the same method on several resource
types. For example the /order/foo/comments operation can belongs on the Order resource with
the comments relation, and on the Comment resource with the list relation.
8.2.5. Specifying path parameter values for URI templates
When RESTEasy adds links to your resources it needs to insert the right values in the URI tem-
plate. This is done either automatically by guessing the list of values from the entity, or by speci-
fying the values in the @LinkResource pathParameters parameter.
8.2.5.1. Loading URI template values from the entity
URI template values are extracted from the entity from fields or Java Bean properties annotated
with @ResourceID, JAXB's @XmlID or JPA's @Id. If there are more than one URI template value
to find in a given entity, you can annotate your entity with @ResourceIDs to list the names of
fields or properties that make up this entity's Id. If there are other URI template values required
from a parent entity, we try to find that parent in a field or Java Bean property annotated with
@ParentResource. The list of URI template values extracted up every @ParentResource is then
reversed and used as the list of values for the URI template.
For example, let's consider the previous Book example, and a list of comments:
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Comment {
@ParentResource
private Book book;
@XmlElement
private String author;
@XmlID
@XmlAttribute
private String id;
@XmlElementRef
private RESTServiceDiscovery rest;
}
Given the previous book store service augmented with comments:
@Path("/")
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
public interface BookStore {
Chapter 8. Linking resources
36
@AddLinks
@LinkResources({
@LinkResource(value = Book.class, rel = "comments"),
@LinkResource(value = Comment.class)
})
@GET
@Path("book/{id}/comments")
public Collection<Comment> getComments(@PathParam("id") String bookId);
@AddLinks
@LinkResource
@GET
@Path("book/{id}/comment/{cid}")
public Comment getComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
@LinkResource
@POST
@Path("book/{id}/comments")
public void addComment(@PathParam("id") String bookId, Comment comment);
@LinkResource
@PUT
@Path("book/{id}/comment/{cid}")
public void updateComment(@PathParam("id") String bookId, @PathParam("cid") String commentId, Comment comment);
@LinkResource(Comment.class)
@DELETE
@Path("book/{id}/comment/{cid}")
public void deleteComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
}
Whenever we need to make links for a Book entity, we look up the ID in the Book's @XmlID property.
Whenever we make links for Comment entities, we have a list of values taken from the Comment's
@XmlID and its @ParentResource: the Book and its @XmlID.
For a Comment with id "1" on a Book with title "foo" we will therefore get a list of URI template
values of {"foo", "1"}, to be replaced in the URI template, thus obtaining either "/book/foo/
comments" or "/book/foo/comment/1".
8.2.5.2. Specifying path parameters manually
If you do not want to annotate your entities with resource ID annotations (@ResourceID, @Re-
sourceIDs, @XmlID or @Id) and @ParentResource, you can also specify the URI template values
inside the @LinkResource annotation, using Unified Expression Language expressions:
Table 8.3.
Specifying path parameter values for URI tem-
plates
37
@LinkResource URI template parameter
Parameter Type Function Default
pathParameters String[] Declares a list of UEL
expressions to obtain
the URI template val-
ues.
Defaults to using
@ResourceID, @Re-
sourceIDs, @XmlID or
@Id and @ParentRe-
source annotations to
extract the values
from the model.
The UEL expressions are evaluated in the context of the entity, which means that any unqualified
variable will be taken as a property for the entity itself, with the special variable this bound to
the entity we're generating links for.
The previous example of Comment service could be declared as such:
@Path("/")
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
public interface BookStore {
@AddLinks
@LinkResources({
@LinkResource(value = Book.class, rel = "comments", pathParameters = "${title}"),
@LinkResource(value = Comment.class, pathParameters = {"${book.title}", "${id}"})
})
@GET
@Path("book/{id}/comments")
public Collection<Comment> getComments(@PathParam("id") String bookId);
@AddLinks
@LinkResource(pathParameters = {"${book.title}", "${id}"})
@GET
@Path("book/{id}/comment/{cid}")
public Comment getComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
@LinkResource(pathParameters = {"${book.title}", "${id}"})
@POST
@Path("book/{id}/comments")
public void addComment(@PathParam("id") String bookId, Comment comment);
@LinkResource(pathParameters = {"${book.title}", "${id}"})
@PUT
@Path("book/{id}/comment/{cid}")
public void updateComment(@PathParam("id") String bookId, @PathParam("cid") String commentId, Comment comment);
@LinkResource(Comment.class, pathParameters = {"${book.title}", "${id}"})
Chapter 8. Linking resources
38
@DELETE
@Path("book/{id}/comment/{cid}")
public void deleteComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
}
8.2.6. Securing entities
You can restrict which links are injected in the resource based on security restrictions for the client,
so that if the current client doesn't have permission to delete a resource he will not be presented
with the "delete" link relation.
Security restrictions can either be specified on the @LinkResource annotation, or using RESTEasy
and EJB's security annotation @RolesAllowed on the JAX-RS method.
Table 8.4.
@LinkResource security restrictions
Parameter Type Function Default
constraint String A UEL expression
which must evaluate
to true to inject this
method's link in the re-
sponse entity.
Defaults to using
@RolesAllowed from
the JAX-RS method.
8.2.7. Extending the UEL context
We've seen that both the URI template values and the security constraints of @LinkResource use
UEL to evaluate expressions, and we provide a basic UEL context with access only to the entity
we're injecting links in, and nothing more.
If you want to add more variables or functions in this context, you can by adding a @LinkEL-
Provider annotation on the JAX-RS method, its class, or its package. This annotation's value
should point to a class that implements the ELProvider interface, which wraps the default EL-
Context in order to add any missing functions.
For example, if you want to support the Seam annotation s:hasPermission(target, permis-
sion) in your security constraints, you can add a package-info.java file like this:
@LinkELProvider(SeamELProvider.class)
package org.jboss.resteasy.links.test;
import org.jboss.resteasy.links.*;
With the following provider implementation:
Extending the UEL context
39
package org.jboss.resteasy.links.test;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.VariableMapper;
import org.jboss.seam.el.SeamFunctionMapper;
import org.jboss.resteasy.links.ELProvider;
public class SeamELProvider implements ELProvider {
public ELContext getContext(final ELContext ctx) {
return new ELContext() {
private SeamFunctionMapper functionMapper;
@Override
public ELResolver getELResolver() {
return ctx.getELResolver();
}
@Override
public FunctionMapper getFunctionMapper() {
if (functionMapper == null)
functionMapper = new SeamFunctionMapper(ctx
.getFunctionMapper());
return functionMapper;
}
@Override
public VariableMapper getVariableMapper() {
return ctx.getVariableMapper();
}
};
}
}
And then use it as such:
@Path("/")
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
public interface BookStore {
Chapter 8. Linking resources
40
@AddLinks
@LinkResources({
@LinkResource(value = Book.class, rel = "comments", constraint = "${s:hasPermission(this,
'add-comment')}"),
@LinkResource(value = Comment.class, constraint = "${s:hasPermission(this,
'insert')}")
})
@GET
@Path("book/{id}/comments")
public Collection<Comment> getComments(@PathParam("id") String bookId);
@AddLinks
@LinkResource(constraint = "${s:hasPermission(this, 'read')}")
@GET
@Path("book/{id}/comment/{cid}")
public Comment getComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
@LinkResource(constraint = "${s:hasPermission(this, 'insert')}")
@POST
@Path("book/{id}/comments")
public void addComment(@PathParam("id") String bookId, Comment comment);
@LinkResource(constraint = "${s:hasPermission(this, 'update')}")
@PUT
@Path("book/{id}/comment/{cid}")
public void updateComment(@PathParam("id") String bookId, @PathParam("cid") String commentId, Comment comment);
@LinkResource(Comment.class, constraint = "${s:hasPermission(this,
'delete')}")
@DELETE
@Path("book/{id}/comment/{cid}")
public void deleteComment(@PathParam("id") String bookId, @PathParam("cid") String commentId);
}
8.2.8. Resource facades
Sometimes it is useful to add resources which are just containers or layers on other resources. For
example if you want to represent a collection of Comment with a start index and a certain number
of entries, in order to implement paging. Such a collection is not really an entity in your model, but
it should obtain the "add" and "list" link relations for the Comment entity.
This is possible using resource facades. A resource facade is a resource which implements the
ResourceFacade<T> interface for the type T, and as such, should receive all links for that type.
Since in most cases the instance of the T type is not directly available in the resource facade,
we need another way to extract its URI template values, and this is done by calling the resource
facade's pathParameters() method to obtain a map of URI template values by name. This map
Resource facades
41
will be used to fill in the URI template values for any link generated for T, if there are enough
values in the map.
Here is an example of such a resource facade for a collection of Comments:
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class ScrollableCollection implements ResourceFacade<Comment> {
private String bookId;
@XmlAttribute
private int start;
@XmlAttribute
private int totalRecords;
@XmlElement
private List<Comment> comments = new ArrayList<Comment>();
@XmlElementRef
private RESTServiceDiscovery rest;
public Class<Comment> facadeFor() {
return Comment.class;
}
public Map<String, ? extends Object> pathParameters() {
HashMap<String, String> map = new HashMap<String, String>();
map.put("id", bookId);
return map;
}
}
This will produce such an XML collection:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<collection xmlns:atom="http://www.w3.org/2005/
Atom" totalRecords="2" start="0">
<atom.link href="http://localhost:8081/book/foo/comments" rel="add"/>
<atom.link href="http://localhost:8081/book/foo/comments" rel="list"/>
<comment xmlid="0">
<text>great book</text>
<atom.link href="http://localhost:8081/book/foo/comment/0" rel="self"/>
<atom.link href="http://localhost:8081/book/foo/comment/0" rel="update"/>
<atom.link href="http://localhost:8081/book/foo/comment/0" rel="remove"/>
<atom.link href="http://localhost:8081/book/foo/comments" rel="add"/>
<atom.link href="http://localhost:8081/book/foo/comments" rel="list"/>
</comment>
<comment xmlid="1">
<text>terrible book</text>
Chapter 8. Linking resources
42
<atom.link href="http://localhost:8081/book/foo/comment/1" rel="self"/>
<atom.link href="http://localhost:8081/book/foo/comment/1" rel="update"/>
<atom.link href="http://localhost:8081/book/foo/comment/1" rel="remove"/>
<atom.link href="http://localhost:8081/book/foo/comments" rel="add"/>
<atom.link href="http://localhost:8081/book/foo/comments" rel="list"/>
</comment>
</collection>
Chapter 9.
43
Chapter 9. @MatrixParam
Note
RESTEasy supports @MatrixParam annotations with no parameter name..
The idea of matrix parameters is that they are an arbitrary set of name-value pairs embedded in
a uri path segment. A matrix parameter example is:
GET http://host.com/library/book;name=EJB 3.0;author=Bill Burke
The basic idea of matrix parameters is that it represents resources that are addressable by their
attributes as well as their raw id. The @MatrixParam annotation allows you to inject URI matrix
parameters into your method invocation
@GET
public String getBook(@MatrixParam("name") String name, @MatrixParam("author")
String author) {...}
There is one big problem with @MatrixParam that the current version of the specification does
not resolve. What if the same MatrixParam exists twice in different path segments? In this case,
right now, its probably better to use PathParam combined with PathSegment.
44
Chapter 10.
45
Chapter 10. @CookieParam
Note
RESTEasy supports @CookieParam annotations with no parameter name..
The @CookieParam annotation allows you to inject the value of a cookie or an object represen-
tation of an HTTP request cookie into your method invocation
GET /books?num=5
@GET
public String getBooks(@CookieParam("sessionid") int id) {
...
}
@GET
public String getBooks(@CookieParam("sessionid") javax.ws.rs.core.Cookie id)
{...}
Like PathParam, your parameter type can be an String, primitive, or class that has a String con-
structor or static valueOf() method. You can also get an object representation of the cookie via
the javax.ws.rs.core.Cookie class.
46
Chapter 11.
47
Chapter 11. @FormParam
Note
RESTEasy supports @FormParam annotations with no parameter name..
When the input request body is of the type "application/x-www-form-urlencoded", a.k.a. an HTML
Form, you can inject individual form parameters from the request body into method parameter
values.
<form method="POST" action="/resources/service">
First name:
<input type="text" name="firstname">
<br>
Last name:
<input type="text" name="lastname">
</form>
If you post through that form, this is what the service might look like:
@Path("/")
public class NameRegistry {
@Path("/resources/service")
@POST
public void addName(@FormParam("firstname") String first,
@FormParam("lastname") String last) {...}
You cannot combine @FormParam with the default "application/x-www-form-urlencoded" that un-
marshalls to a MultivaluedMap<String, String>. i.e. This is illegal:
@Path("/")
public class NameRegistry {
@Path("/resources/service")
@POST
@Consumes("application/x-www-form-urlencoded")
Chapter 11. @FormParam
48
public void addName(@FormParam("firstname") String first,
MultivaluedMap<String, String> form) {...}
Chapter 12.
49
Chapter 12. @Form
This is a RESTEasy specific annotation that allows you to re-use any @*Param annotation with-
in an injected class. RESTEasy will instantiate the class and inject values into any annotated
@*Param or @Context property. This is useful if you have a lot of parameters on your method
and you want to condense them into a value object.
public class MyForm {
@FormParam("stuff")
private int stuff;
@HeaderParam("myHeader")
private String header;
@PathParam("foo")
public void setFoo(String foo) {...}
}
@POST
@Path("/myservice")
public void post(@Form MyForm form) {...}
When somebody posts to /myservice, RESTEasy will instantiate an instance of MyForm and inject
the form parameter "stuff" into the "stuff" field, the header "myheader" into the header field, and
call the setFoo method with the path param variable of "foo".
Also, @Form has some expanded @FormParam features. If you specify a prefix within the Form
param, this will prepend a prefix to any form parameter lookup. For example, let's say you have
one Address class, but want to reference invoice and shipping addresses from the same set of
form parameters:
public static class Person
{
@FormParam("name")
private String name;
@Form(prefix = "invoice")
private Address invoice;
Chapter 12. @Form
50
@Form(prefix = "shipping")
private Address shipping;
}
public static class Address
{
@FormParam("street")
private String street;
}
@Path("person")
public static class MyResource
{
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String post(@Form Person p)
{
return p.toString();
}
}
In this example, the client could send the following form parameters:
name=bill
invoice.street=xxx
shipping.street=yyy
The Person.invoice and Person.shipping fields would be populated appropriately. Also, prefix
mappings also support lists and maps:
public static class Person {
@Form(prefix="telephoneNumbers") List<TelephoneNumber> telephoneNumbers;
@Form(prefix="address") Map<String, Address> addresses;
}
public static class TelephoneNumber {
@FormParam("countryCode") private String countryCode;
@FormParam("number") private String number;
}
public static class Address {
@FormParam("street") private String street;
@FormParam("houseNumber") private String houseNumber;
51
}
@Path("person")
public static class MyResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void post (@Form Person p) {}
The following form params could be submitted and the Person.telephoneNumbers and
Person.addresses fields would be populated appropriately
request.addFormHeader("telephoneNumbers[0].countryCode", "31");
request.addFormHeader("telephoneNumbers[0].number", "0612345678");
request.addFormHeader("telephoneNumbers[1].countryCode", "91");
request.addFormHeader("telephoneNumbers[1].number", "9717738723");
request.addFormHeader("address[INVOICE].street", "Main Street");
request.addFormHeader("address[INVOICE].houseNumber", "2");
request.addFormHeader("address[SHIPPING].street", "Square One");
request.addFormHeader("address[SHIPPING].houseNumber", "13");
52
Chapter 13.
53
Chapter 13. Improved @…Param
annotations
With the addition of parameter names in the bytecode since Java 8, it is no longer necessary
to require users to specify parameter names in the following annotations: @PathParam, @Query-
Param, @FormParam, @CookieParam, @HeaderParam and @MatrixParam. In order to benefit from
this feature, you have to switch to new annotations with the same name, in a different package,
which have an optional value parameter. To use this, follow these steps:
Import the org.jboss.resteasy.annotations.jaxrs package to replace annotations from
the JAX-RS spec.
Tell your build system to record method parameter names in the bytecode.
Remove the annotation value if the name matches the name of the annotated variable.
Note that you can omit the annotation name for annotated method parameters as well as anno-
tated fields or JavaBean properties.
For Maven users, recording method parameter names in the bytecode can be enabled by setting
the maven.compiler.parameters to true:
<properties>
<maven.compiler.parameters>true</maven.compiler.parameters>
</properties>
Usage:
import org.jboss.resteasy.annotations.jaxrs.*;
@Path("/library")
public class Library {
@GET
@Path("/book/{isbn}")
public String getBook(@PathParam String isbn) {
// search my database and get a string representation and return it
}
}
Chapter 13. Improved @…Param ...
54
If your annotated variable does not have the same name as the path parameter, you can still
specify the name:
import org.jboss.resteasy.annotations.jaxrs.*;
@Path("/library")
public class Library {
@GET
@Path("/book/{isbn}")
public String getBook(@PathParam("isbn") String id) {
// search my database and get a string representation and return it
}
}
Chapter 14.
55
Chapter 14. @DefaultValue
@DefaultValue is a parameter annotation that can be combined with any of the other @*Param
annotations to define a default value when the HTTP request item does not exist.
@GET
public String getBooks(@QueryParam("num") @DefaultValue("10") int num) {...}
56
Chapter 15.
57
Chapter 15. @Encoded and
encoding
JAX-RS allows you to get encoded or decoded @*Params and specify path definitions and para-
meter names using encoded or decoded strings.
The @javax.ws.rs.Encoded annotation can be used on a class, method, or param. By default,
inject @PathParam and @QueryParams are decoded. By additionally adding the @Encoded an-
notation, the value of these params will be provided in encoded form.
@Path("/")
public class MyResource {
@Path("/{param}")
@GET
public String get(@PathParam("param") @Encoded String param) {...}
}
In the above example, the value of the @PathParam injected into the param of the get() method
will be URL encoded. Adding the @Encoded annotation as a paramater annotation triggers this
affect.
You may also use the @Encoded annotation on the entire method and any combination of
@QueryParam or @PathParam's values will be encoded.
@Path("/")
public class MyResource {
@Path("/{param}")
@GET
@Encoded
public String get(@QueryParam("foo") String foo, @PathParam("param") String
param) {}
}
In the above example, the values of the "foo" query param and "param" path param will be injected
as encoded values.
You can also set the default to be encoded for the entire class.
Chapter 15. @Encoded and encoding
58
@Path("/")
@Encoded
public class ClassEncoded {
@GET
public String get(@QueryParam("foo") String foo) {}
}
The @Path annotation has an attribute called encode. Controls whether the literal part of the
supplied value (those characters that are not part of a template variable) are URL encoded. If true,
any characters in the URI template that are not valid URI character will be automatically encoded.
If false then all characters must be valid URI characters. By default this is set to true. If you want
to encoded the characters yourself, you may.
@Path(value="hello%20world", encode=false)
Much like @Path.encode(), this controls whether the specified query param name should be en-
coded by the container before it tries to find the query param in the request.
@QueryParam(value="hello%20world", encode=false)
Chapter 16.
59
Chapter 16. @Context
The @Context annotation allows you to inject instances of
• javax.ws.rs.core.HttpHeaders
• javax.ws.rs.core.UriInfo
• javax.ws.rs.core.Request
• javax.servlet.http.HttpServletRequest
• javax.servlet.http.HttpServletResponse
• javax.servlet.ServletConfig
• javax.servlet.ServletContext
• javax.ws.rs.core.SecurityContext
objects.
60
Chapter 17.
61
Chapter 17. JAX-RS Resource
Locators and Sub Resources
Resource classes are able to partially process a request and provide another "sub" resource object
that can process the remainder of the request. For example:
@Path("/")
public class ShoppingStore {
@Path("/customers/{id}")
public Customer getCustomer(@PathParam("id") int id) {
Customer cust = ...; // Find a customer object
return cust;
}
}
public class Customer {
@GET
public String get() {...}
@Path("/address")
public String getAddress() {...}
}
Resource methods that have a @Path annotation, but no HTTP method are considered sub-re-
source locators. Their job is to provide an object that can process the request. In the above ex-
ample ShoppingStore is a root resource because its class is annotated with @Path. The getCus-
tomer() method is a sub-resource locator method.
If the client invoked:
GET /customer/123
The ShoppingStore.getCustomer() method would be invoked first. This method provides a Cus-
tomer object that can service the request. The http request will be dispatched to the Customer.get()
method. Another example is:
GET /customer/123/address
Chapter 17. JAX-RS Resource L...
62
In this request, again, first the ShoppingStore.getCustomer() method is invoked. A customer object
is returned, and the rest of the request is dispatched to the Customer.getAddress() method.
Another interesting feature of Sub-resource locators is that the locator method result is
dynamically processed at runtime to figure out how to dispatch the request. So, the
ShoppingStore.getCustomer() method does not have to declare any specific type.
@Path("/")
public class ShoppingStore {
@Path("/customers/{id}")
public java.lang.Object getCustomer(@PathParam("id") int id) {
Customer cust = ...; // Find a customer object
return cust;
}
}
public class Customer {
@GET
public String get() {...}
@Path("/address")
public String getAddress() {...}
}
In the above example, getCustomer() returns a java.lang.Object. Per request, at runtime, the JAX-
RS server will figure out how to dispatch the request based on the object returned by getCus-
tomer(). What are the uses of this? Well, maybe you have a class hierarchy for your customers.
Customer is the abstract base, CorporateCustomer and IndividualCustomer are subclasses. Your
getCustomer() method might be doing a Hibernate polymorphic query and doesn't know, or care,
what concrete class is it querying for, or what it returns.
@Path("/")
public class ShoppingStore {
@Path("/customers/{id}")
public java.lang.Object getCustomer(@PathParam("id") int id) {
Customer cust = entityManager.find(Customer.class, id);
return cust;
}
}
63
public class Customer {
@GET
public String get() {...}
@Path("/address")
public String getAddress() {...}
}
public class CorporateCustomer extends Customer {
@Path("/businessAddress")
public String getAddress() {...}
}
64
Chapter 18.
65
Chapter 18. Resources metadata
configuration
When processing JAX-RS deployments, RESTEasy relies on ResourceBuilder to create meta-
data for each JAX-RS resource. Such metadata is defined using the metadata SPI in package
org.jboss.resteasy.spi.metadata, in particular the ResourceClass interface:
package org.jboss.resteasy.spi.metadata;
public interface ResourceClass
{
String getPath();
Class<?> getClazz();
ResourceConstructor getConstructor();
FieldParameter[] getFields();
SetterParameter[] getSetters();
ResourceMethod[] getResourceMethods();
ResourceLocator[] getResourceLocators();
}
Among the other classes and interfaces defining metadata SPI, the following interfaces are worth
a mention here:
public interface ResourceConstructor
{
ResourceClass getResourceClass();
Constructor getConstructor();
ConstructorParameter[] getParams();
}
public interface ResourceMethod extends ResourceLocator
{
Set<String> getHttpMethods();
Chapter 18. Resources metadat...
66
MediaType[] getProduces();
MediaType[] getConsumes();
boolean isAsynchronous();
void markAsynchronous();
}
public interface ResourceLocator
{
ResourceClass getResourceClass();
Class<?> getReturnType();
Type getGenericReturnType();
Method getMethod();
Method getAnnotatedMethod();
MethodParameter[] getParams();
String getFullpath();
String getPath();
}
Now, the interesting point is that RESTEasy allows tuning the metadata generation by providing
implementations of the ResourceClassProcessor interface:
package org.jboss.resteasy.spi.metadata;
public interface ResourceClassProcessor
{
/**
* Allows the implementation of this method to modify the resource metadata
represented by
* the supplied {@link ResourceClass} instance. Implementation will typically
create
* wrappers which modify only certain aspects of the metadata.
*
* @param clazz The original metadata
67
* @return the (potentially modified) metadata (never null)
*/
ResourceClass process(ResourceClass clazz);
}
The processors are meant to be, and are resolved as, regular JAX-RS annotated providers. They
allow for wrapping resource metadata classes with custom versions that can be used for various
advanced scenarios like
adding additional resource method/locators to the resource
altering the http methods
altering the @Produces / @Consumes media types
• ...
68
Chapter 19.
69
Chapter 19. JAX-RS Content
Negotiation
The HTTP protocol has built in content negotiation headers that allow the client and server to
specify what content they are transferring and what content they would prefer to get. The server
declares content preferences via the @Produces and @Consumes headers.
@Consumes is an array of media types that a particular resource or resource method consumes.
For example:
@Consumes("text/*")
@Path("/library")
public class Library {
@POST
public String stringBook(String book) {...}
@Consumes("text/xml")
@POST
public String jaxbBook(Book book) {...}
}
When a client makes a request, JAX-RS first finds all methods that match the path, then, it sorts
things based on the content-type header sent by the client. So, if a client sent:
POST /library
Content-Type: text/plain
This is a nice book
The stringBook() method would be invoked because it matches to the default "text/*" media type.
Now, if the client instead sends XML:
POST /library
Content-Type: text/xml
<book name="EJB 3.0" author="Bill Burke"/>
Chapter 19. JAX-RS Content Ne...
70
The jaxbBook() method would be invoked.
The @Produces is used to map a client request and match it up to the client's Accept header.
The Accept HTTP header is sent by the client and defines the media types the client prefers to
receive from the server.
@Produces("text/*")
@Path("/library")
public class Library {
@GET
@Produces("application/json")
public String getJSON() {...}
@GET
public String get() {...}
So, if the client sends:
GET /library
Accept: application/json
The getJSON() method would be invoked.
@Consumes and @Produces can list multiple media types that they support. The client's Accept
header can also send multiple types it might like to receive. More specific media types are chosen
first. The client Accept header or @Produces @Consumes can also specify weighted preferences
that are used to match up requests with resource methods. This is best explained by RFC 2616
section 14.1 . RESTEasy supports this complex way of doing content negotiation.
A variant in JAX-RS is a combination of media type, content-language, and content encod-
ing as well as etags, last modified headers, and other preconditions. This is a more complex
form of content negotiation that is done programmatically by the application developer using the
javax.ws.rs.Variant, VarianListBuilder, and Request objects. Request is injected via @Context.
Read the javadoc for more info on these.
19.1. URL-based negotiation
Some clients, like browsers, cannot use the Accept and Accept-Language headers to nego-
tiation the representation's media type or language. RESTEasy allows you to map file name
suffixes like (.xml, .txt, .en, .fr) to media types and languages. These file name suffixes take
the place and override any Accept header sent by the client. You configure this using the
Query String Parameter-based negotiation
71
resteasy.media.type.mappings and resteasy.language.mappings context-param variables within
your web.xml.
<web-app>
<display-name>Archetype Created Web Application</display-name>
<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>
<context-param>
<param-name>resteasy.language.mappings</param-name>
<param-value>en : en-US, es : es, fr : fr</param-value>
</context-param>
...
</web-app>
Mappings are a comma delimited list of suffix/mediatype or suffix/language mappings. Each map-
ping is delimited by a ':'. So, if you invoked GET /foo/bar.xml.en, this would be equivalent to in-
voking the following request:
GET /foo/bar
Accept: application/xml
Accept-Language: en-US
The mapped file suffixes are stripped from the target URL path before the request is dispatched
to a corresponding JAX-RS resource.
19.2. Query String Parameter-based negotiation
RESTEasy can do content negotiation based in a parameter in query string. To enable this, the
web.xml can be configured like follow:
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>resteasy.media.type.param.mapping</param-name>
<param-value>someName</param-value>
</context-param>
Chapter 19. JAX-RS Content Ne...
72
...
</web-app>
The param-value is the name of the query string parameter that RESTEasy will use in the place
of the Accept header.
Invoking http://service.foo.com/resouce?someName=application/xml, will give the application/xml
media type the highest priority in the content negotiation.
In cases where the request contains both the parameter and the Accept header, the parameter
will be more relevant.
It is possible to left the param-value empty, what will cause the processor to look for a parameter
named 'accept'.
Chapter 20.
73
Chapter 20. Content Marshalling/
Providers
20.1. Default Providers and default JAX-RS Content
Marshalling
RESTEasy can automatically marshal and unmarshal a few different message bodies.
Table 20.1.
Media Types Java Type
application/*+xml, text/*+xml, application/*
+json, application/*+fastinfoset, applica-
tion/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
Note. When a java.io.File is created, as in
@Path("/test")
public class TempFileDeletionResource
{
@POST
@Path("post")
public Response post(File file) throws Exception
{
return Response.ok(file.getPath()).build();
}
}
Chapter 20. Content Marshalli...
74
a temporary file is created in the file system. On the server side, that temporary file will be deleted
at the end of the invocation. On the client side, however, it is the responsibility of the user to delete
the temporary file.
20.2. Content Marshalling with @Provider classes
The JAX-RS specification allows you to plug in your own request/response body reader and writ-
ers. 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 respectively. Here is an example:
@Provider
@Produces("text/plain")
@Consumes("text/plain")
public class DefaultTextPlain implements MessageBodyReader,
MessageBodyWriter {
public boolean isReadable(Class type, Type genericType, Annotation[]
annotations, MediaType mediaType) {
// StringTextStar should pick up strings
return !String.class.equals(type) &&
TypeConverter.isConvertable(type);
}
public Object readFrom(Class type, Type genericType,
Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
InputStream delegate = NoContent.noContentCheck(httpHeaders,
entityStream);
String value = ProviderHelper.readString(delegate, mediaType);
return TypeConverter.getType(type, value);
}
public boolean isWriteable(Class type, Type genericType, Annotation[]
annotations, MediaType mediaType) {
// StringTextStar should pick up strings
return !String.class.equals(type) && !type.isArray();
}
public long getSize(Object o, Class type, Type genericType, Annotation[]
annotations, MediaType mediaType) {
String charset = mediaType.getParameters().get("charset");
if (charset != null)
Providers Utility Class
75
try {
return o.toString().getBytes(charset).length;
} catch (UnsupportedEncodingException e) {
// Use default encoding.
}
return o.toString().getBytes(StandardCharsets.UTF_8).length;
}
public void writeTo(Object o, Class type, Type genericType, Annotation[]
annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream
entityStream) throws IOException, WebApplicationException {
String charset = mediaType.getParameters().get("charset");
if (charset == null)
entityStream.write(o.toString().getBytes(StandardCharsets.UTF_8));
else entityStream.write(o.toString().getBytes(charset));
}
}
The RESTEasy ServletContextLoader will automatically scan your WEB-INF/lib and classes di-
rectories for classes annotated with @Provider or you can manually configure them in web.xml.
See Installation/Configuration.
20.3. Providers Utility Class
javax.ws.rs.ext.Providers is a simple injectable interface that allows you to look up Message-
BodyReaders, Writers, ContextResolvers, and ExceptionMappers. It is very useful, for instance,
for implementing multipart providers. Content types that embed other random content types.
public interface Providers
{
/**
* Get a message body reader that matches a set of criteria. The set of
* readers is first filtered by comparing the supplied value of
* {@code mediaType} with the value of each reader's
* {@link javax.ws.rs.Consumes}, ensuring the supplied value of
* {@code type} is assignable to the generic type of the reader, and
* eliminating those that do not match.
* The list of matching readers is then ordered with those with the best
* matching values of {@link javax.ws.rs.Consumes} (x/y > x&#47;* > *&#47;*)
* sorted first. Finally, the
* {@link MessageBodyReader#isReadable}
* method is called on each reader in order using the supplied criteria and
* the first reader that returns {@code true} is selected and returned.
Chapter 20. Content Marshalli...
76
*
* @param type the class of object that is to be written.
* @param mediaType the media type of the data that will be read.
* @param genericType the type of object to be produced. E.g. if the
* message body is to be converted into a method
parameter, this will be
* the formal type of the method parameter as returned by
* <code>Class.getGenericParameterTypes</code>.
* @param annotations an array of the annotations on the declaration of the
* artifact that will be initialized with the produced
instance. E.g. if the
* message body is to be converted into a method
parameter, this will be
* the annotations on that parameter returned by
* <code>Class.getParameterAnnotations</code>.
* @return a MessageBodyReader that matches the supplied criteria or null
* if none is found.
*/
<T> MessageBodyReader<T> getMessageBodyReader(Class<T> type,
Type genericType, Annotation
annotations[], MediaType mediaType);
/**
* Get a message body writer that matches a set of criteria. The set of
* writers is first filtered by comparing the supplied value of
* {@code mediaType} with the value of each writer's
* {@link javax.ws.rs.Produces}, ensuring the supplied value of
* {@code type} is assignable to the generic type of the reader, and
* eliminating those that do not match.
* The list of matching writers is then ordered with those with the best
* matching values of {@link javax.ws.rs.Produces} (x/y > x&#47;* > *&#47;*)
* sorted first. Finally, the
* {@link MessageBodyWriter#isWriteable}
* method is called on each writer in order using the supplied criteria and
* the first writer that returns {@code true} is selected and returned.
*
* @param mediaType the media type of the data that will be written.
* @param type the class of object that is to be written.
* @param genericType the type of object to be written. E.g. if the
* message body is to be produced from a field, this will be
* the declared type of the field as returned by
* <code>Field.getGenericType</code>.
* @param annotations an array of the annotations on the declaration of the
* artifact that will be written. E.g. if the
* message body is to be produced from a field, this will be
* the annotations on that field returned by
* <code>Field.getDeclaredAnnotations</code>.
* @return a MessageBodyReader that matches the supplied criteria or null
* if none is found.
Providers Utility Class
77
*/
<T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type,
Type genericType, Annotation
annotations[], MediaType mediaType);
/**
* Get an exception mapping provider for a particular class of exception.
* Returns the provider whose generic type is the nearest superclass of
* {@code type}.
*
* @param type the class of exception
* @return an {@link ExceptionMapper} for the supplied type or null if none
* is found.
*/
<T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> type);
/**
* Get a context resolver for a particular type of context and media type.
* The set of resolvers is first filtered by comparing the supplied value of
* {@code mediaType} with the value of each resolver's
* {@link javax.ws.rs.Produces}, ensuring the generic type of the context
* resolver is assignable to the supplied value of {@code contextType}, and
* eliminating those that do not match. If only one resolver matches the
* criteria then it is returned. If more than one resolver matches then the
* list of matching resolvers is ordered with those with the best
* matching values of {@link javax.ws.rs.Produces} (x/y > x&#47;* > *&#47;*)
* sorted first. A proxy is returned that delegates calls to
* {@link ContextResolver#getContext(java.lang.Class)} to each matching context
* resolver in order and returns the first non-null value it obtains or null
* if all matching context resolvers return null.
*
* @param contextType the class of context desired
* @param mediaType the media type of data for which a context is required.
* @return a matching context resolver instance or null if no matching
* context providers are found.
*/
<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 {
Chapter 20. Content Marshalli...
78
private @Context Providers providers;
...
}
20.4. Configuring Document Marshalling
XML document parsers are subject to a form of attack known as the XXE (Xml eXternal Entity)
Attack (http://www.securiteam.com/securitynews/6D0100A5PU.html), in which expanding an ex-
ternal entity causes an unsafe file to be loaded. For example, the document
<?xml version="1.0"?>
<!DOCTYPE foo
[<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<search>
<user>bill</user>
<file>&xxe;<file>
</search>
could cause the passwd file to be loaded.
By default, RESTEasy's built-in unmarshaller for org.w3c.dom.Document documents will not ex-
pand external entities, replacing them by the empty string instead. It can be configured to replace
external entities by values defined in the DTD by setting the context parameter
to "true" in the web.xml file:
<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 context parameter
to "false".
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
Text media types and character sets
79
<!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;'>
then the expansion of &foo6; would result in 1,000,000 foos. By default, RESTEasy will limit the
number of expansions and the number of attributes per entity. The exact behavior depends on
the underlying parser. The limits can be turned off by setting the context parameter
to "false".
20.5. Text media types and character sets
The JAX-RS specification says
When writing responses, implementations SHOULD respect application-supplied
character
set metadata and SHOULD use UTF-8 if a character set is not specified by the
application
or if the application specifies a character set that is unsupported.
On the other hand, the HTTP specification says
When no explicit charset parameter is provided by the sender, media subtypes
of the
"text" type are defined to have a default charset value of "ISO-8859-1" when
received
via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST
be labeled
with an appropriate charset value.
It follows that, in the absence of a character set specified by a resource or resource method,
RESTEasy SHOULD use UTF-8 as the character set for text media types, and, if it does, it
MUST add an explicit charset parameter to the Content-Type response header. RESTEasy start-
ed adding the explicit charset parameter in releases 3.1.2.Final and 3.0.22.Final, and that new
behavior could cause some compatibility problems. To specify the previous behavior, in which
Chapter 20. Content Marshalli...
80
UTF-8 was used for text media types, but the explicit charset was not appended, the context pa-
rameter "resteasy.add.charset" may be set to "false". It defaults to "true".
Note. By "text" media types, we mean
a media type with type "text" and any subtype;
a media type with type ""application" and subtype beginning with "xml".
The latter set includes "application/xml-external-parsed-entity" and "application/xml-dtd".
Chapter 21.
81
Chapter 21. JAXB providers
As required by the specification, RESTEasy JAX-RS includes support for (un)marshalling JAXB
annotated classes. RESTEasy provides multiple JAXB Providers to address some subtle differ-
ences between classes generated by XJC and classes which are simply annotated with @Xml-
RootElement, or working with JAXBElement classes directly.
For the most part, developers using the JAX-RS API, the selection of which provider is invoked
will be completely transparent. For developers wishing to access the providers directly (which
most folks won't need to do), this document describes which provider is best suited for different
configurations.
A JAXB Provider is selected by RESTEasy when a parameter or return type is an object that
is annotated with JAXB annotations (such as @XmlRootEntity or @XmlType) or if the type is a
JAXBElement. Additionally, the resource class or resource method will be annotated with either
a @Consumes or @Produces annotation and contain one or more of the following values:
• text/*+xml
• application/*+xml
• application/*+fastinfoset
• application/*+json
RESTEasy will select a different provider based on the return type or parameter type used in the
resource. This section describes how the selection process works.
@XmlRootEntity When a class is annotated with a @XmlRootElement annotation, RESTEasy will
select the JAXBXmlRootElementProvider. This provider handles basic marshaling and unmar-
shalling of custom JAXB entities.
@XmlType Classes which have been generated by XJC will most likely not contain an @Xml-
RootEntity annotation. In order for these classes to marshalled, they must be wrapped within a
JAXBElement instance. This is typically accomplished by invoking a method on the class which
serves as the XmlRegistry and is named ObjectFactory.
The JAXBXmlTypeProvider provider is selected when the class is annotated with an XmlType
annotation and not an XmlRootElement annotation.
This provider simplifies this task by attempting to locate the XmlRegistry for the target class. By
default, a JAXB implementation will create a class called ObjectFactory and is located in the same
package as the target class. When this class is located, it will contain a "create" method that takes
the object instance as a parameter. For example, if the target type is called "Contact", then the
ObjectFactory class will have a method:
public JAXBElement createContact(Contact value) {..
Chapter 21. JAXB providers
82
JAXBElement<?> If your resource works with the JAXBElement class directly, the RESTEasy
runtime will select the JAXBElementProvider. This provider examines the ParameterizedType
value of the JAXBElement in order to select the appropriate JAXBContext.
21.1. JAXB Decorators
Resteasy's JAXB providers have a pluggable way to decorate Marshaller and Unmarshaller in-
stances. The way it works is that you can write an annotation that can trigger the decoration of
a Marshaller or Unmarshaller. Your decorators can do things like set Marshaller or Unmarshaller
properties, set up validation, stuff like that. Here's an example. Let's say we want to have an an-
notation that will trigger pretty-printing, nice formatting, of an XML document. If we were doing raw
JAXB, we would set a property on the Marshaller of Marshaller.JAXB_FORMATTED_OUTPUT.
Let's write a Marshaller decorator.
First we define a 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 {}
To get this to work, we must annotate our @Pretty annotation with a meta-annotation called @Dec-
orator. The target() attribute must be the JAXB Marshaller class. The processor() attribute is a
class we will write next.
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;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@DecorateTypes({"text/*+xml", "application/*+xml"})
Pluggable JAXBContext's with ContextResolvers
83
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);
}
}
The processor implementation must implement the DecoratorProcessor interface and should also
be annotated with @DecorateTypes. This annotation specifies what media types the processor
can be used with. Now that we've defined our annotation and our Processor, we can use it on our
JAX-RS resource methods or JAXB types as follows:
@GET
@Pretty
@Produces("application/xml")
public SomeJAXBObject get() {...}
If you are confused, check the RESTEasy source code for the implementation of @XmlHeader
21.2. Pluggable JAXBContext's with ContextResolvers
You should not use this feature unless you know what you're doing.
Based on the class you are marshalling/unmarshalling, RESTEasy will, by default create and
cache JAXBContext instances per class type. If you do not want RESTEasy to create JAXBCon-
texts, you can plug-in your own by implementing an instance of javax.ws.rs.ext.ContextResolver
public interface ContextResolver<T>
{
T getContext(Class<?> type);
}
@Provider
@Produces("application/xml")
public class MyJAXBContextResolver implements ContextResolver<JAXBContext>
{
JAXBContext getContext(Class<?> type)
{
if (type.equals(WhateverClassIsOverridedFor.class)) return
JAXBContext.newInstance()...;
}
Chapter 21. JAXB providers
84
}
You must provide a @Produces annotation to specify the media type the context is meant for.
You must also make sure to implement ContextResolver<JAXBContext>. This helps the runtime
match to the correct context resolver. You must also annotate the ContextResolver class with
@Provider.
There are multiple ways to make this ContextResolver available.
1. Return it as a class or instance from a javax.ws.rs.core.Application implementation
2. List it as a provider with resteasy.providers
3. Let RESTEasy automatically scan for it within your WAR file. See Configuration Guide
4. Manually add it via ResteasyProviderFactory.getInstance().registerProvider(Class) or
registerProviderInstance(Object)
21.3. JAXB + XML provider
RESTEasy is required to provide JAXB provider support for XML. It has a few extra annotations
that can help code your app.
21.3.1. @XmlHeader and @Stylesheet
Sometimes when outputting XML documents you may want to set an XML header. RESTEasy
provides the @org.jboss.resteasy.annotations.providers.jaxb.XmlHeader annotation for this. For
example:
@XmlRootElement
public static class Thing
{
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
@XmlHeader and @Stylesheet
85
@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 here forces the XML output to have an xml-stylesheet header. This header
could also have been put on the Thing class to get the same result. See the javadocs for more
details on how you can use substitution values provided by resteasy.
RESTEasy also has a convenience annotation for stylesheet headers. For example:
@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()
Chapter 21. JAXB providers
86
{
Thing thing = new Thing();
thing.setName("bill");
return thing;
}
}
21.4. JAXB + JSON provider
RESTEasy allows you to marshall JAXB annotated POJOs to and from JSON. This provider wraps
the Jettison JSON library to accomplish this. You can obtain more information about Jettison and
how it works from https://github.com/jettison-json/jettison.
To use this integration with Jettision you need to import the resteasy-jettison-provider Maven mod-
ule. Older versions of RESTEasy used to include this within the resteasy-jaxb-provider but we
decided to modularize it more.
Jettison has two mapping formats. One is BadgerFish the other is a Jettison Mapped Convention
format. The Mapped Convention is the default mapping.
For example, consider this JAXB class:
@XmlRootElement(name = "book")
public class Book
{
private String author;
private String ISBN;
private String title;
public Book()
{
}
public Book(String author, String ISBN, String title)
{
this.author = author;
this.ISBN = ISBN;
this.title = title;
}
@XmlElement
public String getAuthor()
{
return author;
}
JAXB + JSON provider
87
public void setAuthor(String author)
{
this.author = author;
}
@XmlElement
public String getISBN()
{
return ISBN;
}
public void setISBN(String ISBN)
{
this.ISBN = ISBN;
}
@XmlAttribute
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
}
This is how the JAXB Book class would be marshalled to JSON using the BadgerFish Convention:
{"book":
{
"@title":"EJB 3.0",
"author":{"$":"Bill Burke"},
"ISBN":{"$":"596529260"}
}
}
Notice that element values have a map associated with them and to get to the value of the element,
you must access the "$" variable. Here's an example of accessing the book in Javascript:
var data = eval("(" + xhr.responseText + ")");
document.getElementById("zone").innerHTML = data.book.@title;
Chapter 21. JAXB providers
88
document.getElementById("zone").innerHTML += data.book.author.$;
To use the BadgerFish Convention you must use the
@org.jboss.resteasy.annotations.providers.jaxb.json.BadgerFish annotation on the JAXB class
you are marshalling/unmarshalling, or, on the JAX-RS resource method or parameter:
@BadgerFish
@XmlRootElement(name = "book")
public class Book {...}
If you are returning a book on the JAX-RS method and you don't want to (or can't) pollute your
JAXB classes with RESTEasy annotations, add the annotation to the JAX-RS method:
@BadgerFish
@GET
public Book getBook(...) {...}
If a Book is your input then you put it on the parameter:
@POST
public void newBook(@BadgerFish Book book) {...}
The default Jettison Mapped Convention would return JSON that looked like this:
{ "book" :
{
"@title":"EJB 3.0",
"author":"Bill Burke",
"ISBN":596529260
}
}
Notice that the @XmlAttribute "title" is prefixed with the '@' character. Unlike BadgerFish, the '$'
does not represent the value of element text. This format is a bit simpler than the BadgerFish con-
vention which is why it was chose as a default. Here's an example of accessing this in Javascript:
JAXB + JSON provider
89
var data = eval("(" + xhr.responseText + ")");
document.getElementById("zone").innerHTML = data.book.@title;
document.getElementById("zone").innerHTML += data.book.author;
The Mapped Convention allows you to fine tune the JAXB mapping using the
@org.jboss.resteasy.annotations.providers.jaxb.json.Mapped annotation. You can provide an
XML Namespace to JSON namespace mapping. For example, if you defined your JAXB name-
space within your package-info.java class like this:
@javax.xml.bind.annotation.XmlSchema(namespace="http://jboss.org/books")
package org.jboss.resteasy.test.books;
You would have to define a JSON to XML namespace mapping or you would receive an exception
of something like this:
java.lang.IllegalStateException: Invalid JSON namespace: http://jboss.org/books
at
org.codehaus.jettison.mapped.MappedNamespaceConvention.getJSONNamespace(MappedNamespaceConvention.java:151)
at
org.codehaus.jettison.mapped.MappedNamespaceConvention.createKey(MappedNamespaceConvention.java:158)
at
org.codehaus.jettison.mapped.MappedXMLStreamWriter.writeStartElement(MappedXMLStreamWriter.java:241)
To fix this problem you need another annotation, @Mapped. You use the @Mapped annotation on
your JAXB classes, on your JAX-RS resource method, or on the parameter you are unmarshalling
import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
...
@GET
@Produces("application/json")
@Mapped(namespaceMap = {
@XmlNsMap(namespace = "http://jboss.org/books", jsonName = "books")
})
public Book get() {...}
Chapter 21. JAXB providers
90
Besides mapping XML to JSON namespaces, you can also force @XmlAttribute's to be marshaled
as XMLElements.
@Mapped(attributeAsElements={"title"})
@XmlRootElement(name = "book")
public class Book {...}
If you are returning a book on the JAX-RS method and you don't want to (or can't) pollute your
JAXB classes with RESTEasy annotations, add the annotation to the JAX-RS method:
@Mapped(attributeAsElements={"title"})
@GET
public Book getBook(...) {...}
If a Book is your input then you put it on the parameter:
@POST
public void newBook(@Mapped(attributeAsElements={"title"}) Book book) {...}
21.5. JAXB + FastinfoSet provider
RESTEasy supports the FastinfoSet mime type with JAXB annotated classes. Fast infoset docu-
ments are faster to serialize and parse, and smaller in size, than logically equivalent XML docu-
ments. Thus, fast infoset documents may be used whenever the size and processing time of XML
documents is an issue. It is configured the same way the XML JAXB provider is so really no other
documentation is needed here.
To use this integration with Fastinfoset you need to import the resteasy-fastinfoset-provider Maven
module. Older versions of RESTEasy used to include this within the resteasy-jaxb-provider but
we decided to modularize it more.
21.6. Arrays and Collections of JAXB Objects
RESTEasy will automatically marshal arrays, java.util.Set's, and java.util.List's of JAXB objects to
and from XML, JSON, Fastinfoset (or any other new JAXB mapper Restasy comes up with).
Arrays and Collections of JAXB Objects
91
@XmlRootElement(name = "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("/")
public class MyResource
{
@PUT
@Path("array")
@Consumes("application/xml")
public void putCustomers(Customer[] customers)
{
Assert.assertEquals("bill", customers[0].getName());
Assert.assertEquals("monica", customers[1].getName());
}
@GET
@Path("set")
@Produces("application/xml")
public Set<Customer> getCustomerSet()
{
HashSet<Customer> set = new HashSet<Customer>();
set.add(new Customer("bill"));
set.add(new Customer("monica"));
return set;
}
@PUT
Chapter 21. JAXB providers
92
@Path("list")
@Consumes("application/xml")
public void putCustomers(List<Customer> customers)
{
Assert.assertEquals("bill", customers.get(0).getName());
Assert.assertEquals("monica", customers.get(1).getName());
}
}
The above resource can publish and receive JAXB objects. It is assumed that are wrapped in a
collection element
<collection>
<customer><name>bill</name></customer>
<customer><name>monica</name></customer>
<collection>
You can change the namespace URI, namespace tag, and collection element name by using the
@org.jboss.resteasy.annotations.providers.jaxb.Wrapped annotation on a parameter or method
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Wrapped
{
String element() default "collection";
String namespace() default "http://jboss.org/resteasy";
String prefix() default "resteasy";
}
So, if we wanted to output this XML
<foo:list xmlns:foo="http://foo.org">
<customer><name>bill</name></customer>
<customer><name>monica</name></customer>
</foo:list>
We would use the @Wrapped annotation as follows:
Retrieving Collections on the client side
93
@GET
@Path("list")
@Produces("application/xml")
@Wrapped(element="list", namespace="http://foo.org", prefix="foo")
public List<Customer> getCustomerSet()
{
List<Customer> list = new ArrayList<Customer>();
list.add(new Customer("bill"));
list.add(new Customer("monica"));
return list;
}
21.6.1. Retrieving Collections on the client side
If you try to retrieve a List or Set of JAXB objects in the obvious way on the client side:
Response response = request.get();
List<Customer> list = response.readEntity(List.class);
the call to readEntity() will fail because it has no way of knowing the element type Customer.
The trick is to use an instance of javax.ws.rs.core.GenericType:
Response response = request.get();
GenericType<List<Customer>> genericType = new GenericType<List<Customer>>()
{};
List<Customer> list = response.readEntity(genericType);
For more information about GenericType, please see its javadoc.
The same trick applies to retrieving a Set:
Response response = request.get();
GenericType<Set<Customer>> genericType = new GenericType<Set<Customer>>()
{};
Set<Customer> set = response.readEntity(genericType);
On the other hand, GenericType is not necessary to retrieve an array of JAXB objects:
Chapter 21. JAXB providers
94
Response response = request.get();
Customer[] array = response.readEntity(Customer[].class);
21.6.2. JSON and JAXB Collections/arrays
RESTEasy supports using collections with JSON. It encloses lists, sets, or arrays of returned
JAXB objects within a simple JSON array. For example:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Foo
{
@XmlAttribute
private String test;
public Foo()
{
}
public Foo(String test)
{
this.test = test;
}
public String getTest()
{
return test;
}
public void setTest(String test)
{
this.test = test;
}
}
This a List or array of this Foo class would be represented in JSON like this:
[{"foo":{"@test":"bill"}},{"foo":{"@test":"monica}"}}]
It also expects this format for input
Maps of JAXB Objects
95
21.7. Maps of JAXB Objects
RESTEasy will automatically marshal maps of JAXB objects to and from XML, JSON, Fastinfoset
(or any other new JAXB mapper Restasy comes up with). Your parameter or method return type
must be a generic with a String as the key and the JAXB object's type.
@XmlRootElement(namespace = "http://foo.com")
public static class Foo
{
@XmlAttribute
private String name;
public Foo()
{
}
public Foo(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
@Path("/map")
public static class MyResource
{
@POST
@Produces("application/xml")
@Consumes("application/xml")
public Map<String, Foo> post(Map<String, Foo> map)
{
Assert.assertEquals(2, map.size());
Assert.assertNotNull(map.get("bill"));
Assert.assertNotNull(map.get("monica"));
Assert.assertEquals(map.get("bill").getName(), "bill");
Assert.assertEquals(map.get("monica").getName(), "monica");
return map;
}
}
Chapter 21. JAXB providers
96
The above resource can publish and receive JAXB objects within a map. By default, they are
wrapped in a "map" element in the default namespace. Also, each "map" element has zero or
more "entry" elements with a "key" attribute.
<map>
<entry key="bill" xmlns="http://foo.com">
<foo name="bill"/>
</entry>
<entry key="monica" xmlns="http://foo.com">
<foo name="monica"/>
</entry>
</map>
You can change the namespace URI, namespace prefix and map, entry, and key element and at-
tribute names by using the @org.jboss.resteasy.annotations.providers.jaxb.WrappedMap anno-
tation on a parameter or method
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WrappedMap
{
/**
* map element name
*/
String map() default "map";
/**
* entry element name *
*/
String entry() default "entry";
/**
* entry's key attribute name
*/
String key() default "key";
String namespace() default "";
String prefix() default "";
}
So, if we wanted to output this XML
Retrieving Maps on the client side
97
<hashmap>
<hashentry hashkey="bill" xmlns:foo="http://foo.com">
<foo:foo name="bill"/>
</hashentry>
</map>
We would use the @WrappedMap annotation as follows:
@Path("/map")
public static class MyResource
{
@GET
@Produces("application/xml")
@WrappedMap(map="hashmap", entry="hashentry", key="hashkey")
public Map<String, Foo> get()
{
...
return map;
}
}
21.7.1. Retrieving Maps on the client side
If you try to retrieve a Map of JAXB objects in the obvious way on the client side:
Response response = request.get();
Map<String, Customer> map = response.readEntity(Map.class);
the call to readEntity() will fail because it has no way of knowing the element type Customer.
The trick is to use an instance of javax.ws.rs.core.GenericType:
Response response = request.get();
GenericType<Map<String, Customer> genericType = new GenericType<Map<String,
Customer>>() {};
Map<String, Customer> map = response.readEntity(genericType);
For more information about GenericType, please see its javadoc.
Chapter 21. JAXB providers
98
21.7.2. JSON and JAXB maps
RESTEasy supports using maps with JSON. It encloses maps returned JAXB objects within a
simple JSON map. For example:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Foo
{
@XmlAttribute
private String test;
public Foo()
{
}
public Foo(String test)
{
this.test = test;
}
public String getTest()
{
return test;
}
public void setTest(String test)
{
this.test = test;
}
}
This a List or array of this Foo class would be represented in JSON like this:
{ "entry1" : {"foo":{"@test":"bill"}}, "entry2" : {"foo":{"@test":"monica}"}}}
It also expects this format for input
21.7.3. Possible Problems with Jettison Provider
If you have the resteasy-jackson-provider-xxx.jar in your classpath, the Jackson JSON provider
will be triggered. This will screw up code that is dependent on the Jettison JAXB/JSon provider.
Interfaces, Abstract Classes, and JAXB
99
If you had been using the Jettison JAXB/Json providers, you must either remove Jackson from
your WEB-INF/lib or classpath, or use the @NoJackson annotation on your JAXB classes.
21.8. Interfaces, Abstract Classes, and JAXB
Some objects models use abstract classes and interfaces heavily. Unfortunately, JAXB doesn't
work with interfaces that are root elements and RESTEasy can't unmarshal parameters that are
interfaces or raw abstract classes because it doesn't have enough information to create a JAXB-
Context. For example:
public interface IFoo {}
@XmlRootElement
public class RealFoo implements IFoo {}
@Path("/jaxb")
public class MyResource {
@PUT
@Consumes("application/xml")
public void put(IFoo foo) {...}
}
In this example, you would get an error from RESTEasy of something like "Cannot find a Message-
BodyReader for...". This is because RESTEasy does not know that implementations of IFoo are
JAXB classes and doesn't know how to create a JAXBContext for it. As a workaround, RESTEasy
allows you to use the JAXB annotation @XmlSeeAlso on the interface to correct the problem.
(NOTE, this will not work with manual, hand-coded JAXB).
@XmlSeeAlso(RealFoo.class)
public interface IFoo {}
The extra @XmlSeeAlso on IFoo allows RESTEasy to create a JAXBContext that knows how to
unmarshal RealFoo instances.
21.9. Configurating JAXB Marshalling
As a consumer of XML datasets, JAXB is subject to a form of attack known as the XXE (Xml eX-
ternal Entity) Attack (http://www.securiteam.com/securitynews/6D0100A5PU.html), in which ex-
panding an external entity causes an unsafe file to be loaded. Preventing the expansion of exter-
nal entities is discussed in Section 20.4, “Configuring Document Marshalling”. The same context
parameter,
Chapter 21. JAXB providers
100
applies to JAXB unmarshallers as well.
Section 20.4, “Configuring Document Marshalling” also discusses the prohibition of DTDs and the
imposition of limits on entity expansion and the number of attributes per element. The context
parameters
and
discussed there, and their default values, also apply to the representation of JAXB objects.
Chapter 22.
101
Chapter 22. RESTEasy Atom
Support
From W3.org (http://tools.ietf.org/html/rfc4287):
"Atom is an XML-based document format that describes lists of related information known as
"feeds". Feeds are composed of a number of items, known as "entries", each with an extensible
set of attached metadata. For example, each entry has a title. The primary use case that Atom
addresses is the syndication of Web content such as weblogs and news headlines to Web sites
as well as directly to user agents."
Atom is the next-gen RSS feed. Although it is used primarily for the syndication of blogs and news,
many are starting to use this format as the envelope for Web Services, for example, distributed
notifications, job queues, or simply a nice format for sending or receiving data in bulk from a
service.
22.1. RESTEasy Atom API and Provider
RESTEasy has defined a simple object model in Java to represent Atom and uses JAXB to mar-
shal and unmarshal it. The main classes are in the org.jboss.resteasy.plugins.providers.atom
package and are Feed, Entry, Content, and Link. If you look at the source, you'd see that these are
annotated with JAXB annotations. The distribution contains the javadocs for this project and are a
must to learn the model. Here is a simple example of sending an atom feed using the RESTEasy
API.
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());
Chapter 22. RESTEasy Atom Support
102
Link link = new Link();
link.setHref(new URI("http://localhost"));
link.setRel("edit");
feed.getLinks().add(link);
feed.getAuthors().add(new Person("Bill Burke"));
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;
}
}
Because RESTEasy's atom provider is JAXB based, you are not limited to sending atom ob-
jects using XML. You can automatically re-use all the other JAXB providers that RESTEasy has
like JSON and fastinfoset. All you have to do is have "atom+" in front of the main subtype. i.e.
@Produces("application/atom+json") or @Consumes("application/atom+fastinfoset")
22.2. Using JAXB with the Atom Provider
The org.jboss.resteasy.plugins.providers.atom.Content class allows you to unmarshal and mar-
shal JAXB annotated objects that are the body of the content. Here's an example of sending an
Entry with a Customer object attached as the body of the entry's content.
@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;
Using JAXB with the Atom Provider
103
}
}
@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 is used to tell the content object you are sending back a
Java JAXB object and want it marshalled appropriately. If you are using a different base format
other than XML, i.e. "application/atom+json", this attached JAXB object will be marshalled into
that same format.
If you have an atom document as your input, you can also extract JAXB objects from Content using
the Content.getJAXBObject(Class clazz) method. Here is an example of an input atom document
and extracting a Customer object from the content.
@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);
}
}
104
Chapter 23.
105
Chapter 23. JSON Support via
Jackson
Besides the Jettision JAXB adapter for JSON, RESTEasy also supports integration with the
Jackson project. Many users find the output from Jackson much nicer than the Badger format
or Mapped format provided by Jettison. For more on Jackson 2, see http://wiki.fasterxml.com/
JacksonHome. Besides JAXB like APIs, it has a JavaBean based model, described at http://
wiki.fasterxml.com/JacksonDataBinding, which allows you to easily marshal Java objects to and
from JSON. RESTEasy integrates with the JavaBean model. While Jackson does come with its
own JAX-RS integration, RESTEasy expanded it a little, as decribed below.
NOTE. The resteasy-jackson-provider module, which is based on the outdated Jackson 1.9.x, is
currently deprecated, and will be removed in a release subsequent to 3.1.0.Final. The resteasy-
jackson2-provider module is based on Jackson 2.
23.1. Using Jackson 1.9.x Outside of WildFly
If you're deploying RESTEasy outside of WildFly, add the RESTEasy Jackson provder to your
WAR pom.xml build:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>${version.resteasy}</version>
</dependency>
23.2. Using Jackson 1.9.x Inside WildFly 8
If you're deploying RESTEasy with WildFly 8, there's nothing you need to do except to make sure
you've updated your installation with the latest and greatest RESTEasy. See the Installation/Con-
figuration section of this documentation for more details.
23.3. Using Jackson 2 Outside of WildFly
If you're deploying RESTEasy outside of WildFly, add the RESTEasy Jackson provder to your
WAR pom.xml build:
<dependency>
<groupId>org.jboss.resteasy</groupId>
Chapter 23. JSON Support via ...
106
<artifactId>resteasy-jackson2-provider</artifactId>
<version>${version.resteasy}</version>
</dependency>
23.4. Using Jackson 2 Inside WildFly 9 and above
If you're deploying RESTEasy with WildFly 9 or above, there's nothing you need to do except
to make sure you've updated your installation with the latest and greatest RESTEasy. See the
Installation/Configuration section of this documentation for more details.
23.5. Additional RESTEasy Specifics
The first extra piece that RESTEasy added to the integration was to support "application/*+json".
Jackson would only accept "application/json" and "text/json" as valid media types. This allows you
to create json-based media types and still let Jackson marshal things for you. For example:
@Path("/customers")
public class MyService {
@GET
@Produces("application/vnd.customer+json")
public Customer[] getCustomers() {}
}
Another problem that occurs is when you are using the RESTEasy JAXB providers along-
side Jackson. You may want to use Jettison and JAXB to output your JSON instead of Jack-
son. In this case, you must either not install the Jackson provider, or use the annotation
@org.jboss.resteasy.annotations.providers.NoJackson on your JAXB annotated classes. For ex-
ample:
@XmlRootElement
@NoJackson
public class Customer {...}
@Path("/customers")
public class MyService {
@GET
@Produces("application/vnd.customer+json")
public Customer[] getCustomers() {}
}
Possible Conflict With JAXB Provider
107
If you can't annotate the JAXB class with @NoJackson, then you can use the annotation on a
method parameter. For example:
@XmlRootElement
public class Customer {...}
@Path("/customers")
public class MyService {
@GET
@Produces("application/vnd.customer+json")
@NoJackson
public Customer[] getCustomers() {}
@POST
@Consumes("application/vnd.customer+json")
public void createCustomer(@NoJackson Customer[] customers) {...}
}
23.6. Possible Conflict With JAXB Provider
If your Jackson classes are annotated with JAXB annotations and you
have the resteasy-jaxb-provider in your classpath, you may trigger the Jetti-
sion JAXB marshalling code. To turn off the JAXB json marshaller use the
@org.jboss.resteasy.annotations.providers.jaxb.IgnoreMediaTypes("application/*+json") on your
classes.
23.7. JSONP Support
If you're using Jackson, RESTEasy has JSONP [http://
en.wikipedia.org/wiki/JSONP] that you can turn on by adding the
provider org.jboss.resteasy.plugins.providers.jackson.JacksonJsonpInterceptor
(Jackson2JsonpInterceptor if you're using the Jackson2 provider) to your deployments. If the me-
dia type of the response is json and a callback query parameter is given, the response will be
a javascript snippet with a method call of the method defined by the callback parameter. For ex-
ample:
GET /resources/stuff?callback=processStuffResponse
will produce this response:
Chapter 23. JSON Support via ...
108
processStuffResponse(<nomal JSON body>)
This supports the default behavior of jQuery [http://api.jquery.com/jQuery.ajax/
]. To enable JacksonJsonpInterceptor in WildFly, you need to import annota-
tions from org.jboss.resteasy.resteasy-jackson-provider module using jboss-deploy-
ment-structure.xml:
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.jboss.resteasy.resteasy-jackson-provider"
annotations="true"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
You can change the name of the callback parameter by setting the callbackQueryParameter prop-
erty.
JacksonJsonpInterceptor can wrap the response into a try-catch block:
try{processStuffResponse(<normal JSON body>)}catch(e){}
You can enable this feature by setting the resteasy.jsonp.silent property to true
Note. Because JSONP can be used in Cross Site Scripting Inclusion (XSSI) attacks,
Jackson2JsonpInterceptor is disabled by default. Two steps are necessary to enable it:
1. As noted above, Jackson2JsonpInterceptor must be included in the deployment. For exam-
ple, a service file META-INF/services/javax.ws.rs.ext.Providers with the line
org.jboss.resteasy.plugins.providers.jackson.Jackson2JsonpInterceptor
may be included on the classpath
2. Also, the servlet context parameter parameter "resteasy.jsonp.enable" must be set to "true".
Jackson JSON Decorator
109
23.8. Jackson JSON Decorator
If you are using the Jackson 2 provider, RESTEasy has provided a pretty-printing annotation
simliar with the one in JAXB provider:
org.jboss.resteasy.annotations.providers.jackson.Formatted
Here is an example:
@GET
@Produces("application/json")
@Path("/formatted/{id}")
@Formatted
public Product getFormattedProduct()
{
return new Product(333, "robot");
}
As the example shown above, the @Formatted annotation will enable the underlying Jackson
option "SerializationFeature.INDENT_OUTPUT".
23.9. JSON Filter Support
In Jackson2 , there is new feature JsonFilter [http://wiki.fasterxml.com/JacksonFeatureJsonFilter]
to allow annotate class with @JsonFilter and doing dynamic filtering. Here is an example which
defines mapping from "nameFilter" to filter instances and filter bean properties when serilize to
json format:
@JsonFilter(value="nameFilter")
public class Jackson2Product {
protected String name;
protected int id;
public Jackson2Product() {
}
public Jackson2Product(final int id, final String name) {
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
Chapter 23. JSON Support via ...
110
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
@JsonFilter annotates resource class to filter out some property not to serialize in the json re-
sponse. To map the filter id and instance we need to create another jackson class to add the id
and filter instance map:
public class ObjectFilterModifier extends ObjectWriterModifier {
public ObjectFilterModifier() {
}
@Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint,
MultivaluedMap<String, Object> httpHeaders, Object valueToWrite,
ObjectWriter w, JsonGenerator jg) throws IOException {
FilterProvider filterProvider = new SimpleFilterProvider().addFilter(
"nameFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
return w.with(filterProvider);
}
}
Here the method modify() will take care of filtering all properties except "name" property before
write. To make this work, we need let RESTEasy know this mapping info. This can be easily set
in a WriterInterceptor using Jackson's ObjectWriterInjector:
@Provider
public class JsonFilterWriteInterceptor implements WriterInterceptor{
private ObjectFilterModifier modifier = new ObjectFilterModifier();
@Override
JSON Filter Support
111
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
//set a threadlocal modifier
ObjectWriterInjector.set(modifier);
context.proceed();
}
}
Alternatively, Jackson's documentation suggest doing the same in a servlet filter; that however
potentially leads to issues on RESTEasy, as the ObjectFilterModifier ends up being stored using
a ThreadLocal object and there's no guarantee the same thread serving the servlet filter will be
running the resource endpoint execution too. So, for the servlet filter scenario, RESTEasy offers
its own injector that relies on the current thread context classloader for carrying over the specified
modifier:
public class ObjectWriterModifierFilter implements Filter {
private static ObjectFilterModifier modifier = new ObjectFilterModifier();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ResteasyObjectWriterInjector.set(Thread.currentThread().getContextClassLoader(),
modifier);
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
112
Chapter 24.
113
Chapter 24. JSON Support via Java
EE 7 JSON-P API
No, this is not the JSONP you are thinking of! JSON-P is a new Java EE 7 JSON parsing API.
Horrible name for a new JSON parsing API! What were they thinking? Anyways, RESTEasy has a
provider for it. If you are using WildFly, it is required by Java EE 7 so you will have it automatically
bundled. Otherwise, use this maven dependency.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-json-p-provider</artifactId>
<version>3.6.2.Final</version>
</dependency>
It has built in support for JsonObject, JsonArray, and JsonStructure as request or response enti-
ties. It should not conflict with Jackson or Jettison if you have that in your path too.
114
Chapter 25.
115
Chapter 25. Multipart Providers
RESTEasy has rich support for the "multipart/*" and "multipart/form-data" mime types. The multi-
part mime format is used to pass lists of content bodies. Multiple content bodies are embedded
in one message. "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.
RESTEasy 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
25.1. Input with multipart/mixed
When writing a JAX-RS service, RESTEasy provides an interface that allows you to read in any
multipart mime type. org.jboss.resteasy.plugins.providers.multipart.MultipartInput
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();
}
Chapter 25. Multipart Providers
116
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. Each part has a set of headers
associated with it You can unmarshall the part by calling one of the getBody() methods. The Type
genericType parameter can be null, but the Class 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. The following piece of code is unmarshalling parts which are XML into a JAXB annotated
class called Customer.
@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();
}
}
Sometimes you may want to unmarshall a body part that is sensitive to generic type metadata.
In this case you can use the org.jboss.resteasy.util.GenericType class. Here's an example of
unmarshalling a type that is 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();
}
java.util.List with multipart data
117
}
Use of GenericType is required because it is really the only way to obtain generic type information
at runtime.
25.2. java.util.List with multipart data
If your body parts are uniform, you do not have to manually unmarshall 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. Here's an example again of unmarshalling
a list of customers.
@Path("/multipart")
public class MyService
{
@PUT
@Consumes("multipart/mixed")
public void put(List<Customer> customers)
{
...
}
}
25.3. Input with multipart/form-data
When writing a JAX-RS service, RESTEasy provides an interface that allows you to read in mul-
tipart/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 multi-
part 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
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;
Chapter 25. Multipart Providers
118
}
It works in much the same way as MultipartInput described earlier in this chapter.
25.4. java.util.Map with multipart/form-data
With form-data, if your body parts are uniform, you do not have to manually unmarshall 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. Here's an example of of
unmarshalling a Map of Customer objects which are JAXB annotated classes.
@Path("/multipart")
public class MyService
{
@PUT
@Consumes("multipart/form-data")
public void put(Map<String, Customer> customers)
{
...
}
}
25.5. Input with multipart/related
When writing a JAX-RS service, RESTEasy provides an interface that allows you to read in mul-
tipart/related mime type. A multipart/related is used to indicate that message parts should not be
considered individually but rather as parts of an aggregate whole. One example usage for multi-
part/related is to send a web page complete with images in a single message. Every multipart/re-
lated message has a root/start part that references the other parts of the message. The parts are
identified by their "Content-ID" headers. multipart/related is defined by RFC 2387. The interface
used for related input is org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedInput
public interface MultipartRelatedInput extends MultipartInput
{
String getType();
String getStart();
String getStartInfo();
InputPart getRootPart();
Output with multipart
119
Map<String, InputPart> getRelatedMap();
}
It works in much the same way as MultipartInput described earlier in this chapter.
25.6. Output with multipart
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()
}
When you want to output multipart data it is as simple as creating a MultipartOutput object and
calling addPart() methods. RESTEasy will automatically find a MessageBodyWriter to marshall
your entity objects. Like MultipartInput, sometimes you may have marshalling which is sensitive to
generic type metadata. In that case, use GenericType. Most of the time though passing in an Ob-
Chapter 25. Multipart Providers
120
ject and its MediaType is enough. In the example below, we are sending back a "multipart/mixed"
format back to the calling client. The parts are Customer objects which are JAXB annotated and
will be marshalling into "application/xml".
@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;
}
}
25.7. Multipart Output with java.util.List
If your body parts are uniform, you do not have to manually marshall each and every part or even
use a MultipartOutput object.. You can just provide a java.util.List. It 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 what media type each part is. Here's an
example of sending back a list of customers back to a client. The customers are JAXB objects
@Path("/multipart")
public class MyService
{
@GET
@Produces("multipart/mixed")
@PartType("application/xml")
public List<Customer> get()
{
...
}
}
25.8. Output with multipart/form-data
RESTEasy provides a simple API to output multipart/form-data.
Output with multipart/form-data
121
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()
public Map<String, List<OutputPart>> getFormDataMap()
}
When you want to output multipart/form-data it is as simple as creating a MultipartFormDataOutput
object and calling addFormData() methods. RESTEasy will automatically find a MessageBody-
Writer to marshall your entity objects. Like MultipartInput, sometimes you may have marshalling
which is sensitive to generic type metadata. In that case, use GenericType. Most of the time though
passing in an Object and its MediaType is enough. In the example below, we are sending back a
"multipart/form-data" format back to the calling client. The parts are Customer objects which are
JAXB annotated and will be marshalling into "application/xml".
@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;
}
}
When using form-data format the named content can be a list of OutputPart objects as long as
each object in the named list contains a uniform object and media type. In the example below,
Chapter 25. Multipart Providers
122
we are sending back a "multipart/form-data" format which consists of two named list of objects,
bill and monica.
@Path("/form")
public class MyService
{
@GET
@Produces("multipart/form-data")
public MultipartFormDataOutput get()
{
MultipartFormDataOutput output = new MultipartFormDataOutput();
output.addPart("smith", new Customer("Joe Smith"),
MediaType.APPLICATION_XML_TYPE);
output.addPart("monica", new Employee("monica"),
MediaType.APPLICATION_JSON_TYPE);
output.addPart("smith", new Customer("Deb Smith"),
MediaType.APPLICATION_XML_TYPE);
output.addPart("smith", new Customer("Buba Smith"),
MediaType.APPLICATION_XML_TYPE);
return output;
}
}
25.9. Multipart FormData Output with java.util.Map
If your body parts are uniform, you do not have to manually marshall each and every part or even
use a MultipartFormDataOutput object.. You can just provide a java.util.Map. It 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 what media type each part
is. Here's an example of sending back a list of customers back to a client. The customers are
JAXB objects
@Path("/multipart")
public class MyService
{
@GET
@Produces("multipart/form-data")
@PartType("application/xml")
public Map<String, Customer> get()
{
...
}
Output with multipart/related
123
}
25.10. Output with multipart/related
RESTEasy provides a simple API to output multipart/related.
package org.jboss.resteasy.plugins.providers.multipart;
public class MultipartRelatedOutput extends MultipartOutput
{
public OutputPart getRootPart()
public OutputPart addPart(Object entity, MediaType mediaType,
String contentId, String contentTransferEncoding)
public String getStartInfo()
public void setStartInfo(String startInfo)
}
When you want to output multipart/related it is as simple as creating a MultipartRelatedOutput
object and calling addPart() methods. The first added part will be used as the root part of the
multipart/related message. RESTEasy will automatically find a MessageBodyWriter to marshall
your entity objects. Like MultipartInput, sometimes you may have marshalling which is sensitive
to generic type metadata. In that case, use GenericType. Most of the time though passing in an
Object and its MediaType is enough. In the example below, we are sending back a "multipart/re-
lated" format back to the calling client. We are sending a html with 2 images.
@Path("/related")
public class MyService
{
@GET
@Produces("multipart/related")
public MultipartRelatedOutput get()
{
MultipartRelatedOutput output = new MultipartRelatedOutput();
output.setStartInfo("text/html");
Map<String, String> mediaTypeParameters = new LinkedHashMap<String,
String>();
mediaTypeParameters.put("charset", "UTF-8");
mediaTypeParameters.put("type", "text/html");
output.addPart(
Chapter 25. Multipart Providers
124
"<html><body>\n"
+ "This is me: <img src='cid:http://example.org/me.png' />\n"
+ "<br />This is you: <img src='cid:http://example.org/you.png' />\n"
+ "</body></html>",
new MediaType("text", "html", mediaTypeParameters),
"<mymessage.xml@example.org>", "8bit");
output.addPart("// binary octets for me png",
new MediaType("image", "png"), "<http://example.org/me.png>",
"binary");
output.addPart("// binary octets for you png", new MediaType(
"image", "png"),
"<http://example.org/you.png>", "binary");
client.putRelated(output);
return output;
}
}
25.11. @MultipartForm and POJOs
If you have a exact knowledge of your multipart/form-data packets, you can
map them to and from a POJO class to and from multipart/form-data using the
@org.jboss.resteasy.annotations.providers.multipart.MultipartForm annotation and the JAX-RS
@FormParam annotation. You simple define a POJO with at least a default constructor and an-
notate its fields and/or properties with @FormParams. These @FormParams must also be anno-
tated with @org.jboss.resteasy.annotations.providers.multipart.PartType if you are doing output.
For example:
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. Here's an
example of sending a CustomerProblemForm using the RESTEasy client framework:
@MultipartForm and POJOs
125
@Path("portal")
public interface CustomerPortal {
@Path("issues/{id}")
@Consumes("multipart/form-data")
@PUT
public void putProblem(@MultipartForm CustomerProblemForm,
@PathParam("id") int id);
}
{
CustomerPortal portal = ProxyFactory.create(CustomerPortal.class, "http://
example.com");
CustomerProblemForm form = new CustomerProblemForm();
form.setCustomer(...);
form.setProblem(...);
portal.putProblem(form, 333);
}
You see that the @MultipartForm annotation was used to tell RESTEasy that the object has
@FormParam and that it should be marshalled from that. You can also use the same object to
receive multipart data. Here is an example of the server side counterpart of our customer portal.
@Path("portal")
public class CustomerPortalServer {
@Path("issues/{id})
@Consumes("multipart/form-data")
@PUT
public void putIssue(@MultipartForm CustoemrProblemForm,
@PathParam("id") int id) {
... write to database...
}
}
In addition to the XML data format, you can also use JSON formatted data to represent your POJO
classes. To achieve this goal, you need to plug in a JSON provider into your project. For example,
you can add RESTEasy Jackson2 Provider into your project's dependency scope:
<dependency>
Chapter 25. Multipart Providers
126
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>${resteasy.ver}</version>
</dependency>
And then you can write an ordinary POJO class, which Jackson2 can automatically serialize/de-
serialize it in JSON format:
public class JsonUser {
private String name;
public JsonUser() {
}
public JsonUser(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The resource class can be written like this:
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import org.jboss.resteasy.annotations.providers.multipart.PartType;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
@Path("/")
public class JsonFormResource {
public JsonFormResource() {
}
public static class Form {
@MultipartForm and POJOs
127
@FormParam("user")
@PartType("application/json")
private JsonUser user;
public Form() {
}
public Form(final JsonUser user) {
this.user = user;
}
public JsonUser getUser() {
return user;
}
}
@PUT
@Path("form/class")
@Consumes("multipart/form-data")
public String putMultipartForm(@MultipartForm Form form) {
return form.getUser().getName();
}
}
As the code shown above, you can see the PartType of JsonUser is marked as "application/json",
and it's included in the "@MultipartForm Form" class instance.
To send request to the resource method, you need to send JSON formatted data that is corre-
sponding with the JsonUser class. The easiest to do this is to use a proxy class that has the same
definition like the resource class. Here is the sample code of the proxy class that is corresponding
with the JsonFormResource class:
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
@Path("/")
public interface JsonForm {
@PUT
@Path("form/class")
@Consumes("multipart/form-data")
String putMultipartForm(@MultipartForm JsonFormResource.Form form);
Chapter 25. Multipart Providers
128
}
And then you can use the proxy class above to send request to the resource method correctly.
Here is the sample code:
ResteasyClient client = new ResteasyClientBuilder().build();
...
JsonForm proxy =
client.target("your_request_url_address").proxy(JsonForm.class);
String name = proxy.putMultipartForm(new JsonFormResource.Form(new
JsonUser("bill")));
...
And if your client side has Jackson2 provider included, your request will be marshaled correctly,
and your JsonUser data will be converted into JSON format and then send to the server side. You
can also use hand-crafted JSON data as your request and send it to server side, but you have to
make sure the request data is in correct form then.
25.12. XML-binary Optimized Packaging (Xop)
RESTEasy supports Xop messages packaged as multipart/related. What does this mean? 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 results in faster transport while still using the convenient POJO. More about Xop can
be read here: http://www.w3.org/TR/xop10/. Now lets see an example:
First we have a JAXB annotated POJO to work with. @XmlMimeType tells JAXB the mime type
of the binary content (its not required to do XOP packaging but it is recommended to be set if
you know the exact type):
@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;
XML-binary Optimized Packaging (Xop)
129
// methods, other fields ...
}
In the above POJO myBinary and myDataHandler will be processed as binary attachments while
the whole Xop object will be sent as xml (in the places of the binaries only their references will
be generated). javax.activation.DataHandler is the most general supported type so if you need an
java.io.InputStream or a javax.activation.DataSource you need to go with the DataHandler. Some
other special types are supported too: java.awt.Image and javax.xml.transform.Source. Let's as-
sume that Customer is also JAXB friendly POJO in the above example (of course it can also have
binary parts). Now lets see a an example Java client that sends this:
// 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);
}
We used @Consumes(MultipartConstants.MULTIPART_RELATED) to tell RESTEasy that we
want to send multipart/related packages (that's the container format that will hold our Xop mes-
sage). We used @XopWithMultipartRelated to tell RESTEasy that we want to make Xop mes-
sages. So we have a POJO and a client service that is willing to send it. All we need now a server
that can read it:
@Path("/mime")
public class XopService {
@PUT
@Path("xop")
@Consumes(MultipartConstants.MULTIPART_RELATED)
Chapter 25. Multipart Providers
130
public void putXopWithMultipartRelated(@XopWithMultipartRelated Xop xop) {
// do very important things here
}
}
We used @Consumes(MultipartConstants.MULTIPART_RELATED) to tell RESTEasy that we
want to read multipart/related packages. We used @XopWithMultipartRelated to tell RESTEasy
that we want to read Xop messages. Of course we could also produce Xop return values but we
would than also need to annotate that and use a Produce annotation, too.
25.13. Note about multipart parsing and working with
other frameworks
There are a lot of frameworks doing multipart parsing automatically with the
help of filters and interceptors. Like 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.
25.14. 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
fallback. This is the value defined by the MIME RFC. However for example some web clients (like
most, if not all, web browsers) do not send Content-Type headers for all fields in a multipart/form-
data request (only for the file parts). This can cause character encoding and unmarshalling errors
on the server side. To correct this there is an option to define an other, non-rfc compliant fallback
value. This can be done dynamically per request with the PreProcessInterceptor infrastructure of
RESTEasy. In the following example we will set "*/*; charset=UTF-8" as the new 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;
}
Overwriting the content type for multipart mes-
sages
131
}
25.15. Overwriting the content type for multipart mes-
sages