View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  
23  package org.opencastproject.execute.impl.endpoint;
24  
25  import org.opencastproject.execute.api.ExecuteException;
26  import org.opencastproject.execute.api.ExecuteService;
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.MediaPackageException;
34  import org.opencastproject.mediapackage.MediaPackageParser;
35  import org.opencastproject.rest.AbstractJobProducerEndpoint;
36  import org.opencastproject.serviceregistry.api.ServiceRegistry;
37  import org.opencastproject.util.doc.rest.RestParameter;
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 org.apache.commons.lang3.StringUtils;
43  import org.osgi.service.component.annotations.Component;
44  import org.osgi.service.component.annotations.Reference;
45  import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  import javax.servlet.http.HttpServletResponse;
50  import javax.ws.rs.FormParam;
51  import javax.ws.rs.POST;
52  import javax.ws.rs.Path;
53  import javax.ws.rs.Produces;
54  import javax.ws.rs.core.MediaType;
55  import javax.ws.rs.core.Response;
56  
57  /**
58   * The REST endpoint for {@link ExecuteService}s
59   */
60  @Path("/execute")
61  // Endpoint to the execute service, that runs CLI commands using MediaPackageElement's as parameters
62  @RestService(
63      name = "execute",
64      title = "Execute Service",
65      notes = {"" },
66      abstractText = "Runs CLI commands with MediaPackageElement's as parameters"
67  )
68  @Component(
69      immediate = true,
70      service = ExecuteRestEndpoint.class,
71      property = {
72          "service.description=Execute REST Endpoint",
73          "opencast.service.type=org.opencastproject.execute",
74          "opencast.service.path=/execute",
75          "opencast.service.jobproducer=true"
76      }
77  )
78  @JaxrsResource
79  public class ExecuteRestEndpoint extends AbstractJobProducerEndpoint {
80  
81    /** The logging facility */
82    private static final Logger logger = LoggerFactory.getLogger(ExecuteRestEndpoint.class);
83  
84    /** The service registry */
85    protected ServiceRegistry serviceRegistry = null;
86  
87    /** The execute service */
88    protected ExecuteService service;
89  
90    @POST
91    @Produces(MediaType.TEXT_XML)
92    @Path(ExecuteService.ENDPOINT_NAME)
93    @RestQuery(
94        name = "name",
95        description = "Executes the given command",
96        restParameters = {
97            @RestParameter(description = "The command to execute", isRequired = true,
98                name = ExecuteService.EXEC_FORM_PARAM, type = RestParameter.Type.STRING),
99            @RestParameter(description = "The arguments to the command", isRequired = true,
100               name = ExecuteService.PARAMS_FORM_PARAM, type = RestParameter.Type.STRING),
101           @RestParameter(description = "The estimated load placed on the system by this command", isRequired = false,
102               name = ExecuteService.LOAD_FORM_PARAM, type = RestParameter.Type.FLOAT),
103           @RestParameter(description = "The mediapackage to apply the command to. Either this or "
104               + ExecuteService.INPUT_ELEM_FORM_PARAM + " are required", isRequired = false,
105               name = ExecuteService.INPUT_MP_FORM_PARAM, type = RestParameter.Type.TEXT),
106           @RestParameter(description = "The mediapackage element to apply the command to. Either this or "
107               + ExecuteService.INPUT_MP_FORM_PARAM + " are required", isRequired = false,
108               name = ExecuteService.INPUT_ELEM_FORM_PARAM, type = RestParameter.Type.TEXT),
109           @RestParameter(description = "The mediapackage element produced by the command", isRequired = false,
110               name = ExecuteService.OUTPUT_NAME_FORM_PARAMETER, type = RestParameter.Type.STRING),
111           @RestParameter(description = "The type of the returned element", isRequired = false,
112               name = ExecuteService.TYPE_FORM_PARAMETER, type = RestParameter.Type.STRING)
113       },
114       responses = {
115           @RestResponse(description = "XML-encoded Job is returned.", responseCode = HttpServletResponse.SC_NO_CONTENT),
116           @RestResponse(description = "Service unavailabe or not currently present",
117               responseCode = HttpServletResponse.SC_SERVICE_UNAVAILABLE),
118           @RestResponse(description = "Incorrect parameters", responseCode = HttpServletResponse.SC_BAD_REQUEST),
119           @RestResponse(description = "Problem executing the command or serializing the arguments/results",
120               responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
121       },
122       returnDescription = "")
123   public Response execute(@FormParam(ExecuteService.EXEC_FORM_PARAM) String exec,
124           @FormParam(ExecuteService.PARAMS_FORM_PARAM) String params,
125           @FormParam(ExecuteService.LOAD_FORM_PARAM) Float loadParam,
126           @FormParam(ExecuteService.INPUT_ELEM_FORM_PARAM) String inputElementStr,
127           @FormParam(ExecuteService.INPUT_MP_FORM_PARAM) String inputMpStr,
128           @FormParam(ExecuteService.OUTPUT_NAME_FORM_PARAMETER) String outputFileName,
129           @FormParam(ExecuteService.TYPE_FORM_PARAMETER) String elementTypeStr) {
130 
131     checkNotNull(service);
132     try {
133 
134       MediaPackageElement.Type expectedType = null;
135       if (StringUtils.isNotBlank(elementTypeStr)) {
136         for (MediaPackageElement.Type candidateType : MediaPackageElement.Type.values()) {
137           if (candidateType.toString().equalsIgnoreCase(elementTypeStr)) {
138             expectedType = candidateType;
139             break;
140           }
141         }
142         if (expectedType == null) {
143           logger.error("Wrong element type specified: {}", elementTypeStr);
144           return Response.status(Response.Status.BAD_REQUEST).build();
145         }
146       }
147 
148       float load = 1.0f;
149       if (loadParam != null) {
150         load = loadParam;
151       }
152 
153       Job retJob = null;
154       if (StringUtils.isNotBlank(inputElementStr) && StringUtils.isNotBlank(inputMpStr)) {
155         logger.error("Only one input MediaPackage OR input MediaPackageElement can be set at the same time");
156         return Response.status(Response.Status.BAD_REQUEST).build();
157       } else if (StringUtils.isNotBlank(inputElementStr)) {
158         MediaPackageElement inputElement = MediaPackageElementParser.getFromXml(inputElementStr);
159         retJob = service.execute(exec, params, inputElement, outputFileName, expectedType, load);
160       } else if (StringUtils.isNotBlank(inputMpStr)) {
161         MediaPackage inputMp = MediaPackageParser.getFromXml(inputMpStr);
162         retJob = service.execute(exec, params, inputMp, outputFileName, expectedType, load);
163       } else {
164         logger.error("A MediaPackage OR MediaPackageElement must be provided");
165         return Response.status(Response.Status.BAD_REQUEST).build();
166       }
167 
168       return Response.ok(new JaxbJob(retJob)).build();
169 
170     } catch (IllegalArgumentException e) {
171       logger.error("The expected element type is required if an output filename is specified");
172       return Response.status(Response.Status.BAD_REQUEST).build();
173     } catch (MediaPackageException e) {
174       logger.error("Received excepcion: {}", e.getMessage());
175       return Response.serverError().build();
176     } catch (ExecuteException e) {
177       logger.error("Received error from the execute service: {}", e.getMessage());
178       return Response.serverError().build();
179     }
180   }
181 
182 
183   /**
184    * Sets the service
185    *
186    * @param service
187    */
188   @Reference
189   public void setExecuteService(ExecuteService service) {
190     this.service = service;
191   }
192 
193   /**
194    * {@inheritDoc}
195    * 
196    * @see org.opencastproject.rest.AbstractJobProducerEndpoint#getService()
197    */
198   @Override
199   public JobProducer getService() {
200     if (service instanceof JobProducer) {
201       return (JobProducer) service;
202     } else {
203       return null;
204     }
205   }
206 
207   /**
208    * Checks if the service or services are available, if not it handles it by returning a 503 with a message
209    * 
210    * @param services
211    *          an array of services to check
212    */
213   protected void checkNotNull(Object... services) {
214     if (services != null) {
215       for (Object object : services) {
216         if (object == null) {
217           throw new javax.ws.rs.WebApplicationException(javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE);
218         }
219       }
220     }
221   }
222 
223   /**
224    * Callback from the OSGi declarative services to set the service registry.
225    *
226    * @param serviceRegistry
227    *          the service registry
228    */
229   @Reference
230   protected void setServiceRegistry(ServiceRegistry serviceRegistry) {
231     this.serviceRegistry = serviceRegistry;
232   }
233 
234   /**
235    * {@inheritDoc}
236    *
237    * @see org.opencastproject.rest.AbstractJobProducerEndpoint#getServiceRegistry()
238    */
239   @Override
240   public ServiceRegistry getServiceRegistry() {
241     return serviceRegistry;
242   }
243 }