ExecutionResource.java

  1. /*
  2.  * Copyright (c) 2007-2017 MetaSolutions AB
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */

  16. package org.entrystore.rest.resources;

  17. import org.entrystore.AuthorizationException;
  18. import org.entrystore.Data;
  19. import org.entrystore.Entry;
  20. import org.entrystore.EntryType;
  21. import org.entrystore.GraphType;
  22. import org.entrystore.PrincipalManager.AccessProperty;
  23. import org.entrystore.ResourceType;
  24. import org.entrystore.transforms.Pipeline;
  25. import org.entrystore.transforms.TransformException;
  26. import org.json.JSONArray;
  27. import org.json.JSONException;
  28. import org.json.JSONObject;
  29. import org.restlet.data.MediaType;
  30. import org.restlet.data.Status;
  31. import org.restlet.ext.json.JsonRepresentation;
  32. import org.restlet.representation.Representation;
  33. import org.restlet.resource.Post;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;

  36. import java.net.URI;
  37. import java.util.ArrayList;
  38. import java.util.List;
  39. import java.util.Set;


  40. /**
  41.  * This resource executes pipelines etc.
  42.  *
  43.  * @author Hannes Ebner
  44.  */
  45. public class ExecutionResource extends BaseResource {

  46.     static Logger log = LoggerFactory.getLogger(ExecutionResource.class);

  47.     List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();

  48.     @Override
  49.     public void doInit() {
  50.         supportedMediaTypes.add(MediaType.APPLICATION_JSON);
  51.     }

  52.     // TODO add support for GET to query currently running pipelines; this should
  53.     // be supported together with asynchronous processing of pipeline executions
  54.        
  55.     @Post
  56.     public void acceptRepresentation(Representation r) {
  57.         if (context == null) {
  58.             getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
  59.             return;
  60.         }

  61.         MediaType preferredMediaType = getRequest().getClientInfo().getPreferredMediaType(supportedMediaTypes);
  62.         if (!MediaType.APPLICATION_JSON.equals(preferredMediaType)) {
  63.             getResponse().setStatus(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE);
  64.             return;
  65.         }

  66.         JSONObject request = null;
  67.         try {
  68.             request = new JSONObject(getRequest().getEntity().getText());
  69.         } catch (Exception e) {
  70.             getResponse().setStatus(Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY);
  71.             log.error(e.getMessage());
  72.             return;
  73.         }

  74.         String pipeline;
  75.         String source = null;
  76. //      String destination;
  77. //      boolean async = false;

  78.         try {
  79.             pipeline = request.getString("pipeline"); // Pipeline Entry URI
  80.             if (request.has("source")) {
  81.                 source = request.getString("source"); // Data source Entry URI
  82.             }
  83. //          destination = request.getString("destination"); // Destination Entry URI
  84. //          async = "async".equalsIgnoreCase(request.getString("async")); // sync is default
  85.         } catch (JSONException e) {
  86.             getResponse().setStatus(Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY);
  87.             return;
  88.         }

  89.         // Parameter pipeline is required
  90.         if (pipeline == null) {
  91.             getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
  92.             return;
  93.         }

  94.         String contextResUri = context.getEntry().getResourceURI().toString();

  95.         // Pipeline have to be located in the same context
  96.         if (!pipeline.startsWith(contextResUri)) {
  97.             getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
  98.             return;
  99.         }

  100.         // Source (if provided= have to be located in the same context
  101.         if ( source != null && !source.startsWith(contextResUri)) {
  102.             getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
  103.             return;
  104.         }

  105.         try {
  106.             // Pipeline execution requires write-rights on context's resource
  107.             getPM().checkAuthenticatedUserAuthorized(context.getEntry(), AccessProperty.WriteResource);

  108.             // Load pipeline entry from context
  109.             Entry pipelineEntry = context.getByEntryURI(URI.create(pipeline));
  110.             if (pipelineEntry == null) {
  111.                 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
  112.                 return;
  113.             }
  114.             // Load source entry from context
  115.             Entry sourceEntry = null;
  116.             URI listURI = null;
  117.             String sourceMimeType = null;
  118.             Data data = null;
  119.             if (source != null) {
  120.                 sourceEntry = context.getByEntryURI(URI.create(source));
  121.                 if (sourceEntry == null) {
  122.                     getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
  123.                     return;
  124.                 }
  125.                 Set<URI> lists = sourceEntry.getReferringListsInSameContext();
  126.                 if (lists.size() == 1) {
  127.                     listURI = lists.iterator().next();
  128.                 }
  129.                 sourceMimeType = sourceEntry.getMimetype();
  130.                 data = (Data) sourceEntry.getResource();
  131.                 if (!EntryType.Local.equals(sourceEntry.getEntryType()) ||
  132.                         !ResourceType.InformationResource.equals(sourceEntry.getResourceType()) ||
  133.                         sourceMimeType == null ||
  134.                         data == null) {
  135.                     getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
  136.                     return;
  137.                 }
  138.             }

  139.             // TODO add support for non-local resources

  140.             if (!GraphType.Pipeline.equals(pipelineEntry.getGraphType())) {
  141.                 getResponse().setStatus(Status.CLIENT_ERROR_CONFLICT);
  142.                 return;
  143.             }

  144.             Set<Entry> processedEntries = null;
  145.             try {
  146.                 processedEntries = new Pipeline(pipelineEntry).run(sourceEntry, listURI);
  147.             } catch (IllegalStateException iae) {
  148.                 log.error(iae.getMessage());
  149.                 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
  150.                 return;
  151.             } catch (TransformException te) {
  152.                 log.error(te.getMessage());
  153.                 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
  154.                 return;
  155.             }

  156.             if (processedEntries != null && processedEntries.size() > 0) {
  157.                 JSONObject result = new JSONObject();
  158.                 JSONArray processedEntriesArr = new JSONArray();
  159.                 for (Entry e : processedEntries) {
  160.                     processedEntriesArr.put(e.getEntryURI().toString());
  161.                 }
  162.                 try {
  163.                     result.put("result", processedEntriesArr);
  164.                 } catch (JSONException e) {
  165.                     log.error(e.getMessage());
  166.                 }

  167.                 getResponse().setEntity(new JsonRepresentation(result));
  168.                 getResponse().setStatus(Status.SUCCESS_CREATED);
  169.             }

  170.             // TODO support execution status for async executions; perhaps the thread executioner can be
  171.             // shared between listeners and pipelines? this would allow to set a reasonable maximum of
  172.             // concurrent threads per EntryStore instance

  173.         } catch(AuthorizationException e) {
  174.             log.debug("Unauthorized POST");
  175.             unauthorizedPOST();
  176.         }
  177.     }

  178. }