1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.util;
23
24 import static org.opencastproject.util.Jsons.obj;
25 import static org.opencastproject.util.Jsons.p;
26 import static org.opencastproject.util.data.Monadics.mlist;
27 import static org.opencastproject.util.data.Option.option;
28 import static org.opencastproject.util.data.Tuple.tuple;
29 import static org.opencastproject.util.data.functions.Strings.split;
30 import static org.opencastproject.util.data.functions.Strings.trimToNil;
31
32 import org.opencastproject.job.api.JaxbJob;
33 import org.opencastproject.job.api.Job;
34 import org.opencastproject.rest.ErrorCodeException;
35 import org.opencastproject.rest.RestConstants;
36 import org.opencastproject.systems.OpencastConstants;
37 import org.opencastproject.util.Jsons.Obj;
38 import org.opencastproject.util.data.Function;
39 import org.opencastproject.util.data.Monadics;
40 import org.opencastproject.util.data.Option;
41 import org.opencastproject.util.data.Tuple;
42
43 import org.apache.commons.lang3.StringUtils;
44 import org.osgi.service.component.ComponentContext;
45
46 import java.io.File;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.net.URI;
50 import java.util.regex.Pattern;
51
52 import javax.ws.rs.core.MediaType;
53 import javax.ws.rs.core.Response;
54
55
56 public final class RestUtil {
57
58 private RestUtil() {
59 }
60
61
62
63
64
65
66
67
68
69
70
71 public static Tuple<String, String> getEndpointUrl(ComponentContext cc) {
72 return getEndpointUrl(cc, OpencastConstants.SERVER_URL_PROPERTY, RestConstants.SERVICE_PATH_PROPERTY);
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public static Tuple<String, String> getEndpointUrl(ComponentContext cc, String serverUrlKey, String servicePathKey) {
90 final String serverUrl = option(cc.getBundleContext().getProperty(serverUrlKey)).getOrElse(
91 UrlSupport.DEFAULT_BASE_URL);
92 final String servicePath = option((String) cc.getProperties().get(servicePathKey)).getOrElse(
93 Option.<String> error(RestConstants.SERVICE_PATH_PROPERTY + " property not configured"));
94 return tuple(serverUrl, servicePath);
95 }
96
97
98 public static Response.ResponseBuilder fileResponse(File f, String contentType, Option<String> fileName) {
99 final Response.ResponseBuilder b = Response.ok(f).header("Content-Type", contentType)
100 .header("Content-Length", f.length());
101 for (String fn : fileName)
102 b.header("Content-Disposition", "attachment; filename=" + fn);
103 return b;
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public static Response.ResponseBuilder partialFileResponse(File f, String contentType, Option<String> fileName,
122 String rangeHeader) throws IOException {
123
124 String rangeValue = rangeHeader.trim().substring("bytes=".length());
125 long fileLength = f.length();
126 long start;
127 long end;
128 if (rangeValue.startsWith("-")) {
129 end = fileLength - 1;
130 start = fileLength - 1 - Long.parseLong(rangeValue.substring("-".length()));
131 } else {
132 String[] range = rangeValue.split("-");
133 start = Long.parseLong(range[0]);
134 end = range.length > 1 ? Long.parseLong(range[1]) : fileLength - 1;
135 }
136 if (end > fileLength - 1) {
137 end = fileLength - 1;
138 }
139
140
141 Response.ResponseBuilder response = Response.status(206);
142
143 if (start <= end) {
144 long contentLength = end - start + 1;
145 response.header("Accept-Ranges", "bytes");
146 response.header("Connection", "Close");
147 response.header("Content-Length", contentLength + "");
148 response.header("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
149 response.header("Content-Type", contentType);
150 response.entity(new ChunkedFileInputStream(f, start, end));
151 }
152
153 return response;
154 }
155
156
157
158
159
160
161
162
163 @Deprecated
164 public static Response.ResponseBuilder streamResponse(InputStream in, String contentType, Option<Long> streamLength,
165 Option<String> fileName) {
166 final Response.ResponseBuilder b = Response.ok(in).header("Content-Type", contentType);
167 for (Long l : streamLength)
168 b.header("Content-Length", l);
169 for (String fn : fileName)
170 b.header("Content-Disposition", "attachment; filename=" + fn);
171 return b;
172 }
173
174
175
176
177
178
179 @Deprecated
180 public static MediaType getResponseFormat(String format) {
181 return "json".equalsIgnoreCase(format) ? MediaType.APPLICATION_JSON_TYPE : MediaType.APPLICATION_XML_TYPE;
182 }
183
184
185 public static MediaType getResponseType(String type) {
186 return "json".equalsIgnoreCase(type) ? MediaType.APPLICATION_JSON_TYPE : MediaType.APPLICATION_XML_TYPE;
187 }
188
189 private static final Function<String, String[]> CSV_SPLIT = split(Pattern.compile(","));
190
191
192
193
194
195
196 public static Monadics.ListMonadic<String> splitCommaSeparatedParam(Option<String> param) {
197 for (String p : param)
198 return mlist(CSV_SPLIT.apply(p)).bind(trimToNil);
199 return mlist();
200 }
201
202 public static String generateErrorResponse(ErrorCodeException e) {
203 Obj json = obj(p("error", obj(p("code", e.getErrorCode()), p("message", StringUtils.trimToEmpty(e.getMessage())))));
204 return json.toJson();
205 }
206
207
208 public static final class R {
209 private R() {
210 }
211
212 public static Response ok() {
213 return Response.ok().build();
214 }
215
216 public static Response ok(Object entity) {
217 return Response.ok().entity(entity).build();
218 }
219
220 public static Response ok(boolean entity) {
221 return Response.ok().entity(Boolean.toString(entity)).build();
222 }
223
224 public static Response ok(Jsons.Obj json) {
225 return Response.ok().entity(json.toJson()).type(MediaType.APPLICATION_JSON_TYPE).build();
226 }
227
228 public static Response ok(Job job) {
229 return Response.ok().entity(new JaxbJob(job)).build();
230 }
231
232 public static Response ok(MediaType type, Object entity) {
233 return Response.ok(entity, type).build();
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248 public static Response ok(InputStream in, String contentType, Option<Long> streamLength, Option<String> fileName) {
249 return ok(in, option(contentType), streamLength, fileName);
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264 public static Response ok(InputStream in, Option<String> contentType, Option<Long> streamLength,
265 Option<String> fileName) {
266 final Response.ResponseBuilder b = Response.ok(in);
267 for (String t : contentType)
268 b.header("Content-Type", t);
269 for (Long l : streamLength)
270 b.header("Content-Length", l);
271 for (String fn : fileName)
272 b.header("Content-Disposition", "attachment; filename=" + fn);
273 return b.build();
274 }
275
276 public static Response created(URI location) {
277 return Response.created(location).build();
278 }
279
280 public static Response notFound() {
281 return Response.status(Response.Status.NOT_FOUND).build();
282 }
283
284 public static Response notFound(Object entity) {
285 return Response.status(Response.Status.NOT_FOUND).entity(entity).build();
286 }
287
288 public static Response notFound(Object entity, MediaType type) {
289 return Response.status(Response.Status.NOT_FOUND).entity(entity).type(type).build();
290 }
291
292 public static Response locked() {
293 return Response.status(423).build();
294 }
295
296 public static Response serverError() {
297 return Response.serverError().build();
298 }
299
300 public static Response conflict() {
301 return Response.status(Response.Status.CONFLICT).build();
302 }
303
304 public static Response noContent() {
305 return Response.noContent().build();
306 }
307
308 public static Response badRequest() {
309 return Response.status(Response.Status.BAD_REQUEST).build();
310 }
311
312 public static Response badRequest(String msg) {
313 return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
314 }
315
316 public static Response forbidden() {
317 return Response.status(Response.Status.FORBIDDEN).build();
318 }
319
320 public static Response forbidden(String msg) {
321 return Response.status(Response.Status.FORBIDDEN).entity(msg).build();
322 }
323
324 public static Response unauthorized(Object entity) {
325 return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build();
326 }
327
328 public static Response conflict(String msg) {
329 return Response.status(Response.Status.CONFLICT).entity(msg).build();
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 public static Response preconditionFailed() {
354 return Response.status(Response.Status.PRECONDITION_FAILED).build();
355 }
356
357
358
359
360
361
362
363
364 public static Response preconditionFailed(String message) {
365 return Response.status(Response.Status.PRECONDITION_FAILED).entity(message).build();
366 }
367
368 }
369 }