1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.publication.configurable.endpoint;
22
23 import org.opencastproject.job.api.JaxbJob;
24 import org.opencastproject.job.api.Job;
25 import org.opencastproject.job.api.JobProducer;
26 import org.opencastproject.mediapackage.MediaPackage;
27 import org.opencastproject.mediapackage.MediaPackageElement;
28 import org.opencastproject.mediapackage.MediaPackageElementParser;
29 import org.opencastproject.mediapackage.MediaPackageParser;
30 import org.opencastproject.mediapackage.Publication;
31 import org.opencastproject.publication.api.ConfigurablePublicationService;
32 import org.opencastproject.rest.AbstractJobProducerEndpoint;
33 import org.opencastproject.serviceregistry.api.ServiceRegistry;
34 import org.opencastproject.util.doc.rest.RestParameter;
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 com.google.gson.Gson;
40 import com.google.gson.reflect.TypeToken;
41
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.util.Collection;
49 import java.util.HashSet;
50 import java.util.Set;
51
52 import javax.servlet.http.HttpServletResponse;
53 import javax.ws.rs.FormParam;
54 import javax.ws.rs.POST;
55 import javax.ws.rs.Path;
56 import javax.ws.rs.Produces;
57 import javax.ws.rs.core.MediaType;
58 import javax.ws.rs.core.Response;
59
60
61
62
63 @Path("/publication/api")
64 @RestService(
65 name = "configurablepublicationservice",
66 title = "Configurable Publication Service",
67 abstractText = "This service publishes and retracts media package elements to a configurable channel",
68 notes = {
69 "All paths above are relative to the REST endpoint base (something like http://your.server/files). "
70 + "If the service is down or not working it will return a status 503, this means the "
71 + "underlying service is not working and is either restarting or has failed. A status "
72 + "code 500 means a general failure has occurred which is not recoverable and was not "
73 + "anticipated. In other words, there is a bug!"
74 }
75 )
76 @Component(
77 immediate = true,
78 service = ConfigurablePublicationRestService.class,
79 property = {
80 "service.description=Configurable Publication REST Endpoint",
81 "opencast.service.type=org.opencastproject.publication.configurable",
82 "opencast.service.path=/publication/api",
83 "opencast.service.jobproducer=true"
84 }
85 )
86 @JaxrsResource
87 public class ConfigurablePublicationRestService extends AbstractJobProducerEndpoint {
88
89 private static final Logger logger = LoggerFactory.getLogger(ConfigurablePublicationRestService.class);
90
91
92 private Gson gson = new Gson();
93
94 private ConfigurablePublicationService service;
95 private ServiceRegistry serviceRegistry;
96
97 @Reference
98 public void setService(final ConfigurablePublicationService service) {
99 this.service = service;
100 }
101
102 @Reference
103 public void setServiceRegistry(final ServiceRegistry serviceRegistry) {
104 this.serviceRegistry = serviceRegistry;
105 }
106
107 @Override
108 public JobProducer getService() {
109
110
111 return (JobProducer) this.service;
112 }
113
114 @Override
115 public ServiceRegistry getServiceRegistry() {
116 return this.serviceRegistry;
117 }
118
119 @POST
120 @Path("/replace")
121 @Produces(MediaType.TEXT_XML)
122 @RestQuery(
123 name = "replace",
124 description = "Replace a media package in this publication channel",
125 returnDescription = "The job that can be used to track the publication",
126 restParameters = {
127 @RestParameter(
128 name = "mediapackage",
129 isRequired = true,
130 description = "The media package",
131 type = RestParameter.Type.TEXT
132 ),
133 @RestParameter(
134 name = "channel",
135 isRequired = true,
136 description = "The channel name",
137 type = RestParameter.Type.STRING
138 ),
139 @RestParameter(
140 name = "addElements",
141 isRequired = true,
142 description = "The media package elements to published",
143 type = RestParameter.Type.STRING
144 ),
145 @RestParameter(
146 name = "retractElements",
147 isRequired = true,
148 description = "The identifiers of the media package elements to be retracted from the media package",
149 type = RestParameter.Type.STRING
150 )
151 },
152 responses = {
153 @RestResponse(
154 responseCode = HttpServletResponse.SC_OK,
155 description = "An XML representation of the publication job"
156 )
157 }
158 )
159 public Response replace(
160 @FormParam("mediapackage") final String mediaPackageXml,
161 @FormParam("channel") final String channel,
162 @FormParam("addElements") final String addElementsXml,
163 @FormParam("retractElements") final String retractElements
164 ) {
165 Response response;
166 final Job job;
167 try {
168 final MediaPackage mediaPackage = MediaPackageParser.getFromXml(mediaPackageXml);
169 final Collection<? extends MediaPackageElement> addElements = new HashSet<>(
170 MediaPackageElementParser.getArrayFromXml(addElementsXml));
171 Set<String> retractElementsIds = gson.fromJson(retractElements, new TypeToken<Set<String>>() { }.getType());
172 job = service.replace(mediaPackage, channel, addElements, retractElementsIds);
173 response = Response.ok(new JaxbJob(job)).build();
174 } catch (IllegalArgumentException e) {
175 logger.warn("Unable to create a publication job", e);
176 response = Response.status(Response.Status.BAD_REQUEST).build();
177 } catch (Exception e) {
178 logger.warn("Error publishing or retracting element", e);
179 response = Response.serverError().build();
180 }
181 return response;
182 }
183
184 @POST
185 @Path("/replacesync")
186 @Produces(MediaType.TEXT_XML)
187 @RestQuery(
188 name = "replacesync",
189 description = "Synchronously replace a media package in this publication channel",
190 returnDescription = "The publication",
191 restParameters = {
192 @RestParameter(
193 name = "mediapackage",
194 isRequired = true,
195 description = "The media package",
196 type = RestParameter.Type.TEXT
197 ),
198 @RestParameter(
199 name = "channel",
200 isRequired = true,
201 description = "The channel name",
202 type = RestParameter.Type.STRING
203 ),
204 @RestParameter(
205 name = "addElements",
206 isRequired = true,
207 description = "The media package elements to published",
208 type = RestParameter.Type.STRING
209 ),
210 @RestParameter(
211 name = "retractElements",
212 isRequired = true,
213 description = "The identifiers of the media package elements to be retracted from the media package",
214 type = RestParameter.Type.STRING
215 )
216 },
217 responses = {
218 @RestResponse(
219 responseCode = HttpServletResponse.SC_OK,
220 description = "An XML representation of the publication"
221 )
222 }
223 )
224 public Response replaceSync(
225 @FormParam("mediapackage") final String mediaPackageXml,
226 @FormParam("channel") final String channel,
227 @FormParam("addElements") final String addElementsXml,
228 @FormParam("retractElements") final String retractElements
229 ) {
230 Response response;
231 final Publication publication;
232 try {
233 final MediaPackage mediaPackage = MediaPackageParser.getFromXml(mediaPackageXml);
234 final Collection<? extends MediaPackageElement> addElements = new HashSet<>(
235 MediaPackageElementParser.getArrayFromXml(addElementsXml));
236 Set<String> retractElementsIds = gson.fromJson(retractElements, new TypeToken<Set<String>>() { }.getType());
237 publication = service.replaceSync(mediaPackage, channel, addElements, retractElementsIds);
238 response = Response.ok(MediaPackageElementParser.getAsXml(publication)).build();
239 } catch (Exception e) {
240 logger.warn("Error publishing or retracting element", e);
241 response = Response.serverError().build();
242 }
243 return response;
244 }
245
246 }