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.annotation.impl;
23
24 import static javax.servlet.http.HttpServletResponse.SC_CREATED;
25 import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
26 import static javax.servlet.http.HttpServletResponse.SC_OK;
27
28 import org.opencastproject.annotation.api.Annotation;
29 import org.opencastproject.annotation.api.AnnotationService;
30 import org.opencastproject.rest.RestConstants;
31 import org.opencastproject.util.NotFoundException;
32 import org.opencastproject.util.UrlSupport;
33 import org.opencastproject.util.doc.rest.RestParameter;
34 import org.opencastproject.util.doc.rest.RestParameter.Type;
35 import org.opencastproject.util.doc.rest.RestQuery;
36 import org.opencastproject.util.doc.rest.RestResponse;
37 import org.opencastproject.util.doc.rest.RestService;
38
39 import org.apache.commons.lang3.StringUtils;
40 import org.osgi.service.component.ComponentContext;
41 import org.osgi.service.component.annotations.Activate;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Reference;
44 import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import java.net.URI;
49 import java.net.URISyntaxException;
50
51 import javax.servlet.http.HttpServletRequest;
52 import javax.ws.rs.DELETE;
53 import javax.ws.rs.FormParam;
54 import javax.ws.rs.GET;
55 import javax.ws.rs.PUT;
56 import javax.ws.rs.Path;
57 import javax.ws.rs.PathParam;
58 import javax.ws.rs.Produces;
59 import javax.ws.rs.QueryParam;
60 import javax.ws.rs.WebApplicationException;
61 import javax.ws.rs.core.Context;
62 import javax.ws.rs.core.MediaType;
63 import javax.ws.rs.core.Response;
64 import javax.ws.rs.core.Response.Status;
65
66
67
68
69 @Path("/annotation")
70 @RestService(name = "annotation", title = "Annotation Service",
71 abstractText = "This service is used for managing user generated annotations.",
72 notes = {
73 "<strong>Deprecated:</strong> "
74 + "<em>This module is deprecated. It may be removed at any time. Planned removal is the Opencast release in "
75 + "December 2017. Please do not use it for new development.</em>",
76 "All paths above are relative to the REST endpoint base (something like http://your.server/files)",
77 "If the service is down or not working it will return a status 503, this means the the underlying service is "
78 + "not working and is either restarting or has failed",
79 "A status code 500 means a general failure has occurred which is not recoverable and was not anticipated. In "
80 + "other words, there is a bug! You should file an error report with your server logs from the time when the "
81 + "error occurred: <a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>" })
82 @Component(
83 immediate = true,
84 service = AnnotationRestService.class,
85 property = {
86 "service.description=Annotation REST Endpoint",
87 "opencast.service.type=org.opencastproject.annotation",
88 "opencast.service.path=/annotation"
89 }
90 )
91 @JaxrsResource
92 public class AnnotationRestService {
93
94
95 private static final Logger logger = LoggerFactory.getLogger(AnnotationRestService.class);
96
97
98 private AnnotationService annotationService;
99
100
101 protected String serverUrl = UrlSupport.DEFAULT_BASE_URL;
102
103
104
105 protected String serviceUrl = "/annotation";
106
107
108
109
110
111
112
113 @Reference
114 public void setService(AnnotationService service) {
115 this.annotationService = service;
116 }
117
118
119
120
121
122
123
124 @Activate
125 public void activate(ComponentContext cc) {
126
127 if (cc == null) {
128 serverUrl = UrlSupport.DEFAULT_BASE_URL;
129 } else {
130 String ccServerUrl = cc.getBundleContext().getProperty("org.opencastproject.server.url");
131 logger.info("configured server url is {}", ccServerUrl);
132 if (ccServerUrl == null) {
133 serverUrl = UrlSupport.DEFAULT_BASE_URL;
134 } else {
135 serverUrl = ccServerUrl;
136 }
137 serviceUrl = (String) cc.getProperties().get(RestConstants.SERVICE_PATH_PROPERTY);
138 }
139 }
140
141
142
143
144 @GET
145 @Produces(MediaType.TEXT_XML)
146 @Path("annotations.xml")
147 @RestQuery(
148 name = "annotationsasxml",
149 description = "Get annotations by key and day",
150 returnDescription = "The user annotations.",
151 restParameters = {
152 @RestParameter(
153 name = "episode",
154 description = "The episode identifier",
155 isRequired = false,
156 type = Type.STRING
157 ),
158 @RestParameter(
159 name = "type",
160 description = "The type of annotation",
161 isRequired = false,
162 type = Type.STRING
163 ),
164 @RestParameter(
165 name = "day",
166 description = "The day of creation (format: YYYYMMDD)",
167 isRequired = false,
168 type = Type.STRING
169 ),
170 @RestParameter(
171 name = "limit",
172 description = "The maximum number of items to return per page",
173 isRequired = false,
174 type = Type.INTEGER
175 ),
176 @RestParameter(
177 name = "offset",
178 description = "The page number",
179 isRequired = false,
180 type = Type.INTEGER
181 ),
182 },
183 responses = {
184 @RestResponse(responseCode = SC_OK, description = "An XML representation of the user annotations"),
185 }
186 )
187 public Response getAnnotationsAsXml(@QueryParam("episode") String id, @QueryParam("type") String type,
188 @QueryParam("day") String day, @QueryParam("limit") int limit, @QueryParam("offset") int offset) {
189
190
191 if (offset < 0 || limit < 0) {
192 return Response.status(Status.BAD_REQUEST).build();
193 }
194
195
196 if (limit == 0) {
197 limit = 10;
198 }
199
200 if (!StringUtils.isEmpty(id) && !StringUtils.isEmpty(type)) {
201 return Response.ok(annotationService.getAnnotationsByTypeAndMediapackageId(type, id, offset, limit)).build();
202 } else if (!StringUtils.isEmpty(id)) {
203 return Response.ok(annotationService.getAnnotationsByMediapackageId(id, offset, limit)).build();
204 } else if (!StringUtils.isEmpty(type) && !StringUtils.isEmpty(day)) {
205 return Response.ok(annotationService.getAnnotationsByTypeAndDay(type, day, offset, limit)).build();
206 } else if (!StringUtils.isEmpty(type)) {
207 return Response.ok(annotationService.getAnnotationsByType(type, offset, limit)).build();
208 } else if (!StringUtils.isEmpty(day)) {
209 return Response.ok(annotationService.getAnnotationsByDay(day, offset, limit)).build();
210 } else {
211 return Response.ok(annotationService.getAnnotations(offset, limit)).build();
212 }
213 }
214
215
216
217
218 @GET
219 @Produces(MediaType.APPLICATION_JSON)
220 @Path("annotations.json")
221 @RestQuery(
222 name = "annotationsasjson",
223 description = "Get annotations by key and day",
224 returnDescription = "The user annotations.",
225 restParameters = {
226 @RestParameter(
227 name = "episode",
228 description = "The episode identifier",
229 isRequired = false,
230 type = Type.STRING
231 ),
232 @RestParameter(
233 name = "type",
234 description = "The type of annotation",
235 isRequired = false,
236 type = Type.STRING
237 ),
238 @RestParameter(
239 name = "day",
240 description = "The day of creation (format: YYYYMMDD)",
241 isRequired = false,
242 type = Type.STRING
243 ),
244 @RestParameter(
245 name = "limit",
246 description = "The maximum number of items to return per page",
247 isRequired = false,
248 type = Type.INTEGER
249 ),
250 @RestParameter(
251 name = "offset",
252 description = "The page number",
253 isRequired = false,
254 type = Type.INTEGER
255 ),
256 },
257 responses = {
258 @RestResponse(responseCode = SC_OK, description = "A JSON representation of the user annotations"),
259 }
260 )
261 public Response getAnnotationsAsJson(
262 @QueryParam("episode") String id,
263 @QueryParam("type") String type,
264 @QueryParam("day") String day,
265 @QueryParam("limit") int limit,
266 @QueryParam("offset") int offset
267 ) {
268 return getAnnotationsAsXml(id, type, day, limit, offset);
269 }
270
271 @PUT
272 @Path("")
273 @Produces(MediaType.TEXT_XML)
274 @RestQuery(
275 name = "add",
276 description = "Add an annotation on an episode",
277 returnDescription = "The user annotation.",
278 restParameters = {
279 @RestParameter(
280 name = "episode",
281 description = "The episode identifier",
282 isRequired = true,
283 type = Type.STRING
284 ),
285 @RestParameter(
286 name = "type",
287 description = "The type of annotation",
288 isRequired = true,
289 type = Type.STRING
290 ),
291 @RestParameter(
292 name = "value",
293 description = "The value of the annotation",
294 isRequired = true,
295 type = Type.TEXT
296 ),
297 @RestParameter(
298 name = "in",
299 description = "The time, or inpoint, of the annotation",
300 isRequired = true,
301 type = Type.STRING
302 ),
303 @RestParameter(
304 name = "out",
305 description = "The optional outpoint of the annotation",
306 isRequired = false,
307 type = Type.STRING
308 ),
309 @RestParameter(
310 name = "isPrivate",
311 description = "True if the annotation is private",
312 isRequired = false,
313 type = Type.BOOLEAN
314 ),
315 },
316 responses = {
317 @RestResponse(
318 responseCode = SC_CREATED,
319 description = "The URL to this annotation is returned in the Location header, and an "
320 + "XML representation of the annotation itelf is returned in the response body."
321 ),
322 }
323 )
324 public Response add(
325 @FormParam("episode") String mediapackageId,
326 @FormParam("in") int inpoint,
327 @FormParam("out") int outpoint,
328 @FormParam("type") String type,
329 @FormParam("value") String value,
330 @FormParam("isPrivate") boolean isPrivate,
331 @Context HttpServletRequest request
332 ) {
333 String sessionId = request.getSession().getId();
334 Annotation a = new AnnotationImpl();
335 a.setMediapackageId(mediapackageId);
336 a.setSessionId(sessionId);
337 a.setInpoint(inpoint);
338 a.setOutpoint(outpoint);
339 a.setType(type);
340 a.setValue(value);
341 a.setPrivate(isPrivate);
342 a = annotationService.addAnnotation(a);
343 URI uri;
344 try {
345 uri = new URI(
346 UrlSupport.concat(new String[] { serverUrl, serviceUrl, Long.toString(a.getAnnotationId()), ".xml" }));
347 } catch (URISyntaxException e) {
348 throw new WebApplicationException(e);
349 }
350 return Response.created(uri).entity(a).build();
351 }
352
353 @PUT
354 @Path("{id}")
355 @Produces(MediaType.TEXT_XML)
356 @RestQuery(
357 name = "change",
358 description = "Changes the value of an annotation specified by its identifier ",
359 returnDescription = "The user annotation.",
360 pathParameters = {
361 @RestParameter(
362 name = "id",
363 description = "The annotation identifier",
364 isRequired = true,
365 type = Type.STRING
366 ),
367 },
368 restParameters = {
369 @RestParameter(
370 name = "value",
371 description = "The value of the annotation",
372 isRequired = true,
373 type = Type.TEXT
374 ),
375 },
376 responses = {
377 @RestResponse(
378 responseCode = SC_CREATED,
379 description = "The URL to this annotation is returned in the Location header, and an "
380 + "XML representation of the annotation itelf is returned in the response body."
381 ),
382 }
383 )
384 public Response change(
385 @PathParam("id") String idAsString,
386 @FormParam("value") String value,
387 @Context HttpServletRequest request
388 ) throws NotFoundException {
389 Long id = null;
390 try {
391 id = Long.parseLong(idAsString);
392 } catch (NumberFormatException e) {
393 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
394 }
395 Annotation a = annotationService.getAnnotation(id);
396 a.setValue(value);
397 a = annotationService.changeAnnotation(a);
398 URI uri;
399 try {
400 uri = new URI(
401 UrlSupport.concat(new String[] { serverUrl, serviceUrl, Long.toString(a.getAnnotationId()), ".xml" }));
402 } catch (URISyntaxException e) {
403 throw new WebApplicationException(e);
404 }
405 return Response.created(uri).entity(a).build();
406 }
407
408 @GET
409 @Produces(MediaType.TEXT_XML)
410 @Path("{id}.xml")
411 @RestQuery(
412 name = "annotationasxml",
413 description = "Gets an annotation by its identifier",
414 returnDescription = "An XML representation of the user annotation.",
415 pathParameters = {
416 @RestParameter(name = "id", description = "The episode identifier", isRequired = false, type = Type.STRING),
417 },
418 responses = {
419 @RestResponse(responseCode = SC_OK, description = "An XML representation of the user annotation"),
420 }
421 )
422 public AnnotationImpl getAnnotationAsXml(@PathParam("id") String idAsString) throws NotFoundException {
423 Long id = null;
424 try {
425 id = Long.parseLong(idAsString);
426 } catch (NumberFormatException e) {
427 throw new WebApplicationException(e, Status.BAD_REQUEST);
428 }
429 return (AnnotationImpl) annotationService.getAnnotation(id);
430 }
431
432 @GET
433 @Produces(MediaType.APPLICATION_JSON)
434 @Path("{id}.json")
435 @RestQuery(
436 name = "annotationasjson",
437 description = "Gets an annotation by its identifier",
438 returnDescription = "A JSON representation of the user annotation.",
439 pathParameters = {
440 @RestParameter(name = "id", description = "The episode identifier", isRequired = false, type = Type.STRING),
441 },
442 responses = {
443 @RestResponse(responseCode = SC_OK, description = "A JSON representation of the user annotation"),
444 }
445 )
446 public AnnotationImpl getAnnotationAsJson(@PathParam("id") String idAsString) throws NotFoundException {
447 return getAnnotationAsXml(idAsString);
448 }
449
450 @DELETE
451 @Path("{id}")
452 @RestQuery(
453 name = "remove",
454 description = "Remove an annotation",
455 returnDescription = "Return status code",
456 pathParameters = {
457 @RestParameter(
458 name = "id",
459 description = "The annotation identifier",
460 isRequired = false,
461 type = Type.STRING
462 ),
463 },
464 responses = {
465 @RestResponse(responseCode = SC_OK, description = "Annotation deleted."),
466 @RestResponse(responseCode = SC_NO_CONTENT, description = "Annotation not found."),
467 }
468 )
469 public Response removeAnnotation(@PathParam("id") String idAsString) throws NotFoundException {
470 Long id = null;
471 Annotation a;
472 try {
473 id = Long.parseLong(idAsString);
474 } catch (NumberFormatException e) {
475 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
476 }
477 a = (AnnotationImpl) annotationService.getAnnotation(id);
478 boolean removed = annotationService.removeAnnotation(a);
479 if (removed) {
480 return Response.status(Status.OK).build();
481 }
482 else {
483 return Response.status(Status.NO_CONTENT).build();
484 }
485 }
486
487 }