smart-executor/src/main/java/org/gcube/vremanagement/executor/rest/RestSmartExecutor.java

301 lines
12 KiB
Java

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<String, Class<? extends Plugin>> availablePlugins = pluginManager.getAvailablePlugins();
List<PluginDefinition> 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 pluginManager = PluginManager.getInstance();
List<String> 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<ScheduledTask> gotScheduledTasks = persistenceConnector.getScheduledTasks(plugins);
List<ScheduledTask> 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<org.gcube.vremanagement.executor.plugin.ScheduledTask> tasks = new ArrayList<>(scheduledTasks);
*/
List<org.gcube.vremanagement.executor.plugin.ScheduledTask> 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);
}
}