1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.distribution.aws.s3.endpoint;
22
23 import static javax.servlet.http.HttpServletResponse.SC_OK;
24 import static javax.ws.rs.core.Response.status;
25
26 import org.opencastproject.distribution.aws.s3.api.AwsS3DistributionService;
27 import org.opencastproject.job.api.JaxbJob;
28 import org.opencastproject.job.api.Job;
29 import org.opencastproject.job.api.JobProducer;
30 import org.opencastproject.mediapackage.MediaPackage;
31 import org.opencastproject.mediapackage.MediaPackageElement;
32 import org.opencastproject.mediapackage.MediaPackageElementParser;
33 import org.opencastproject.mediapackage.MediaPackageParser;
34 import org.opencastproject.rest.AbstractJobProducerEndpoint;
35 import org.opencastproject.serviceregistry.api.ServiceRegistry;
36 import org.opencastproject.util.doc.rest.RestParameter;
37 import org.opencastproject.util.doc.rest.RestParameter.Type;
38 import org.opencastproject.util.doc.rest.RestQuery;
39 import org.opencastproject.util.doc.rest.RestResponse;
40 import org.opencastproject.util.doc.rest.RestService;
41
42 import com.google.gson.Gson;
43 import com.google.gson.reflect.TypeToken;
44
45 import org.osgi.service.component.ComponentContext;
46 import org.osgi.service.component.annotations.Activate;
47 import org.osgi.service.component.annotations.Component;
48 import org.osgi.service.component.annotations.Reference;
49 import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import java.util.List;
54 import java.util.Set;
55
56 import javax.ws.rs.DefaultValue;
57 import javax.ws.rs.FormParam;
58 import javax.ws.rs.POST;
59 import javax.ws.rs.Path;
60 import javax.ws.rs.Produces;
61 import javax.ws.rs.core.MediaType;
62 import javax.ws.rs.core.Response;
63 import javax.ws.rs.core.Response.Status;
64
65
66
67
68 @Path("/distribution/s3")
69 @RestService(
70 name = "awss3distributionservice",
71 title = "AWS S3 Distribution Service",
72 abstractText = "This service distributes media package elements to AWS S3.",
73 notes = {
74 "All paths above are relative to the REST endpoint base (something like http://your.server/files)",
75 "If the service is down or not working it will return a status 503, this means the the "
76 + "underlying service is not working and is either restarting or has failed",
77 "A status code 500 means a general failure has occurred which is not recoverable and was "
78 + "not anticipated. In other words, there is a bug! You should file an error report "
79 + "with your server logs from the time when the error occurred: "
80 + "<a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>"
81 }
82 )
83 @Component(
84 immediate = true,
85 service = AwsS3DistributionRestService.class,
86 property = {
87 "service.description=AWS S3 Distribution REST Endpoint",
88 "opencast.service.type=org.opencastproject.distribution.aws.s3",
89 "opencast.service.path=/distribution/s3",
90 "opencast.service.jobproducer=true"
91 }
92 )
93 @JaxrsResource
94 public class AwsS3DistributionRestService extends AbstractJobProducerEndpoint {
95
96
97 private static final Logger logger = LoggerFactory.getLogger(AwsS3DistributionRestService.class);
98
99
100 protected AwsS3DistributionService service;
101
102
103 protected ServiceRegistry serviceRegistry = null;
104
105
106
107
108
109
110
111 @Activate
112 public void activate(ComponentContext cc) {
113 }
114
115
116
117
118
119
120
121 @Reference
122 protected void setServiceRegistry(ServiceRegistry serviceRegistry) {
123 this.serviceRegistry = serviceRegistry;
124 }
125
126
127
128
129
130 @Reference(target = "(distribution.channel=aws.s3)")
131 public void setService(AwsS3DistributionService service) {
132 this.service = service;
133 }
134
135 @POST
136 @Path("/")
137 @Produces(MediaType.TEXT_XML)
138 @RestQuery(
139 name = "distribute",
140 description = "Distribute a media package element to this distribution channel",
141 returnDescription = "The job that can be used to track the distribution",
142 restParameters = {
143 @RestParameter(
144 name = "mediapackage",
145 isRequired = true,
146 description = "The mediapackage",
147 type = Type.TEXT
148 ),
149 @RestParameter(
150 name = "channelId",
151 isRequired = true,
152 description = "The publication channel ID",
153 type = Type.TEXT
154 ),
155 @RestParameter(
156 name = "elementId",
157 isRequired = true,
158 description = "The element to distribute",
159 type = Type.STRING
160 ),
161 @RestParameter(
162 name = "checkAvailability",
163 isRequired = false,
164 description = "If the service should try to access the distributed element",
165 type = Type.BOOLEAN
166 )
167 },
168 responses = {
169 @RestResponse(responseCode = SC_OK, description = "An XML representation of the distribution job")
170 }
171 )
172 public Response distribute(
173 @FormParam("mediapackage") String mediaPackageXml,
174 @FormParam("channelId") String channelId,
175 @FormParam("elementId") String elementId,
176 @FormParam("checkAvailability") @DefaultValue("true") boolean checkAvailability
177 ) throws Exception {
178 Job job = null;
179 try {
180 Gson gson = new Gson();
181 Set<String> setElementIds = gson.fromJson(elementId, new TypeToken<Set<String>>() { }.getType());
182 MediaPackage mediapackage = MediaPackageParser.getFromXml(mediaPackageXml);
183 job = service.distribute(channelId, mediapackage, setElementIds, checkAvailability);
184 } catch (IllegalArgumentException e) {
185 logger.debug("Unable to distribute element: {}", e.getMessage());
186 return status(Status.BAD_REQUEST).build();
187 } catch (Exception e) {
188 logger.warn("Unable to distribute media package {}, element {} to aws s3 channel",
189 mediaPackageXml, elementId, e);
190 return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
191 }
192 return Response.ok(new JaxbJob(job)).build();
193 }
194
195 @POST
196 @Path("/distributesync")
197 @Produces(MediaType.TEXT_XML)
198 @RestQuery(
199 name = "distributesync",
200 description = "Synchronously distribute a media package element to this distribution channel",
201 returnDescription = "The distribution",
202 restParameters = {
203 @RestParameter(
204 name = "mediapackage",
205 isRequired = true,
206 description = "The mediapackage",
207 type = Type.TEXT
208 ),
209 @RestParameter(
210 name = "channelId",
211 isRequired = true,
212 description = "The publication channel ID",
213 type = Type.TEXT
214 ),
215 @RestParameter(
216 name = "elementId",
217 isRequired = true,
218 description = "The element to distribute",
219 type = Type.STRING
220 ),
221 @RestParameter(
222 name = "checkAvailability",
223 isRequired = false,
224 description = "If the service should try to access the distributed element",
225 type = Type.BOOLEAN
226 )
227 },
228 responses = {
229 @RestResponse(responseCode = SC_OK, description = "An XML representation of the distribution")
230 }
231 )
232 public Response distributeSync(
233 @FormParam("mediapackage") String mediaPackageXml,
234 @FormParam("channelId") String channelId,
235 @FormParam("elementId") String elementId,
236 @FormParam("checkAvailability") @DefaultValue("true") boolean checkAvailability
237 ) throws Exception {
238 List<MediaPackageElement> result = null;
239 try {
240 Gson gson = new Gson();
241 Set<String> setElementIds = gson.fromJson(elementId, new TypeToken<Set<String>>() { }.getType());
242 MediaPackage mediapackage = MediaPackageParser.getFromXml(mediaPackageXml);
243 result = service.distributeSync(channelId, mediapackage, setElementIds, checkAvailability);
244 } catch (IllegalArgumentException e) {
245 logger.debug("Unable to distribute element: {}", e.getMessage());
246 return status(Status.BAD_REQUEST).build();
247 } catch (Exception e) {
248 logger.warn("Unable to distribute media package {}, element {} to aws s3 channel",
249 mediaPackageXml, elementId, e);
250 return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
251 }
252 return Response.ok(MediaPackageElementParser.getArrayAsXml(result)).build();
253 }
254
255 @POST
256 @Path("/retract")
257 @Produces(MediaType.TEXT_XML)
258 @RestQuery(
259 name = "retract",
260 description = "Retract a media package element from this distribution channel",
261 returnDescription = "The job that can be used to track the retraction",
262 restParameters = {
263 @RestParameter(
264 name = "mediapackage",
265 isRequired = true,
266 description = "The mediapackage",
267 type = Type.TEXT
268 ),
269 @RestParameter(
270 name = "channelId",
271 isRequired = true,
272 description = "The publication channel ID",
273 type = Type.TEXT
274 ),
275 @RestParameter(
276 name = "elementId",
277 isRequired = true,
278 description = "The element to retract",
279 type = Type.STRING
280 )
281 },
282 responses = {
283 @RestResponse(responseCode = SC_OK, description = "An XML representation of the retraction job")
284 }
285 )
286 public Response retract(
287 @FormParam("mediapackage") String mediaPackageXml,
288 @FormParam("channelId") String channelId,
289 @FormParam("elementId") String elementId
290 ) throws Exception {
291 Job job = null;
292 try {
293 Gson gson = new Gson();
294 Set<String> setElementIds = gson.fromJson(elementId, new TypeToken<Set<String>>() { }.getType());
295 MediaPackage mediapackage = MediaPackageParser.getFromXml(mediaPackageXml);
296 job = service.retract(channelId, mediapackage, setElementIds);
297 } catch (IllegalArgumentException e) {
298 logger.debug("Unable to retract element: {}", e.getMessage());
299 return status(Status.BAD_REQUEST).build();
300 } catch (Exception e) {
301 logger.warn("Unable to retract media package {}, element {} from aws s3 channel",
302 mediaPackageXml, elementId, e);
303 return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
304 }
305 return Response.ok(new JaxbJob(job)).build();
306 }
307
308 @POST
309 @Path("/retractsync")
310 @Produces(MediaType.TEXT_XML)
311 @RestQuery(
312 name = "retractsync",
313 description = "Synchronously retract a media package element from this distribution channel",
314 returnDescription = "The retraction",
315 restParameters = {
316 @RestParameter(
317 name = "mediapackage",
318 isRequired = true,
319 description = "The mediapackage",
320 type = Type.TEXT
321 ),
322 @RestParameter(
323 name = "channelId",
324 isRequired = true,
325 description = "The publication channel ID",
326 type = Type.TEXT
327 ),
328 @RestParameter(
329 name = "elementId",
330 isRequired = true,
331 description = "The element to retract",
332 type = Type.STRING
333 )
334 },
335 responses = {
336 @RestResponse(responseCode = SC_OK, description = "An XML representation of the retraction")
337 }
338 )
339 public Response retractSync(
340 @FormParam("mediapackage") String mediaPackageXml,
341 @FormParam("channelId") String channelId,
342 @FormParam("elementId") String elementId
343 ) throws Exception {
344 List<MediaPackageElement> result = null;
345 try {
346 Gson gson = new Gson();
347 Set<String> setElementIds = gson.fromJson(elementId, new TypeToken<Set<String>>() { }.getType());
348 MediaPackage mediapackage = MediaPackageParser.getFromXml(mediaPackageXml);
349 result = service.retractSync(channelId, mediapackage, setElementIds);
350 } catch (IllegalArgumentException e) {
351 logger.debug("Unable to retract element: {}", e.getMessage());
352 return status(Status.BAD_REQUEST).build();
353 } catch (Exception e) {
354 logger.warn("Unable to retract media package {}, element {} from aws s3 channel",
355 mediaPackageXml, elementId, e);
356 return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
357 }
358 return Response.ok(MediaPackageElementParser.getArrayAsXml(result)).build();
359 }
360
361
362
363
364
365
366 @Override
367 public JobProducer getService() {
368 if (service instanceof JobProducer) {
369 return (JobProducer) service;
370 } else {
371 return null;
372 }
373 }
374
375
376
377
378
379
380 @Override
381 public ServiceRegistry getServiceRegistry() {
382 return serviceRegistry;
383 }
384
385 }