JAX-RS Client
Goals
- Explore the JAX-RS library for clients.
- Learn how to use Restito for testing REST clients.
- Consider ways to reuse RESTful code between client and server.
Concepts
- client
- entity
- invocation
- invoke
- unmarshal
- web target
Library
java.io.InputStream
java.lang.Class.getResource(String name)
java.lang.Class.getResourceAsStream(String name)
javax.ws.rs.NotFoundException
javax.ws.rs.RedirectionException
javax.ws.rs.RedirectionException.getLocation()
javax.ws.rs.WebApplicationException
javax.ws.rs.client
javax.ws.rs.client.Client
javax.ws.rs.client.Client.close()
javax.ws.rs.client.Client.target(String uri)
javax.ws.rs.client.Client.target(URI uri)
javax.ws.rs.client.Client.target(UriBuilder uriBuilder)
javax.ws.rs.client.ClientBuilder
javax.ws.rs.client.ClientBuilder.build()
javax.ws.rs.client.ClientBuilder.newBuilder()
javax.ws.rs.client.ClientBuilder.newClient()
javax.ws.rs.client.Entity<T>
javax.ws.rs.client.Entity.entity(T entity, MediaType mediaType)
javax.ws.rs.client.Entity.form(Form form)
javax.ws.rs.client.Entity.json(T entity)
javax.ws.rs.client.Invocation
javax.ws.rs.client.Invocation.invoke()
javax.ws.rs.client.Invocation.invoke(Class<T> responseType)
javax.ws.rs.client.Invocation.Builder
javax.ws.rs.client.Invocation.Builder.accept(MediaType... mediaTypes)
javax.ws.rs.client.Invocation.Builder.acceptEncoding(String... encodings)
javax.ws.rs.client.Invocation.Builder.acceptLanguage(Locale... locales)
javax.ws.rs.client.Invocation.Builder.build(String method)
javax.ws.rs.client.Invocation.Builder.build(String method, Entity<?> entity)
javax.ws.rs.client.Invocation.Builder.buildGet()
javax.ws.rs.client.Invocation.Builder.buildPost(Entity<?> entity)
javax.ws.rs.client.Invocation.Builder.buildPut(Entity<?> entity)
javax.ws.rs.client.Invocation.Builder.cookie(Cookie cookie)
javax.ws.rs.client.Invocation.Builder.header(String name, Object value)
javax.ws.rs.client.SyncInvoker
javax.ws.rs.client.SyncInvoker.get()
javax.ws.rs.client.SyncInvoker.get(Class<T> responseType)
javax.ws.rs.client.SyncInvoker.put(Entity<?> entity)
javax.ws.rs.client.SyncInvoker.put(Entity<?> entity, Class<T> responseType)
javax.ws.rs.client.SyncInvoker.method(String name)
javax.ws.rs.client.SyncInvoker.method(String name, Class<T> responseType)
javax.ws.rs.client.SyncInvoker.method(String name, Entity<?> entity)
javax.ws.rs.client.SyncInvoker.method(String name, Entity<?> entity, Class<T> responseType)
javax.ws.rs.client.WebTarget
javax.ws.rs.client.WebTarget.matrixParam(String name, Object... values)
javax.ws.rs.client.WebTarget.queryParam(String name, Object... values)
javax.ws.rs.client.WebTarget.request()
javax.ws.rs.client.WebTarget.request(MediaType... acceptedResponseTypes)
javax.ws.rs.client.WebTarget.resolveTemplate(String name, Object value)
javax.ws.rs.core.Form
javax.ws.rs.core.HttpHeaders
javax.ws.rs.core.MediaType
javax.ws.rs.core.Configurable<C extends Configurable>
javax.ws.rs.core.Configurable.property(String name, Object value)
javax.ws.rs.core.Configurable.register(Class<?> componentClass)
javax.ws.rs.core.Configurable.register(Object component)
javax.ws.rs.core.Response
javax.ws.rs.core.Response.getStatus()
javax.ws.rs.core.Response.close()
javax.ws.rs.core.Response.getHeaderString(String name)
javax.ws.rs.core.Response.getLocation()
javax.ws.rs.core.Response.getMediaType()
javax.ws.rs.core.Response.readEntity(Class<T> entityType)
javax.ws.rs.core.UriBuilder
javax.ws.rs.core.UriBuilder.path(Class resource)
javax.ws.rs.core.UriBuiler.path(Class resource, String method)
javax.ws.rs.ext.MessageBodyReader<T>
javax.ws.rs.ext.MessageBodyWriter<T>
com.xebialabs.restito.builder.stub.StubHttp
com.xebialabs.restito.builder.stub.StubHttp.match(Condition... conditions)
com.xebialabs.restito.builder.stub.StubHttp.whenHttp(StubServer server)
com.xebialabs.restito.builder.stub.StubWithCondition
com.xebialabs.restito.builder.stub.StubWithCondition.then(Applicable... actions)
com.xebialabs.restito.builder.verify.VerifyHttp
com.xebialabs.restito.builder.verify.VerifyHttp.atLeast(int t, Condition... conditions)
com.xebialabs.restito.builder.verify.VerifyHttp.never(Condition... conditions)
com.xebialabs.restito.builder.verify.VerifyHttp.once(Condition... conditions)
com.xebialabs.restito.builder.verify.VerifyHttp.times(int t, Condition... conditions)
com.xebialabs.restito.builder.verify.VerifyHttp.verifyHttp(StubServer stubServer)
com.xebialabs.restito.builder.verify.VerifySequenced
com.xebialabs.restito.builder.verify.VerifySequenced.then()
com.xebialabs.restito.semantics.Action
com.xebialabs.restito.semantics.Action.bytesContent(byte[] content)
com.xebialabs.restito.semantics.Action.charset(Charset charset)
com.xebialabs.restito.semantics.Action.charset(String charset)
com.xebialabs.restito.semantics.Action.contentType(String contentType)
com.xebialabs.restito.semantics.Action.header(String key, String value)
com.xebialabs.restito.semantics.Action.noContent()
com.xebialabs.restito.semantics.Action.noop()
com.xebialabs.restito.semantics.Action.ok()
com.xebialabs.restito.semantics.Action.resourceContent(String resourcePath)
com.xebialabs.restito.semantics.Action.resourceContent(String resourcePath, String charset)
com.xebialabs.restito.semantics.Action.resourceContent(URL resourceUrl)
com.xebialabs.restito.semantics.Action.resourceContent(URL resourceUrl, Charset charset)
com.xebialabs.restito.semantics.Action.status(HttpStatus status)
com.xebialabs.restito.semantics.Action.stringContent(String content)
com.xebialabs.restito.semantics.Action.success()
com.xebialabs.restito.semantics.Action.unauthorized()
com.xebialabs.restito.semantics.Action.unauthorized(String realm)
com.xebialabs.restito.semantics.Condition
com.xebialabs.restito.semantics.Condition.get(String uri)
com.xebialabs.restito.semantics.Condition.matchesUri(Pattern p)
com.xebialabs.restito.semantics.Condition.method(Method m)
com.xebialabs.restito.semantics.Condition.not(Condition c)
com.xebialabs.restito.semantics.Condition.parameter(String key, String... parameterValues)
com.xebialabs.restito.semantics.Condition.uri(String uri)
com.xebialabs.restito.semantics.Condition.withHeader(String key)
com.xebialabs.restito.semantics.Condition.withHeader(String key, String value)
com.xebialabs.restito.server.StubServer
com.xebialabs.restito.server.StubServer.StubServer(int port, Stub... stubs)
com.xebialabs.restito.server.StubServer.DEFAULT_PORT
com.xebialabs.restito.server.StubServer.getPort()
com.xebialabs.restito.server.StubServer.secure()
com.xebialabs.restito.server.StubServer.start()
com.xebialabs.restito.server.StubServer.stop()
Dependencies
javax.ws.rs:javax.ws.rs-api:2.1
(scope:provided
)org.jboss.resteasy:resteasy-client:3.1.4.Final
com.xebialabs.restito:restito:0.9.2
(scope:test
)
Preview
try {
final Pen pigpen = ClientBuilder.newClient()
.target(UriBuilder.fromUri("https://example.com/farm/").path(PensResource.class, "getPen"))
.request()
.accept(MediaType.APPLICATION_JSON_TYPE)
.get(Pen.class);
//TODO do something with the pigpen
} catch(final NotFoundException notFoundException) {
//TODO handle the missing pigpen
}
Lesson
JAX-RS is very convenient for specifying a RESTful API and implementing RESTful services. But the framework is not only for the server side; JAX-RS also provides a comprehensive API for writing clients that can access RESTful services. The JAX-RS client API is included with the dependency javax.ws.rs:javax.ws.rs-api
, which also contains the server API. As with the server API, you will need to choose a JAX-RS client API implementation for your program to work. You should write your code to the API itself, though, independent of the implementation you use.
The JAX-RS client API is contained in javax.ws.rs.client
package, and has three central concepts:
- client
- Manages connections to the server.
- web target
- Defines a RESTful endpoint and its parameters.
- invocation
- Encapsulates a reusable client request to a particular web target.
Client
The term “client” can have different levels of meaning. When used in describing client/server communication, it can refer to the program that program that sends requests to the server. In JAX-RS, the javax.ws.rs.client.Client
interface encapsulates the entire client-side infrastructure for client communication—in other words, it represents the part of your program that actually performs the “client” functionality. Some of the responsibilities of JAX-RS Client
include:
- Handling HTTP connections (resusing them when appropriate).
- Controlling SSL/TLS certificate verification.
- Managing authentication credentials for individual client users.
Clients are created using the javax.ws.rs.client.ClientBuilder
class. The easiest way to create a client is to call ClientBuilder.newClient()
. However most commonly programs will need to create a ClientBuilder
using ClientBuilder.newBuilder()
, configure the builder, and then call ClientBuilder.build()
to create the actual Client
instance. ClientBuilder
comes with a fluent interface, allowing you to chain configuration method calls.
WebTarget
The main access to RESTful API endpoints is represented by instances of the javax.ws.rs.client.WebTarget
interface. A Client
instance acts as a factory for a WebTarget
most commonly using the Client.target(URI uri)
method. There is also a String
version Client.target(String uri)
, allowing you to call client.target("https://example.com/farm/pens/")
to retrieve all the pens from the example endpoint in the JAX-RS lesson.
But the power of WebTarget
lies in its ability to maintain a template for resolving path parameters, as well as adding query parameters or matrix parameters on the fly. You learned in the previous JAX-RS lesson that the server API allows you to define a path parameter by placing some variable name between curly brackets {
and }
. WebTarget
recognizes the same template format for URIs. For example, you can call client.target("https://example.com/farm/pens/{penId}")
to create a template WebTarget
instance; later, before using the WebTarget
, you can use one or more of its fluent builder methods to create another WebTarget
instance with the parameters replaced. Here are some of the common fluent customization methods of a WebTarget
:
matrixParam(String name, Object... values)
- Creates a new
WebTarget
instance with the given matrix parameters added to the last URI segment. queryParam(String name, Object... values)
- Creates a new
WebTarget
instance with the given query parameter added. resolveTemplate(String name, Object value)
- Encodes the indicated value and uses it to replaces the indicated template parameter, returning a new
WebTarget
with the result. There exist other variations of this method that allow substitution of mltiple variables, as well as more control over whether and how the value is encoded; see theWebTarget
API documentation for more details.
Invocation
After defining an endpoint to be used by the client, you will need to invoke the web target. The javax.ws.rs.client.Invocation
interface provides all the information needed to actually make the HTTP request. To configure the Invocation
, you will use a javax.ws.rs.client.Invocation.Builder
, which you can retrieve for a specific web target using WebTarget.request()
.
Configuring an Invocation
An Invocation
instance is essentially an HTTP request ready to be invoked, containing all the necessary configuration for headers and other HTTP information. Here are some of the useful fluent configuration methods of an Invocation.Builder
:
accept(MediaType... mediaTypes)
- Sets the
Accept
header for the HTTP request. There is a companion method that acceptsString
media types. acceptEncoding(String... encodings)
- Sets the
Accept-Encoding
header for the HTTP request. acceptLanguage(Locale... locales)
- Sets the
Accept-Language
header for the HTTP request. There is a companion method that acceptsString
language tags. build(String method)
- Builds the
Invocation
, configuring it to invoke the named HTTP method such asGET
. Convenience methods are provided for common HTTP methods, such asbuildGet()
. cookie(Cookie cookie)
- Adds a cookie to be set by the request. There is a companion method that accepts a
String
name and value. header(String name, Object value)
- Sets an arbitrary header of the HTTP request. Many of the above header-specific methods are convenience versions of this method.
Invoking an HTTP Request
An Invocation
only encapsulates the information necessary to make the HTTP request. To actually invoke the request, call Invocation.invoke()
, which when finished will return an instance of javax.ws.rs.core.Response
. This is the same Response
type used by the JAX-RS server API you already studied.
From the Response
you can determine the HTTP response code using Response.getStatus()
. You can use methods such as Response.getLocation()
for example to determine the Location
header (for redirect responses), or Response.getHeaderString(String name)
to get an arbitrary header by name. Response.getMediaType()
will return the type of content returned (specified in the Content-Type header), or null
if there is no response content.
As a shortcut, the javax.ws.rs.client.SyncInvoker
interface allows you to skip the explicit creation of an intermediate Invocation
object, and invoke the request directly from the Invocation.Builder
, which implements that interface. SyncInvoker provides a plethora of invocation methods such as SyncInvoker.get()
for performing an HTTP GET
request, and SyncInvoker.method(String name)
for invoking some arbitrary HTTP method. These methods and similar ones return the same Response
as you would get from calling Invocation.invoke()
, allowing the above example to be rewritten in a more compact form.
Response Content
To process the returned information, you can request to read an entity from the response using Response.readEntity(Class<T> entityType)
, specifying the type of response you expect.
Raw Bytes
At the lowest level of processing, you can request a java.io.InputStream
which will allow you to read the raw bytes returned by the response. As usual you must close the stream when you are finished with it.
Strings
Most of the time you do not want to handle parsing an input stream manually when processing a response. JAX-RS can take care of producing many entity types automatically. If the response content type can be interpreted as text (such as text/xml
or more obviously text/plain
), simply call response.readEntity(String.class)
. JAX-RS will take care converting the bytes based upon the charset, and return a string with the contents.
Unmarshaling Entities
The preferred higher-level approach is to let JAX-RS completely take care of object marshaling. When you used JAX-RS on the server, you learned that you could automatically marshal returned objects by registering a javax.ws.rs.ext.MessageBodyWriter<T>
for the appropriate type. The same approach works on the client side; if you have an appropriate javax.ws.rs.ext.MessageBodyReader<T>
registered for the FooBar
type, you can call response.readEntity(FooBar.class)
and JAX-RS will automatically unmarshal the content for you and return an instance of FooBar
. There is no need to close any input stream; JAX-RS handles this for you.
For example you can use the same Jackson library from the previous lessons to automatically unmarshal an instance of the Pen
class using content returned as JSON.
Bypassing Response
with Entities
By specifying a response type during invocation, you can bypass altogether the explicit creation and processing of the Response
object. The Invocation.invoke(Class<T> responseType)
method will invoke the HTTP method check the Response
for an error condition. If the request failed, JAX-RS will throw the appropriate javax.ws.rs.WebApplicationException
subtype, for example javax.ws.rs.NotFoundException
, indicating the problem. This is the same WebApplicationException
type used by the JAX-RS server API you already studied. If the request was successful, JAX-RS will automatically read, unmarshal, and return the entity. With this approach there is no need to worry about the intermediate Response
object!
In fact a series of SyncInvoker
methods such as SyncInvoker.get(Class<T> responseType)
allow you to bypass, not only the intermediate Response
, but also the intermediate Invocation
itself. Calling one of these methods (including SyncInvoker.method(String name, Class<T> responseType)
for arbitrary HTTP methods) on the Invocation.Builder
as you did above results in a very compact and readable invocation, with automatic unmarshaling.
Request Content
For several HTTP methods such as PUT
or POST
you may need to send resource representations in addition to receiving them. Resource representations to send are wrapped with the javax.ws.rs.client.Entity<T>
class, which encapsulates the object itself along with other information such as the media type to use. The Entity
class comes with several static factory methods, such as Entity.entity(T entity, MediaType mediaType)
.
Once you have an entity, you can send it in the request using variations of the methods you've seen above, depending on which form of invocation you are using.
- You can build an
Invocation
with anEntity
using one of the convenience methods such asInvocation.Builder.buildPut(Entity<?> entity)
orInvocation.Builder.buildPost(Entity<?> entity)
, or more generally usingInvocation.Builder.build(String method, Entity<?> entity)
for an arbitrary HTTP method. - You can invoke an
Invocation
directly from anInvocation.Builder
using one of its shortcutSyncInvoker
methods such asSyncInvoker.put(Entity<?> entity)
, which returns aResponse
for further processing; orSyncInvoker.put(Entity<?> entity, Class<T> responseType)
, which bypasses theResponse
altogether to return the response object as the requested type. VariationsSyncInvoker.method(String name, Entity<?> entity)
andSyncInvoker.method(String name, Entity<?> entity, Class<T> responseType)
exist for calling arbitrary HTTP methods using the same respective approaches.
Configuration
For everything to work smoothly you'll likely need to configure JAX-RS. You may need to provide authentication credentials for the HTTP connection, and you will want to register providers for marshaling custom types. Such configuration is performed using methods of the javax.ws.rs.core.Configurable<C extends Configurable>
interface. Several of the JAX-RS client components implement Configurable
:
ClientBuilder
Client
WebTarget
This means that you can perform configuration at the place most convenient. For example you may want to configure HTTP authentication information at the client level, but install different providers for marshaling based upon which web target you are using. Or you may simply configure the client with all the necessary settings for the entire application—you can still provide overriding properties and/or registrations at the other levels. Here are some of the most common Configurable
methods:
property(String name, Object value)
- Sets a configuration property, updating an existing property if it has been set already.
register(Class<?> componentClass)
- Registers a class of a provider, such as a
MessageBodyReader<T>
, to be instantiated when needed. register(Object component)
- Registers a component that has already been instantiated.
Testing with Restito
RESTful client code needs to be tested like any other code, but the need for a server to connect to presents some difficulties. It would be nice to test the functionality of the REST client, independent of the real server—to test the REST client as a unit, without invoking the entire server back-end implementation, which would bring in code that itself needs to be tested. The more isolated the unit tests, the better.
You've already learned about test doubles, and how they can “mock” a dependency to return values necessary just for testing. The Restito testing framework for REST clients uses the same concept to essentially mock the entire server layer. In other words, Restito creates a fake server that you can configure, using a fluent API, to respond to your client requests with prepared data as if it were the real server you would use in production.
Restito Server
You'll need a com.xebialabs.restito.server.StubServer
to run during each unit test of your RESTful client. This is best done using the JUnit @Before
and @After
annotations you learned about for hooking into the unit test life cycle. You can start the server using StubServer.start()
, and stop it using StubServer.stop()
.
Once the server is started, you can find out the server's port using StubServer.getPort()
.
Stubbing Endpoints
Now that the mock server is in place, you'll need to stub out some RESTful endpoints. The first step is to define the endpoints, but not in a declarative fashion as you used in JAX-RS on the server. Rather Restito uses an fluent, imperative approach, essentially saying, When a particular HTTP request is made at a particular path with certain parameters, then…
.
To start stubbing, call the static factory method StubHttp.whenHttp(StubServer server)
to get a stubbed version of the server, com.xebialabs.restito.builder.stub.StubHttp
. This class allows you to check for specific conditions such as incoming HTTP requests using StubHttp.match(Condition... conditions)
. Here are some common conditions, each a subclass of com.xebialabs.restito.semantics.Condition
, returned via fluent factory methods of the same Condition
class:
get(String uri)
- Matches when the HTTP method
GET
was called on the URI path. Similar methods are available forDELETE
,PATCH
,POST
, andPUT
. matchesUri(Pattern p)
- Matches a URI pattern.
method(Method m)
- Matches arbitrary HTTP methods. Currently the HTTP method must be identified using a class from the underlying Glassfish server Restito uses. See Restito Issue #22.
not(Condition c)
- Negates another condition.
parameter(String key, String... parameterValues)
- Matches a URI with the given parameter(s).
uri(String uri)
- Matches an exact URI path.
withHeader(String key, String value)
- Matches an HTTP request with the given header set to the given value. There is a similar method
withHeader(String key)
that simply matches the presence of a header, without regard to its value.
Providing one or more conditions will return a com.xebialabs.restito.builder.stub.StubWithCondition
which allows you to specify what actions to perform in response to the request. You provide the actions using StubWithCondition.then(Applicable... actions)
. The actions you provide are usually implementations of the com.xebialabs.restito.semantics.Action
class, and can be retrieved using fluent factory methods of the Action
class itself. Here are some of the actions you'll use most often:
bytesContent(byte[] content)
- Provides actual bytes to return in the content.
charset(Charset charset)
- Sets the charset of the response. A similar method
charset(String charset)
exists which takes a string. You must set the charset before specifying content to return. contentType(String contentType)
- Indicates the content type of the response.
header(String key, String value)
- Sets a response header name and value.
noContent()
- Sets the HTTP response code to
204
(No Content
). noop()
- Does nothing.
ok()
- Sets the HTTP response code to
200
(OK
). There is an equivalent methodsuccess()
, butok()
should usually be used instead as it makes clearer which HTTP response is indicated. resourceContent(URL resourceUrl)
- Returns content from the given URL. Restito will attempt to determine the content type based upon the resource extension; currently
.xml
and.json
are supported. The convenience methodresourceContent(URL resourceUrl, Charset charset)
allows you to indicate the content and the charset at the same time, without needing to callcharset(Charset charset)
separately. Be careful with the similar methodsresourceContent(String resourcePath)
andresourceContent(String resourcePath, String charset)
; these take string resource paths, but use the classloader of the Action class, which may not be appropriate for loading your resources. status(HttpStatus status)
- Allows an arbitrary HTTP status to be returned. Currently the HTTP status must be identified using a class from the underlying Glassfish server Restito uses.
stringContent(String content)
- Returns the provided response content as text. As of Restito 0.9.2 there is a severe string encoding bug that uses the default system charset to determing the string content. Avoid this method for now and call
charset(charset)
followed bybytesContent(string.getBytes(charset))
. See Restito Issue #66. unauthorized()
- Sets the HTTP response code to
401
(Unauthorized
). A companion methodunauthorized(String realm)
is available if you need to specify the authentication realm.
Thus you might store a premade representation of the pigpen in a JSON resource file, in the same package as the Pen
interface.
Now you can set up a Restito mock server to return the pigpen representation when the appropriate endpoint is accessed by a client.
Verifying Calls
In addition to stubbing methods, Restito can act like a spy so that you can verify the correct endpoints were called. You start by retrieving an instance of com.xebialabs.restito.builder.verify.VerifyHttp
by calling the static factory method VerifyHttp.verifyHttp(StubServer stubServer)
with the stub server you just used. Then you verify that certain conditions were met based upon the number of times you expected those conditions to occur. This is accomplished by other methods of VerifyHttp
, some of the most useful of which are shown below. Conditions are specified using the same Condition
types you saw above.
atLeast(int t, Condition... conditions)
- Checks that there certain conditions were met at least a specified number of times.
never(Condition... conditions)
- Checks that certain conditions never happened.
times(int t, Condition... conditions)
- Checks that certain conditions happened an exact number of times. The convenience verification
once(Condition... conditions)
checks that the conditions occurred only once.
Review
Summary
Gotchas
- Don't forget to call
Client.close()
when you are finished using it, or your application will leak resources such as pooled connections. - Don't forget to call
Response.close()
after working with an HTTP request, or your application may leak resources such as open TCP sockets. - Don't forget to register your providers, or custom marshaling won't work.
In the Real World
If you use the base JAX-RS client library, you may find that yourself duplicating code in different calls to REST endpoints. It might be useful to encapsulate JAX-RS client calls in some class related to each endpoint. It turns out that, if care is taken, you can implement the same interface you defined on the server for the endpoint, yet on the client. For example you could implement the same PensResource
interface you created to define the endpoint for pens as a XXXResourceTarget
on the client.
PensResource
- Defines the RESTful endpoint in general.
PensResourceService
- The server-side implementation of the
pens
endpoint. PensResourceTarget
- The client-side implementation of the
pens
endpoint.
Think About It
- Does the spy capability of Restito bring additional value to testing, if it is already verified that the correct values are being returned?
Self Evaluation
- Why are instances of the JAX-RS interface
Client
considered heavy-weight? How does this affect their usage? - List some of the ways in which you can configure a
Client
. - How can you bypass working directly with a
Response
object when making RESTful requests using JAX-RS? - What is the purpose of the Restito library?
Task
Update your Booker command-line client application to fully support accessing library information from a remote computer using the RESTful API you designed and already implemented on the server.
- Crate a
RestPublicationRepository
implementation ofPublicationRepository
using the JAX-RS client library. - In your SURF configuration, for the library
location
support the a URL that indicates the base URI of your RESTful API. Your dependency wiring will automatically choseRestPublicationRepository
when this property's value is set to aurf-Iri
indicating a usable URL.
See Also
- Accessing REST Resources with the JAX-RS Client API (Oracle - The Java EE Tutorial)
- RESTful Java with JAX-RS 2.0, Second Edition (Bill Burke - O'Reilly, 2013)
References
Resources
- Java API for RESTful Services (JAX-RS) (Java.net)
- RESTEasy (JBoss)
- RESTEasy Documentation (JBoss)
- Jackson (GitHub)
- Jackson JSON Processor Wiki
- Restito
Acknowledgments
- Some symbols are from Font Awesome by Dave Gandy.