package org.gcube.vremanagement.executor.rest; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import org.gcube.com.fasterxml.jackson.core.JsonProcessingException; import org.gcube.common.authorization.library.provider.CalledMethodProvider; import org.gcube.vremanagement.executor.ResourceInitializer; import org.gcube.vremanagement.executor.annotation.PURGE; import org.gcube.vremanagement.executor.api.rest.RestConstants; import org.gcube.vremanagement.executor.api.types.LaunchParameter; import org.gcube.vremanagement.executor.exception.ExecutorException; import org.gcube.vremanagement.executor.exception.InputsNullException; import org.gcube.vremanagement.executor.exception.InvalidInputsException; import org.gcube.vremanagement.executor.exception.SchedulePersistenceException; import org.gcube.vremanagement.executor.exception.SchedulerNotFoundException; import org.gcube.vremanagement.executor.json.ExtendedSEMapper; import org.gcube.vremanagement.executor.json.SEMapper; import org.gcube.vremanagement.executor.persistence.SmartExecutorPersistenceConnector; import org.gcube.vremanagement.executor.persistence.SmartExecutorPersistenceFactory; import org.gcube.vremanagement.executor.plugin.Plugin; import org.gcube.vremanagement.executor.plugin.PluginDefinition; import org.gcube.vremanagement.executor.plugin.PluginStateEvolution; import org.gcube.vremanagement.executor.pluginmanager.PluginManager; import org.gcube.vremanagement.executor.scheduledtask.ScheduledTask; import org.gcube.vremanagement.executor.scheduler.SmartExecutorScheduler; import org.gcube.vremanagement.executor.scheduler.SmartExecutorSchedulerFactory; import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Path(RestConstants.PLUGINS_PATH_PART) public class RestSmartExecutor { private static Logger logger = LoggerFactory.getLogger(RestSmartExecutor.class); private static final String UUID_PATH_PARAM = "UUID"; private static final String PLUGIN_NAME_PATH_PARAM = "NAME"; protected static final String LOCATION_HEADER = "Location"; @Context protected UriInfo uriInfo; protected void setCalledMethod(String calledMethod) { logger.trace("Going to set calledMethod as '{}'", calledMethod); CalledMethodProvider.instance.set(calledMethod); logger.info("Arrived request at {} with query parameters {}", uriInfo.getAbsolutePath(), uriInfo.getQueryParameters()); } protected ResponseBuilder addLocation(ResponseBuilder responseBuilder, String id) { return responseBuilder.header(LOCATION_HEADER, String.format("%s %s/%s", uriInfo.getAbsolutePath().toString(), id)); } @GET @Path("") @Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8) public String getAvailablePlugins() throws ExecutorException { //setCalledMethod(HttpMethod.GET + " /" + RestConstants.PLUGINS_PATH_PART); setCalledMethod("getAvailablePlugins"); try { PluginManager pluginManager = PluginManager.getInstance(); Map> availablePlugins = pluginManager.getAvailablePlugins(); List plugins = new ArrayList<>(); for(String pluginName : availablePlugins.keySet()) { plugins.add(pluginManager.getPlugin(pluginName)); } return ExtendedSEMapper.getInstance().marshal(PluginDefinition.class, plugins); }catch (Exception e) { throw new ExecutorException(e); } } @GET @Path("/{" + PLUGIN_NAME_PATH_PARAM + "}/" + RestConstants.EXECUTIONS_PATH_PART) @Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8) public String getScheduled(@PathParam(PLUGIN_NAME_PATH_PARAM) String pluginName) throws ExecutorException { // setCalledMethod(HttpMethod.GET + " /" + RestConstants.PLUGINS_PATH_PART + "/" + pluginName + "/" + RestConstants.EXECUTIONS_PATH_PART); setCalledMethod("getScheduledExecution"); try { SmartExecutorPersistenceConnector persistenceConnector = SmartExecutorPersistenceFactory .getPersistenceConnector(); PluginManager.getInstance(); List plugins = new ArrayList<>(); boolean orphan = false; if(pluginName.compareTo(RestConstants.ORPHAN_PATH_PARAM)!=0) { plugins.add(pluginName); }else { // plugins.addAll(pluginManager.getAvailablePlugins().keySet()); orphan = true; } List gotScheduledTasks = persistenceConnector.getScheduledTasks(plugins); List scheduledTasks; if(orphan) { scheduledTasks = new ArrayList<>(); for(ScheduledTask scheduledTask : gotScheduledTasks) { if(persistenceConnector.isOrphan(scheduledTask, false)) { scheduledTasks.add(scheduledTask); } } }else { scheduledTasks = gotScheduledTasks; } /* * Using SEMapper because the server must not return sensitive information like token * To achieve this we need to convert the list to the base ScheduledTask type * i.e. * from org.gcube.vremanagement.executor.scheduledtask.ScheduledTask * to org.gcube.vremanagement.executor.plugin.ScheduledTask * * This conversion is not possible using * List tasks = new ArrayList<>(scheduledTasks); */ List tasks = new ArrayList<>(); for(ScheduledTask scheduledTask : scheduledTasks) { org.gcube.vremanagement.executor.plugin.ScheduledTask task = new org.gcube.vremanagement.executor.plugin.ScheduledTask( scheduledTask.getUUID(), scheduledTask.getRunOn(), scheduledTask.getLaunchParameter()); tasks.add(task); } return SEMapper.getInstance().marshal(org.gcube.vremanagement.executor.plugin.ScheduledTask.class, tasks); } catch(ExecutorException e) { throw e; } catch(Exception e) { throw new ExecutorException(e); } } @POST @Path("/{" + PLUGIN_NAME_PATH_PARAM + "}/" + RestConstants.EXECUTIONS_PATH_PART) @Consumes({MediaType.TEXT_PLAIN, ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8}) @Produces(MediaType.TEXT_PLAIN) public String launch(@PathParam(PLUGIN_NAME_PATH_PARAM) String pluginName, String launchParameterString) throws ExecutorException { //setCalledMethod(HttpMethod.POST + " /" + RestConstants.PLUGINS_PATH_PART + "/" + pluginName + "/" + RestConstants.EXECUTIONS_PATH_PART); setCalledMethod("execute"); try { logger.info("Requested to launch {} ({})", pluginName, launchParameterString); LaunchParameter launchParameter = ExtendedSEMapper.getInstance().unmarshal(LaunchParameter.class, launchParameterString); if(pluginName == null) { String error = String.format("Plugin Name provided in the URL (%s) cannot be null", pluginName); logger.error(error); throw new InputsNullException(error); } if(pluginName.compareTo(launchParameter.getPluginName()) != 0) { String error = String.format( "Plugin Name provided in the URL (%s) does not match with the one provided in %s (%s)", pluginName, LaunchParameter.class.getSimpleName(), launchParameter.getPluginName()); logger.error(error); throw new InvalidInputsException(error); } SmartExecutorScheduler smartExecutorScheduler = SmartExecutorSchedulerFactory.getSmartExecutorScheduler(); UUID uuid = smartExecutorScheduler.schedule(launchParameter, null); logger.info("{} ({}) has been lauched with uuid {}", pluginName, launchParameterString, uuid); return uuid.toString(); } catch(ExecutorException e) { throw e; } catch(Exception e) { throw new ExecutorException(e); } } @GET @Path("/{" + PLUGIN_NAME_PATH_PARAM + "}/" + RestConstants.EXECUTIONS_PATH_PART + "/" + "{" + UUID_PATH_PARAM + "}") @Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8) public String getLaunchState(@PathParam(PLUGIN_NAME_PATH_PARAM) String pluginName, @PathParam(UUID_PATH_PARAM) String executionIdentifier, @QueryParam(RestConstants.ITERATION_PARAM) Integer iteration) throws ExecutorException { // setCalledMethod(HttpMethod.GET + " /" + RestConstants.PLUGINS_PATH_PART + "/" + pluginName + "/" + RestConstants.EXECUTIONS_PATH_PART + "/{" + UUID_PATH_PARAM + "}"); setCalledMethod("getState"); PluginStateEvolution pluginStateEvolution = null; try { SmartExecutorPersistenceConnector persistenceConnector = SmartExecutorPersistenceFactory .getPersistenceConnector(); pluginStateEvolution = persistenceConnector.getPluginInstanceState(UUID.fromString(executionIdentifier), iteration); logger.info("{} for {} (iteration n. {}) is {}", PluginStateEvolution.class.getSimpleName(), executionIdentifier, iteration, pluginStateEvolution); } catch(ExecutorException e) { throw e; } catch(Exception e) { throw new ExecutorException(e); } if(pluginName.compareTo(pluginStateEvolution.getPluginDefinition().getName()) != 0) { String error = String.format( "Plugin Name provided in the URL (%s) does not match with the one got from %s (%s)", pluginName, PluginStateEvolution.class.getSimpleName(), pluginStateEvolution.getPluginDefinition().getName()); throw new InvalidInputsException(error); } try { return ExtendedSEMapper.getInstance().marshal(pluginStateEvolution); } catch(JsonProcessingException e) { throw new ExecutorException(e); } } @DELETE @Path("/{" + PLUGIN_NAME_PATH_PARAM + "}/" + RestConstants.EXECUTIONS_PATH_PART + "/" + "{" + UUID_PATH_PARAM + "}") public Response delete(@PathParam(PLUGIN_NAME_PATH_PARAM) String pluginName, @PathParam(UUID_PATH_PARAM) String executionIdentifier, @QueryParam(RestConstants.UNSCHEDULE_PARAM) Boolean unschedule) throws ExecutorException { try { if(unschedule == null) { unschedule = false; } /* if(unschedule) { setCalledMethod(PURGE.class.getSimpleName() + " /" + RestConstants.PLUGINS_PATH_PART + "/" + pluginName + "/" + RestConstants.EXECUTIONS_PATH_PART + "/{" + UUID_PATH_PARAM + "}"); } else { setCalledMethod(HttpMethod.DELETE + " /" + RestConstants.PLUGINS_PATH_PART + "/" + pluginName + "/" + RestConstants.EXECUTIONS_PATH_PART + "/{" + UUID_PATH_PARAM + "}"); } */ setCalledMethod("stop"); logger.info("Requested to delete for {} with UUID {}{}", pluginName, executionIdentifier, unschedule ? "globally" : ""); boolean currentStopped = true; try { SmartExecutorScheduler smartExecutorScheduler = SmartExecutorSchedulerFactory .getSmartExecutorScheduler(); UUID uuid = UUID.fromString(executionIdentifier); smartExecutorScheduler.stop(uuid, unschedule); } catch(SchedulerNotFoundException e) { // currentStopped = true; logger.error("Error unscheduling task {}", executionIdentifier, e); throw new ExecutorException(e); } catch(SchedulerException e) { // currentStopped = false; logger.error("Error unscheduling task {}", executionIdentifier, e); throw new ExecutorException(e); } catch(SchedulePersistenceException e) { // currentStopped = true; logger.error("Error removing scheduled task from persistence.", e); } catch(ExecutorException e) { throw e; } catch(Exception e) { // currentStopped = false; logger.error("Error unscheduling task {}", executionIdentifier, e); throw new ExecutorException(e); } logger.info("{} with UUID {} was{} stopped successfully", pluginName, executionIdentifier, currentStopped ? "" : " NOT"); if(currentStopped) { return Response.status(Status.NO_CONTENT).build(); }else { throw new ExecutorException(pluginName + " with UUID " + executionIdentifier + " was NOT stopped successfully"); } } catch(ExecutorException e) { throw e; } catch(Exception e) { throw new ExecutorException(e); } } @PURGE @Path("/{" + PLUGIN_NAME_PATH_PARAM + "}/" + RestConstants.EXECUTIONS_PATH_PART + "/" + "{" + UUID_PATH_PARAM + "}") public Response purge(@PathParam(PLUGIN_NAME_PATH_PARAM) String pluginName, @PathParam(UUID_PATH_PARAM) String executionIdentifier) throws ExecutorException { return delete(pluginName, executionIdentifier, true); } }