forked from resteasy/resteasy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RESTEasy_Client_Framework.xml
367 lines (304 loc) · 14.9 KB
/
RESTEasy_Client_Framework.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
<chapter id="RESTEasy_Client_Framework">
<title>Resteasy Client API</title>
<sect1>
<title>JAX-RS 2.0 Client API</title>
<para>JAX-RS 2.0 introduces a new client API so that you can make http requests to your remote RESTful web services.
It is a 'fluent' request building API with really 3 main classes: Client, WebTarget, and Response. The Client
interface is a builder of WebTarget instances. WebTarget represents a distinct URL or URL template from which
you can build more sub-resource WebTargets or invoke requests on.</para>
<para>
There are really two ways to create a Client. Standard way, or you can instantiate Resteasy's core class
directly. The advantage of the latter is that you can work with Resteasy extension interfaces.
</para>
<programlisting>
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://foo.com/resource");
Response response = target.request().get();
String value = response.readEntity(String.class);
response.close(); // You should close connections!
ResteasyClient client = new ResteasyClient();
ResteasyWebTarget target = client.target("http://foo.com/resource");
</programlisting>
<para>
Resteasy will load automatically load a set of default providers. (Basically all classes listed in all
META-INF/services/javax.ws.rs.ext.Providers files). Additionally, you can manually register other providers,
filters, and interceptors through the Configuration object provided by the method call Client.configuration().
Configuration also lets you set various configuration properties that may be needed.
</para>
<para>
Each WebTarget has its own Configuration instance which inherits the components and properties registered with
its parent. This allows you to set specific configuration options per target resource. For example, username
and password.
</para>
</sect1>
<para>
</para>
<sect1>
<title>Resteasy Proxy Framework</title>
<para>
The Resteasy Proxy Framework is the mirror opposite of the JAX-RS server-side specification. Instead of using
JAX-RS annotations to map an incoming request to your RESTFul Web Service method, the client framework builds an
HTTP request that it uses to invoke on a remote RESTful Web Service. This remote service does not have to be a
JAX-RS service and can be any web resource that accepts HTTP requests.
</para>
<para>
Resteasy has a client proxy framework that allows you to use JAX-RS annotations to invoke on a remote HTTP
resource.
The way it works is that you write a Java interface and use JAX-RS annotations on methods and the interface. For
example:
</para>
<para>
<programlisting>
public interface SimpleClient
{
@GET
@Path("basic")
@Produces("text/plain")
String getBasic();
@PUT
@Path("basic")
@Consumes("text/plain")
void putBasic(String body);
@GET
@Path("queryParam")
@Produces("text/plain")
String getQueryParam(@QueryParam("param")String param);
@GET
@Path("matrixParam")
@Produces("text/plain")
String getMatrixParam(@MatrixParam("param")String param);
@GET
@Path("uriParam/{param}")
@Produces("text/plain")
int getUriParam(@PathParam("param")int param);
}</programlisting>
</para>
<para>
Resteasy has a simple API based on Apache HttpClient. You generate a proxy then you can invoke methods on the
proxy. The invoked method gets translated to an HTTP request based on how you annotated the method and posted to
the server. Here's how you would set this up:
</para>
<para>
<programlisting>
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget)target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
client.putBasic("hello world");
</programlisting>
Alternatively you can use the Resteasy client extension interfaces directly:
<programlisting>
ResteasyClient client = new ResteasyClient();
ResteasyWebTarget target = client.target("http://example.com/base/uri");
SimpleClient simple = target.proxy(SimpleClient.class);
client.putBasic("hello world");
</programlisting>
</para>
<para>
@CookieParam works the mirror opposite of its server-side counterpart and creates a cookie header to send to the
server. You do not need to use @CookieParam if you allocate your own javax.ws.rs.core.Cookie object and pass it
as
a parameter to a client proxy method. The client framework understands that you are passing a cookie to the
server
so no extra metadata is needed.
</para>
<para>
The framework also supports the JAX-RS locator pattern, but on the client side. So, if you have a method annotated only with @Path, that proxy method
will return a new proxy of the interface returned by that method.
</para>
<sect1 id="Custom_client-side_responses">
<title>Abstract Responses</title>
<para>
Sometimes you are interested not only in the response body of a client request, but also either the response
code and/or response headers. The Client-Proxy framework has two ways to get at this information
</para>
<para>
</para>
<para>
You may return a javax.ws.rs.core.Response.Status enumeration from your method calls:
</para>
<para>
<programlisting>
@Path("/")
public interface MyProxy {
@POST
Response.Status updateSite(MyPojo pojo);
}
</programlisting>
</para>
<para>
Internally, after invoking on the server, the client proxy internals will convert the HTTP response code into
a
Response.Status enum.
</para>
<para>
</para>
<para>
If you are interested in everything, you can get it with the javax.ws.rs.core.Response class:
</para>
<para>
<programlisting>
@Path("/")
public interface LibraryService {
@GET
@Produces("application/xml")
Response getAllBooks();
}</programlisting>
</para>
</sect1>
<sect1 id="Sharing_interfaces">
<title>Sharing an interface between client and server</title>
<para>
It is generally possible to share an interface between the client and server. In this scenario, you just
have your JAX-RS services implement an annotated interface
and then reuse that same interface to create client proxies to invoke on the client-side.
</para>
</sect1>
</sect1>
<sect1 id="transport_layer">
<title>Apache HTTP Client 4.x and other backends</title>
<para>
Network communication between the client and server is handled in Resteasy,
by default, by HttpClient (4.x) from the Apache HttpComponents project. In general,
the interface between the Resteasy Client Framework and the network is
found in an implementation of
<classname>org.jboss.resteasy.client.jaxrs.ClientHttpEngine</classname>, and
<classname>org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine</classname>,
which uses HttpClient (4.x), is the default implementation. Resteasy
also ships with the following client engines, all found in the
<code>org.jboss.resteasy.client.jaxrs.engines</code> package:
</para>
<itemizedlist>
<listitem>
URLConnectionClientExecutor: uses <classname>java.net.HttpURLConnection</classname>;
</listitem>
<listitem>InMemoryClientExecutor: dispatches requests to a server in the same JVM.</listitem>
</itemizedlist>
<para>
and a client executor may be passed to a specific <classname>ClientRequest</classname>:
</para>
<programlisting>
ResteasyClient client = (ResteasyClient)ClientFactory.newClient();
ClientHttpEngine engine = new ApacheHttpClient4Engine();
client.httpEngine(engine);
</programlisting>
<para>
Resteasy and HttpClient make reasonable default decisions so that it is possible to use the client
framework without ever referencing HttpClient, but for some applications it may be necessary to drill
down into the HttpClient details. <classname>ApacheHttpClient4Engine</classname> can
be supplied with an instance of <classname>org.apache.http.client.HttpClient</classname> and
an instance of <classname>org.apache.http.protocol.HttpContext</classname>, which can carry
additional configuration details into the HttpClient layer. For example, authentication
may be configured as follows:
</para>
<programlisting>
// Configure HttpClient to authenticate preemptively
// by prepopulating the authentication data cache.
// 1. Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// 2. Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put("com.bluemonkeydiamond.sippycups", basicAuth);
// 3. Add AuthCache to the execution context
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
// 4. Create client executor and proxy
httpClient = new DefaultHttpClient();
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient, localContext);
ResteasyClient client = (ResteasyClient)ClientFactory.newClient();
client.httpEngine(engine);
</programlisting>
<para>
One default decision made by HttpClient and adopted by Resteasy is the use of
<classname>org.apache.http.impl.conn.SingleClientConnManager</classname>,
which manages a single socket at any given time and which
supports the use case in which one or more invocations are made serially
from a single thread. For multithreaded applications,
<classname>SingleClientConnManager</classname> may be replaced by
<classname>org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager</classname>:
</para>
<programlisting>
ClientConnectionManager cm = new ThreadSafeClientConnManager();
HttpClient httpClient = new DefaultHttpClient(cm);
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
</programlisting>
<para>
For more information about HttpClient (4.x), see the documentation
at <ulink url="http://hc.apache.org/httpcomponents-client-ga/tutorial/html/">
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/</ulink>.
</para>
<para>
<emphasis role="bold">Note.</emphasis> It is important to understand
the difference between "releasing" a connection and "closing" a
connection. <emphasis role="bold">Releasing</emphasis> a connection
makes it available for reuse. <emphasis role="bold">Closing</emphasis>
a connection frees its resources and makes it unusable.
</para>
<para>
<classname>SingleClientConnManager</classname> manages
a single socket, which it allocates to at most a single invocation
at any given time. Before that socket can be reused, it has to be
released from its current use, which can occur in one of two ways. If
an execution of a request or a call on
a proxy returns a class other than <classname>Response</classname>,
then Resteasy will take care of releasing the connection. For example,
in the fragments
</para>
<programlisting>
WebTarget target = client.target("http://localhost:8081/customer/123");
String answer = target.request().get(String.class);
</programlisting>
<para>
or
</para>
<programlisting>
ResteasyWebTarget target = client.target("http://localhost:8081/customer/123");
RegistryStats stats = target.proxy(RegistryStats.class);
RegistryData data = stats.get();
</programlisting>
<para>
Resteasy will release the connection under the covers. The only counterexample is the case
in which the response is an instance of <classname>InputStream</classname>, which must
be closed explicitly.
</para>
<para>
On the other hand, if the result of an invocation is an instance of
<classname>Response</classname>, then Response.close() method must be used to released the connection.
</para>
<programlisting>
WebTarget target = client.target("http://localhost:8081/customer/123");
Response response = target.request().get();
System.out.println(response.getStatus());
response.close();
</programlisting>
<para>
You should probably execute this in a try/finally block. Again, releasing a connection only makes it available
for another use. <emphasis role="bold">It does not normally close the socket.</emphasis>
</para>
<para>
On the other hand,
<methodname>ApacheHttpClient4Engine.finalize()</methodname> will close any open
sockets, but only if it created the <classname>HttpClient</classname> it has been
using. If an <classname>HttpClient</classname> has been passed into the
<classname>ApacheHttpClient4Executor</classname>, then the user is responsible
for closing the connections:
</para>
<programlisting>
HttpClient httpClient = new DefaultHttpClient();
ApacheHttpClient4Engine executor = new ApacheHttpClient4Engine(httpClient);
...
httpClient.getConnectionManager().shutdown();
</programlisting>
<para>
Note that if <classname>ApacheHttpClient4Engine</classname> has created its own
instance of <classname>HttpClient</classname>, it is not necessary to wait
for <methodname>finalize()</methodname> to close open sockets. The
<classname>ClientHttpEngine</classname> interface has a <methodname>close()</methodname>
method for this purpose.
</para>
<para>
Finally, if your javax.ws.rs.client.Client class has created the engine automatically for you, you should
call Client.close() and this will clean up any socket connections.
</para>
</sect1>
</chapter>