1
2
3
4
5
6
7 package ch.colabproject.colab.generator.plugin.rest;
8
9 import ch.colabproject.colab.generator.model.exceptions.HttpException;
10 import jakarta.json.bind.Jsonb;
11 import jakarta.json.bind.JsonbBuilder;
12 import jakarta.json.bind.JsonbException;
13 import jakarta.ws.rs.client.*;
14 import jakarta.ws.rs.core.*;
15 import jakarta.ws.rs.core.Response.Status;
16 import org.glassfish.jersey.media.multipart.FormDataMultiPart;
17 import org.glassfish.jersey.media.multipart.MultiPartFeature;
18 import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map;
27
28
29
30
31
32
33 public class RestClient {
34
35
36
37
38 private WebTarget webTarget;
39
40
41
42
43 private Client client;
44
45
46
47
48 private final Jsonb jsonb;
49
50
51
52
53
54
55
56
57
58 public RestClient(String baseUri, String cookieName, Jsonb jsonb, Object... clientFeatures) {
59 ClientBuilder builder = ClientBuilder.newBuilder();
60
61 builder.register(MultiPartFeature.class);
62
63 for (Object feature : clientFeatures) {
64 builder.register(feature);
65 }
66
67 if (cookieName != null && !cookieName.isBlank()) {
68 builder.register(new CookieFilter(cookieName));
69 }
70
71 this.client = builder.build();
72 this.webTarget = client.target(baseUri);
73
74 if (jsonb != null) {
75 this.jsonb = jsonb;
76 } else {
77 this.jsonb = JsonbBuilder.create();
78 }
79 }
80
81
82
83
84 public void close() {
85 client.close();
86 }
87
88
89
90
91
92
93
94
95
96
97 private <T> T readEntity(InputStream stream, GenericType<T> type) {
98 try {
99 if (stream != null && stream.available() > 0) {
100 return jsonb.fromJson(stream, type.getType());
101 } else {
102 return null;
103 }
104 } catch (JsonbException | IOException ex) {
105
106 return null;
107 }
108 }
109
110
111
112
113
114
115
116
117 private String readTextualEntity(InputStream stream) {
118 try {
119 return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
120 } catch (IOException ex) {
121
122 return null;
123 }
124 }
125
126 private <T> T processResponse(Response response, GenericType<T> type) {
127 Status.Family family = Status.Family.familyOf(response.getStatus());
128 if (family == Status.Family.SUCCESSFUL) {
129 if (response.getStatus() == 204) {
130 return null;
131 } else {
132 if (Response.class.isAssignableFrom(type.getRawType())) {
133 return (T) response;
134 }
135 Object entity = response.getEntity();
136 if (entity instanceof InputStream) {
137 InputStream stream = (InputStream) entity;
138 String contentType = response.getHeaderString("Content-Type");
139 if (contentType.startsWith("application/json")
140 || contentType.startsWith("text/json")) {
141 return readEntity(stream, type);
142 } else if (contentType.startsWith("text/plain")
143 || contentType.startsWith("text/html")) {
144 if (type.getRawType() == String.class) {
145 return (T) readTextualEntity(stream);
146 } else {
147 return null;
148 }
149 } else {
150 return null;
151 }
152 } else {
153 return null;
154 }
155 }
156 } else if (family == Status.Family.CLIENT_ERROR) {
157 HttpException error = readEntity((InputStream) response.getEntity(),
158 new GenericType<>(HttpException.class));
159 if (error != null) {
160 throw error;
161 } else {
162 throw new ClientException(Status.fromStatusCode(response.getStatus()));
163 }
164 } else if (family == Status.Family.SERVER_ERROR) {
165 throw new ServerException(Status.fromStatusCode(response.getStatus()));
166 } else {
167 throw new ServerException(Status.fromStatusCode(response.getStatus()));
168 }
169 }
170
171
172
173
174
175
176
177
178 private FormDataMultiPart getFormData(Map<String, FormField> fields) {
179 FormDataMultiPart multipart = new FormDataMultiPart();
180
181 fields.forEach((fieldName, data) -> {
182 MediaType mimeType = data.getMimeType();
183 if (data.getData() instanceof File) {
184 FileDataBodyPart filePart;
185 if (mimeType != null) {
186 filePart = new FileDataBodyPart(fieldName, (File) data.getData(), mimeType);
187 } else {
188 filePart = new FileDataBodyPart(fieldName, (File) data.getData());
189 }
190 multipart.bodyPart(filePart);
191 } else {
192 if (mimeType != null) {
193 multipart.field(fieldName, data.getData(), mimeType);
194 } else {
195 multipart.field(fieldName, data.getData().toString());
196 }
197 }
198 });
199 return multipart;
200 }
201
202
203
204
205
206
207
208
209
210
211
212 public <T> T get(String path, GenericType<T> type, String... accept) {
213 return processResponse(webTarget.path(path).request()
214 .accept(accept).get(),
215 type);
216 }
217
218
219
220
221
222
223
224
225
226
227
228 public <T> T delete(String path, GenericType<T> type, String... accept) {
229 return processResponse(webTarget.path(path).request()
230 .accept(accept).delete(),
231 type);
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 public <T> T post(String path, Object body, GenericType<T> type, String... accept) {
246 return processResponse(webTarget.path(path).request()
247 .accept(accept)
248 .post(Entity.entity(body, MediaType.APPLICATION_JSON_TYPE)),
249 type);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263 public <T> T post(
264 String path,
265 Map<String, FormField> fields,
266 GenericType<T> type,
267 String... accept
268 ) {
269 FormDataMultiPart multipart = getFormData(fields);
270
271 return processResponse(webTarget.path(path).request()
272 .accept(accept)
273 .post(Entity.entity(multipart, multipart.getMediaType())),
274 type);
275 }
276
277
278
279
280
281
282
283
284
285
286
287 public <T> T post(String path, GenericType<T> type, String... accept) {
288 return this.post(path, "", type, accept);
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302 public <T> T put(String path, Object body, GenericType<T> type, String... accept) {
303 return processResponse(webTarget.path(path).request()
304 .accept(accept)
305 .put(Entity.entity(body, MediaType.APPLICATION_JSON_TYPE)),
306 type);
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320 public <T> T put(String path, Map<String, FormField> fields, GenericType<T> type,
321 String... accept) {
322 FormDataMultiPart multipart = getFormData(fields);
323
324 return processResponse(webTarget.path(path).request()
325 .accept(accept)
326 .put(Entity.entity(multipart, multipart.getMediaType())),
327 type);
328 }
329
330
331
332
333
334
335
336
337
338
339
340 public <T> T put(String path, GenericType<T> type, String... accept) {
341 return this.put(path, "", type, accept);
342
343 }
344
345
346
347
348
349 public static class CookieFilter implements ClientRequestFilter, ClientResponseFilter {
350
351
352
353
354 private String sessionId;
355
356
357
358
359 private final String cookieName;
360
361
362
363
364
365
366 public CookieFilter(String cookieName) {
367 this.cookieName = cookieName;
368 this.sessionId = null;
369 }
370
371
372
373
374
375
376 @Override
377 public void filter(ClientRequestContext requestContext) throws IOException {
378 if (this.sessionId != null) {
379 List<Object> cookies = new ArrayList<>();
380 Cookie cookie = new Cookie.Builder(cookieName)
381 .value(sessionId)
382 .build();
383 cookies.add(cookie);
384 requestContext.getHeaders().put("Cookie", cookies);
385 }
386 }
387
388
389
390
391
392
393 @Override
394 public void filter(ClientRequestContext requestContext,
395 ClientResponseContext responseContext) throws IOException {
396 NewCookie get = responseContext.getCookies().get(cookieName);
397 if (get != null) {
398 if (get.getMaxAge() == 0) {
399 this.sessionId = null;
400 } else {
401 this.sessionId = get.getValue();
402 }
403 }
404 }
405
406 }
407 }