/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive_grid_cloud_portal.scheduler; import static com.google.common.base.Preconditions.checkNotNull; import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.SequenceInputStream; import java.io.Serializable; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.KeyException; import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.security.auth.login.LoginException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Context; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileType; import org.apache.commons.vfs2.Selectors; import org.apache.log4j.Logger; import org.atmosphere.cpr.AtmosphereResource; import org.atmosphere.cpr.AtmosphereResourceFactory; import org.atmosphere.cpr.Broadcaster; import org.atmosphere.websocket.WebSocketEventListenerAdapter; import org.dozer.DozerBeanMapper; import org.dozer.Mapper; import org.jboss.resteasy.annotations.GZIP; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.objectweb.proactive.ActiveObjectCreationException; import org.objectweb.proactive.api.PAActiveObject; import org.objectweb.proactive.api.PAFuture; import org.objectweb.proactive.core.node.NodeException; import org.objectweb.proactive.core.util.log.ProActiveLogger; import org.objectweb.proactive.utils.StackTraceUtil; import org.ow2.proactive.authentication.crypto.CredData; import org.ow2.proactive.authentication.crypto.Credentials; import org.ow2.proactive.db.SortOrder; import org.ow2.proactive.db.SortParameter; import org.ow2.proactive.scheduler.common.JobFilterCriteria; import org.ow2.proactive.scheduler.common.JobSortParameter; import org.ow2.proactive.scheduler.common.Page; import org.ow2.proactive.scheduler.common.Scheduler; import org.ow2.proactive.scheduler.common.SchedulerAuthenticationInterface; import org.ow2.proactive.scheduler.common.SchedulerConnection; import org.ow2.proactive.scheduler.common.SchedulerConstants; import org.ow2.proactive.scheduler.common.SortSpecifierContainer; import org.ow2.proactive.scheduler.common.exception.ConnectionException; import org.ow2.proactive.scheduler.common.exception.InternalSchedulerException; import org.ow2.proactive.scheduler.common.exception.JobAlreadyFinishedException; import org.ow2.proactive.scheduler.common.exception.JobCreationException; import org.ow2.proactive.scheduler.common.exception.NotConnectedException; import org.ow2.proactive.scheduler.common.exception.PermissionException; import org.ow2.proactive.scheduler.common.exception.SchedulerException; import org.ow2.proactive.scheduler.common.exception.SubmissionClosedException; import org.ow2.proactive.scheduler.common.exception.UnknownJobException; import org.ow2.proactive.scheduler.common.exception.UnknownTaskException; import org.ow2.proactive.scheduler.common.job.Job; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.job.JobInfo; import org.ow2.proactive.scheduler.common.job.JobPriority; import org.ow2.proactive.scheduler.common.job.JobResult; import org.ow2.proactive.scheduler.common.job.JobState; import org.ow2.proactive.scheduler.common.job.factories.FlatJobFactory; import org.ow2.proactive.scheduler.common.task.Task; import org.ow2.proactive.scheduler.common.task.TaskId; import org.ow2.proactive.scheduler.common.task.TaskResult; import org.ow2.proactive.scheduler.common.task.TaskState; import org.ow2.proactive.scheduler.common.task.TaskStatesPage; import org.ow2.proactive.scheduler.common.util.PageBoundaries; import org.ow2.proactive.scheduler.common.util.Pagination; import org.ow2.proactive.scheduler.common.util.SchedulerProxyUserInterface; import org.ow2.proactive.scheduler.common.util.TaskLoggerRelativePathGenerator; import org.ow2.proactive.scheduler.common.util.logforwarder.LogForwardingException; import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties; import org.ow2.proactive.scheduler.job.JobIdImpl; import org.ow2.proactive_grid_cloud_portal.common.SchedulerRestInterface; import org.ow2.proactive_grid_cloud_portal.common.Session; import org.ow2.proactive_grid_cloud_portal.common.SessionStore; import org.ow2.proactive_grid_cloud_portal.common.SharedSessionStore; import org.ow2.proactive_grid_cloud_portal.common.dto.LoginForm; import org.ow2.proactive_grid_cloud_portal.dataspace.RestDataspaceImpl; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobIdData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobInfoData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobResultData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobStateData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobUsageData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.JobValidationData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.RestMapPage; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.RestPage; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.SchedulerStatusData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.SchedulerUserData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.TaskIdData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.TaskResultData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.TaskStateData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.UserJobData; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.eventing.EventNotification; import org.ow2.proactive_grid_cloud_portal.scheduler.dto.eventing.EventSubscription; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.JobAlreadyFinishedRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.JobCreationRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.LogForwardingRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.NotConnectedRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.PermissionRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.SchedulerRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.SubmissionClosedRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.UnknownJobRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.exception.UnknownTaskRestException; import org.ow2.proactive_grid_cloud_portal.scheduler.util.EventUtil; import org.ow2.proactive_grid_cloud_portal.scheduler.util.WorkflowVariablesTransformer; import org.ow2.proactive_grid_cloud_portal.webapp.DateFormatter; import org.ow2.proactive_grid_cloud_portal.webapp.PortalConfiguration; /** * This class exposes the Scheduler as a RESTful service. */ @Path("/scheduler/") public class SchedulerStateRest implements SchedulerRestInterface { /** * If the rest api was unable to instantiate the value from byte array * representation */ public static final String UNKNOWN_VALUE_TYPE = "Unknown value type"; private static final Logger logger = ProActiveLogger.getLogger(SchedulerStateRest.class); private static final String ATM_BROADCASTER_ID = "atmosphere.broadcaster.id"; private static final String ATM_RESOURCE_ID = "atmosphere.resource.id"; private final SessionStore sessionStore = SharedSessionStore.getInstance(); private static RestDataspaceImpl dataspaceRestApi = new RestDataspaceImpl(); private static Map<String, String> sortableTaskAttrMap = null; private static final int TASKS_PAGE_SIZE = PASchedulerProperties.TASKS_PAGE_SIZE.getValueAsInt(); static { sortableTaskAttrMap = createSortableTaskAttrMap(); } protected static final List<SortParameter<JobSortParameter>> DEFAULT_JOB_SORT_PARAMS = Arrays.asList(new SortParameter<>(JobSortParameter.STATE, SortOrder.ASC), new SortParameter<>(JobSortParameter.ID, SortOrder.DESC)); private static final Mapper mapper = new DozerBeanMapper(Collections.singletonList("org/ow2/proactive_grid_cloud_portal/scheduler/dozer-mappings.xml")); @Context private HttpServletRequest httpServletRequest; private final WorkflowVariablesTransformer workflowVariablesTransformer = new WorkflowVariablesTransformer(); private final ValidationUtil jobValidator = new ValidationUtil(); /** * Returns the ids of the current jobs under a list of string. * * @param sessionId * a valid session id * @param index * optional, if a sublist has to be returned the index of the * sublist * @param limit * optional, if a sublist has to be returned, the limit of the * sublist * @return a list of jobs' ids under the form of a list of string */ @Override @GET @Path("jobs") @Produces("application/json") public RestPage<String> jobs(@HeaderParam("sessionid") String sessionId, @QueryParam("index") @DefaultValue("-1") int index, @QueryParam("limit") @DefaultValue("-1") int limit) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "/scheduler/jobs"); Page<JobInfo> page = s.getJobs(index, limit, new JobFilterCriteria(false, true, true, true), DEFAULT_JOB_SORT_PARAMS); List<String> ids = new ArrayList<String>(page.getList().size()); for (JobInfo jobInfo : page.getList()) { ids.add(jobInfo.getJobId().value()); } return new RestPage<String>(ids, page.getSize()); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } /** * Call a method on the scheduler's frontend in order to renew the lease the * user has on this frontend. see PORTAL-70 * * @throws NotConnectedRestException */ protected void renewLeaseForClient(Scheduler scheduler) throws NotConnectedRestException { try { scheduler.renewSession(); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a subset of the scheduler state, including pending, running, * finished jobs (in this particular order). each jobs is described using - * its id - its owner - the JobInfo class * * @param index * optional, if a sublist has to be returned the index of the * sublist * @param limit * optional, if a sublist has to be returned, the limit of the * sublist * @param sessionId * a valid session id * @return a list of UserJobData */ @Override @GET @Path("jobsinfo") @Produces({ "application/json", "application/xml" }) public RestPage<UserJobData> jobsInfo(@HeaderParam("sessionid") String sessionId, @QueryParam("index") @DefaultValue("-1") int index, @QueryParam("limit") @DefaultValue("-1") int limit) throws PermissionRestException, NotConnectedRestException { try { Scheduler s = checkAccess(sessionId, "/scheduler/jobsinfo"); Page<JobInfo> page = s.getJobs(index, limit, new JobFilterCriteria(false, true, true, true), DEFAULT_JOB_SORT_PARAMS); List<UserJobData> userJobInfoList = new ArrayList<UserJobData>(page.getList().size()); for (JobInfo jobInfo : page.getList()) { userJobInfoList.add(new UserJobData(mapper.map(jobInfo, JobInfoData.class))); } return new RestPage<UserJobData>(userJobInfoList, page.getSize()); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } /** * Returns a map containing one entry with the revision id as key and the * list of UserJobData as value. each jobs is described using - its id - its * owner - the JobInfo class * * @param sessionId * a valid session id * @param index * optional, if a sublist has to be returned the index of the * sublist * @param limit * optional, if a sublist has to be returned, the limit of the * sublist * @param myJobs * fetch only the jobs for the user making the request * @param pending * fetch pending jobs * @param running * fetch running jobs * @param finished * fetch finished jobs * @return a map containing one entry with the revision id as key and the * list of UserJobData as value. */ @Override @GET @GZIP @Path("revisionjobsinfo") @Produces({ "application/json", "application/xml" }) public RestMapPage<Long, ArrayList<UserJobData>> revisionAndJobsInfo(@HeaderParam("sessionid") String sessionId, @QueryParam("index") @DefaultValue("-1") int index, @QueryParam("limit") @DefaultValue("-1") int limit, @QueryParam("myjobs") @DefaultValue("false") boolean myJobs, @QueryParam("pending") @DefaultValue("true") boolean pending, @QueryParam("running") @DefaultValue("true") boolean running, @QueryParam("finished") @DefaultValue("true") boolean finished) throws PermissionRestException, NotConnectedRestException { try { Scheduler s = checkAccess(sessionId, "revisionjobsinfo?index=" + index + "&limit=" + limit); String user = sessionStore.get(sessionId).getUserName(); boolean onlyUserJobs = (myJobs && user != null && user.trim().length() > 0); Page<JobInfo> page = s.getJobs(index, limit, new JobFilterCriteria(onlyUserJobs, pending, running, finished), DEFAULT_JOB_SORT_PARAMS); List<JobInfo> jobsInfo = page.getList(); ArrayList<UserJobData> jobs = new ArrayList<>(jobsInfo.size()); for (JobInfo jobInfo : jobsInfo) { jobs.add(new UserJobData(mapper.map(jobInfo, JobInfoData.class))); } HashMap<Long, ArrayList<UserJobData>> map = new HashMap<Long, ArrayList<UserJobData>>(1); map.put(SchedulerStateListener.getInstance().getSchedulerStateRevision(), jobs); RestMapPage<Long, ArrayList<UserJobData>> restMapPage = new RestMapPage<Long, ArrayList<UserJobData>>(); restMapPage.setMap(map); restMapPage.setSize(page.getSize()); return restMapPage; } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns the revision number of the scheduler state * * @param sessionId * a valid session id. * @return the revision of the scheduler state */ @Override @GET @Path("state/revision") @Produces({ "application/json", "application/xml" }) public long schedulerStateRevision(@HeaderParam("sessionid") String sessionId) throws NotConnectedRestException { checkAccess(sessionId, "/scheduler/revision"); return SchedulerStateListener.getInstance().getSchedulerStateRevision(); } /** * Returns a JobState of the job identified by the id <code>jobid</code> * * @param sessionId * a valid session id * @param jobId * the id of the job to retrieve */ @Override @GET @Path("jobs/{jobid}") @Produces({ "application/json", "application/xml" }) public JobStateData listJobs(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "/scheduler/jobs/" + jobId); JobState js = s.getJobState(jobId); js = PAFuture.getFutureValue(js); return mapper.map(js, JobStateData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } } /** * Stream the output of job identified by the id <code>jobid</code> only * stream currently available logs, call this method several times to get * the complete output. * * @param sessionId * a valid session id * @param jobId * the id of the job to retrieve * @throws IOException * @throws LogForwardingRestException */ @GET @GZIP @Path("jobs/{jobid}/livelog") @Produces("application/json") @Override public String getLiveLogJob(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException, LogForwardingRestException, IOException { try { Scheduler scheduler = checkAccess(sessionId, "/scheduler/jobs/" + jobId + "/livelog"); Session session = sessionStore.get(sessionId); JobState jobState = scheduler.getJobState(jobId); boolean isFinished = jobState != null && jobState.isFinished(); int availableLinesCount = session.getJobsOutputController().availableLinesCount(jobId); if (!isFinished || availableLinesCount > 0) { return session.getJobsOutputController().getNewLogs(jobId); } else { session.getJobsOutputController().removeAppender(jobId); return ""; } } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (LogForwardingException e) { throw new LogForwardingRestException(e); } } /** * number of available bytes in the stream or -1 if the stream does not * exist. * * @param sessionId * a valid session id * @param jobId * the id of the job to retrieve */ @Override @GET @Path("jobs/{jobid}/livelog/available") @Produces("application/json") public int getLiveLogJobAvailable(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException { checkAccess(sessionId, "/scheduler/jobs/" + jobId + "/livelog/available"); Session ss = sessionStore.get(sessionId); return ss.getJobsOutputController().availableLinesCount(jobId); } /** * remove the live log object. * * @param sessionId * a valid session id * @param jobId * the id of the job to retrieve * @throws NotConnectedRestException */ @Override @DELETE @Path("jobs/{jobid}/livelog") @Produces("application/json") public boolean deleteLiveLogJob(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException { checkAccess(sessionId, "delete /scheduler/jobs/livelog" + jobId); Session ss = sessionStore.get(sessionId); ss.getJobsOutputController().removeAppender(jobId); return true; } /** * Returns the job result associated to the job referenced by the id * <code>jobid</code> * * @param sessionId * a valid session id * @return the job result of the corresponding job */ @Override @GET @GZIP @Path("jobs/{jobid}/result") @Produces("application/json") public JobResultData jobResult(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, PermissionRestException, UnknownJobRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/result"); return mapper.map(PAFuture.getFutureValue(s.getJobResult(jobId)), JobResultData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * {@inheritDoc} */ @Override public JobInfoData jobInfo(String sessionId, String jobId) throws NotConnectedRestException, PermissionRestException, UnknownJobRestException { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/info"); JobInfoData job = null; try { job = mapper.map(s.getJobInfo(jobId), JobInfoData.class); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } return job; } /** * Returns all the task results of this job as a map whose the key is the * name of the task and its task result.<br> * If the result cannot be instantiated, the content is replaced by the * string 'Unknown value type'. To get the serialized form of a given * result, one has to call the following restful service * jobs/{jobid}/tasks/{taskname}/result/serializedvalue * * @param sessionId * a valid session id * @param jobId * a job id */ @Override @GET @GZIP @Path("jobs/{jobid}/result/value") @Produces("application/json") public Map<String, String> jobResultValue(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, PermissionRestException, UnknownJobRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/result/value"); JobResult jobResult = PAFuture.getFutureValue(s.getJobResult(jobId)); if (jobResult == null) { return null; } Map<String, TaskResult> allResults = jobResult.getAllResults(); Map<String, String> res = new HashMap<>(allResults.size()); for (final Entry<String, TaskResult> entry : allResults.entrySet()) { TaskResult taskResult = entry.getValue(); String value = getTaskResultValueAsStringOrExceptionStackTrace(taskResult); res.put(entry.getKey(), value); } return res; } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Delete a job * * @param sessionId * a valid session id * @param jobId * the id of the job to delete * @return true if success, false if the job not yet finished (not removed, * kill the job then remove it) * */ @Override @DELETE @Path("jobs/{jobid}") @Produces("application/json") public boolean removeJob(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "DELETE jobs/" + jobId); return s.removeJob(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns job server logs * * @param sessionId * a valid session id * @param jobId * the id of the job * @return job traces from the scheduler and resource manager */ @Override @GET @GZIP @Path("jobs/{jobid}/log/server") @Produces("application/json") public String jobServerLog(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/log/server"); return s.getJobServerLogs(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Kill the job represented by jobId.<br> * * @param sessionId * a valid session id * @param jobId * the job to kill. * @return true if success, false if not. */ @Override @PUT @Path("jobs/{jobid}/kill") @Produces("application/json") public boolean killJob(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobId + "/kill"); return s.killJob(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Kill a task within a job * * @param sessionId * current session * @param jobid * id of the job containing the task to kill * @param taskname * name of the task to kill * @throws UnknownJobRestException * @throws UnknownTaskRestException * @throws PermissionRestException * @throws NotConnectedRestException */ @PUT @Path("jobs/{jobid}/tasks/{taskname}/kill") @Produces("application/json") public boolean killTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobid, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobid + "/tasks/" + taskname + "/kill"); return s.killTask(jobid, taskname); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Preempt a task within a job * <p> * The task will be stopped and restarted later * * @param sessionId * current session * @param jobid * id of the job containing the task to preempt * @param taskname * name of the task to preempt * @throws NotConnectedRestException * @throws org.ow2.proactive_grid_cloud_portal.scheduler.exception.UnknownJobRestException * @throws org.ow2.proactive_grid_cloud_portal.scheduler.exception.UnknownTaskRestException * @throws org.ow2.proactive_grid_cloud_portal.scheduler.exception.PermissionRestException */ @Override @PUT @Path("jobs/{jobid}/tasks/{taskname}/preempt") @Produces("application/json") public boolean preemptTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobid, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobid + "/tasks/" + taskname + "/preempt"); return s.preemptTask(jobid, taskname, 5); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Restart a task within a job * * @param sessionId * current session * @param jobid * id of the job containing the task to kill * @param taskname * name of the task to kill * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws UnknownTaskRestException * @throws PermissionRestException */ @Override @PUT @Path("jobs/{jobid}/tasks/{taskname}/restart") @Produces("application/json") public boolean restartTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobid, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobid + "/tasks/" + taskname + "/restart"); return s.restartTask(jobid, taskname, 5); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Finish a task, which is in InError state inside a job. * * @param sessionId * current session * @param jobid * id of the job containing the task to finish (only when InError state) * @param taskname * name of the task to finish (only when InError state) * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws UnknownTaskRestException * @throws PermissionRestException */ @Override @PUT @Path("jobs/{jobid}/tasks/{taskname}/finishInErrorTask") @Produces("application/json") public boolean finishInErrorTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobid, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobid + "/tasks/" + taskname + "/finishInErrorTask"); return s.finishInErrorTask(jobid, taskname); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Restart a pause on error task within a job * * @param sessionId * current session * @param jobid * id of the job containing the task to kill * @param taskname * name of the task to kill * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws UnknownTaskRestException * @throws PermissionRestException */ @Override @PUT @Path("jobs/{jobid}/tasks/{taskname}/restartInErrorTask") @Produces("application/json") public boolean restartInErrorTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobid, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "PUT jobs/" + jobid + "/tasks/" + taskname + "/restartInErrorTask"); return s.restartInErrorTask(jobid, taskname); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Returns a list of the name of the tasks belonging to job * <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * jobid one wants to list the tasks' name * @return a list of tasks' name */ @Override @GET @Path("jobs/{jobid}/tasks") @Produces("application/json") public RestPage<String> getTasksNames(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { return getTasksNamesPaginated(sessionId, jobId, 0, TASKS_PAGE_SIZE); } /** * Returns a list of the name of the tasks belonging to job * <code>jobId</code> with pagination * * @param sessionId * a valid session id * @param jobId * jobid one wants to list the tasks' name * @param offset * the number of the first task to fetch * @param limit * the number of the last task to fetch (non inclusive) * @return the list of task ids with the total number of them */ @Override @GET @Path("jobs/{jobid}/tasks/paginated") @Produces("application/json") public RestPage<String> getTasksNamesPaginated(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @QueryParam("offset") @DefaultValue("0") int offset, @QueryParam("limit") @DefaultValue("-1") int limit) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { if (limit == -1) limit = TASKS_PAGE_SIZE; try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks"); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTasksPaginated(offset, limit); List<String> tasksNames = new ArrayList<>(page.getTaskStates().size()); for (TaskState ts : page.getTaskStates()) { tasksNames.add(ts.getId().getReadableName()); } return new RestPage<String>(tasksNames, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a list of the name of the tasks belonging to job and filtered by * a given tag. <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * jobid one wants to list the tasks' name * @param taskTag * the tag used to filter the tasks. * @return the list of task ids with the total number of them */ @Override @GET @Path("jobs/{jobid}/tasks/tag/{tasktag}") @Produces("application/json") public RestPage<String> getJobTasksIdsByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks"); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTaskByTagPaginated(taskTag, 0, TASKS_PAGE_SIZE); List<TaskState> tasks = page.getTaskStates(); List<String> tasksName = new ArrayList<>(tasks.size()); for (TaskState ts : tasks) { tasksName.add(ts.getId().getReadableName()); } return new RestPage<String>(tasksName, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a list of the name of the tasks belonging to job * <code>jobId</code> (with pagination) * * @param sessionId * a valid session id. * @param jobId * the job id. * @param taskTag * the tag used to filter the tasks. * @param offset * the number of the first task to fetch * @param limit * the number of the last task to fetch (non inclusive) * @return a list of task' states of the job <code>jobId</code> filtered by * a given tag, for a given pagination. */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/paginated") @Produces("application/json") public RestPage<String> getJobTasksIdsByTagPaginated(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag, @QueryParam("offset") @DefaultValue("0") int offset, @QueryParam("limit") @DefaultValue("-1") int limit) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { if (limit == -1) limit = TASKS_PAGE_SIZE; try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskTag + "/paginated"); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTaskByTagPaginated(taskTag, offset, limit); List<TaskState> tasks = page.getTaskStates(); List<String> tasksName = new ArrayList<>(tasks.size()); for (TaskState ts : tasks) { tasksName.add(ts.getId().getReadableName()); } return new RestPage<String>(tasksName, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a list of the tags of the tasks belonging to job * <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * jobid one wants to list the tasks' tags * @return a list of tasks' name */ @GET @Path("jobs/{jobid}/tasks/tags") @Produces("application/json") public List<String> getJobTaskTags(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tags"); JobState jobState = s.getJobState(jobId); return jobState.getTags(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a list of the tags of the tasks belonging to job * <code>jobId</code> and filtered by a prefix pattern * * @param sessionId * a valid session id * @param jobId * jobid one wants to list the tasks' tags * @param prefix * the prefix used to filter tags * @return a list of tasks' name */ @GET @Path("jobs/{jobid}/tasks/tags/startsWith/{prefix}") @Produces("application/json") public List<String> getJobTaskTagsPrefix(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("prefix") String prefix) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tags/startswith/" + prefix); JobState jobState = s.getJobState(jobId); return jobState.getTags(prefix); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * {@inheritDoc} */ @Override @GET @Path("jobs/{jobid}/html") @Produces("text/html") public String getJobHtml(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws IOException, NotConnectedRestException { checkAccess(sessionId); File jobHtml = new File(PortalConfiguration.jobIdToPath(jobId) + ".html"); if (!jobHtml.exists()) { throw new IOException("the file " + jobHtml.getAbsolutePath() + " was not found on the server"); } InputStream ips = new BufferedInputStream(new FileInputStream(jobHtml)); return new String(IOUtils.toByteArray(ips)); } /** * Returns a list of taskState * * @param sessionId * a valid session id * @param jobId * the job id * @return a list of task' states of the job <code>jobId</code> */ @Override @GET @GZIP @Path("jobs/{jobid}/taskstates") @Produces("application/json") public RestPage<TaskStateData> getJobTaskStates(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { return getJobTaskStatesPaginated(sessionId, jobId, 0, TASKS_PAGE_SIZE); } /** * Returns a list of taskState with pagination * * @param sessionId * a valid session id * @param jobId * the job id * @param offset * the index of the first TaskState to return * @param limit * the index (non inclusive) of the last TaskState to return * @return a list of task' states of the job <code>jobId</code> */ @Override @GET @GZIP @Path("jobs/{jobid}/taskstates/paginated") @Produces("application/json") public RestPage<TaskStateData> getJobTaskStatesPaginated(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @QueryParam("offset") @DefaultValue("0") int offset, @QueryParam("limit") @DefaultValue("-1") int limit) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { if (limit == -1) limit = TASKS_PAGE_SIZE; try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/taskstates/paginated"); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTasksPaginated(offset, limit); List<TaskStateData> tasks = map(page.getTaskStates(), TaskStateData.class); return new RestPage<TaskStateData>(tasks, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns a list of taskState of the tasks filtered by a given tag. * * @param sessionId * a valid session id * @param jobId * the job id * @param taskTag * the tag used to filter the tasks * @return a list of task' states of the job <code>jobId</code> filtered by * a given tag. */ @Override @GET @GZIP @Path("jobs/{jobid}/taskstates/{tasktag}") @Produces("application/json") public RestPage<TaskStateData> getJobTaskStatesByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/taskstates/" + taskTag); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTaskByTagPaginated(taskTag, 0, TASKS_PAGE_SIZE); List<TaskStateData> tasks = map(page.getTaskStates(), TaskStateData.class); return new RestPage<TaskStateData>(tasks, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * {@inheritDoc} */ @Override @GET @GZIP @Path("jobs/{jobid}/taskstates/{tasktag}/paginated") @Produces("application/json") public RestPage<TaskStateData> getJobTaskStatesByTagPaginated(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag, @QueryParam("offset") @DefaultValue("0") int offset, @QueryParam("limit") @DefaultValue("-1") int limit) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { if (limit == -1) limit = TASKS_PAGE_SIZE; try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/taskstates/" + taskTag + "/paginated"); JobState jobState = s.getJobState(jobId); TaskStatesPage page = jobState.getTaskByTagPaginated(taskTag, offset, limit); List<TaskStateData> tasks = map(page.getTaskStates(), TaskStateData.class); return new RestPage<TaskStateData>(tasks, page.getSize()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } @Override @GET @GZIP @Path("jobs/{jobid}/log/full") @Produces("application/json") public InputStream jobFullLogs(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @QueryParam("sessionid") String session) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException, IOException { if (sessionId == null) { sessionId = session; } try { Scheduler scheduler = checkAccess(sessionId, "jobs/" + jobId + "/log/full"); JobState jobState = scheduler.getJobState(jobId); List<TaskState> tasks = jobState.getTasks(); List<InputStream> streams = new ArrayList<>(tasks.size()); Collections.sort(tasks, TaskState.COMPARE_BY_FINISHED_TIME_ASC); for (TaskState taskState : tasks) { InputStream inputStream = null; try { if (taskState.isPreciousLogs()) { inputStream = retrieveTaskLogsUsingDataspaces(sessionId, jobId, taskState.getId()); } else { String taskLogs = retrieveTaskLogsUsingDatabase(sessionId, jobId, taskState.getName()); if (!taskLogs.isEmpty()) { inputStream = IOUtils.toInputStream(taskLogs); } logger.warn("Retrieving truncated logs for task '" + taskState.getId() + "'"); } } catch (Exception e) { logger.info("Could not retrieve logs for task " + taskState.getId() + " (could be a non finished or killed task)", e); } if (inputStream != null) { streams.add(inputStream); } } if (streams.isEmpty()) { return null; // will produce HTTP 204 code } else { return new SequenceInputStream(Collections.enumeration(streams)); } } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } public InputStream retrieveTaskLogsUsingDataspaces(String sessionId, String jobId, TaskId taskId) throws PermissionRestException, IOException, NotConnectedRestException { return pullFile(sessionId, SchedulerConstants.USERSPACE_NAME, new TaskLoggerRelativePathGenerator(taskId).getRelativePath()); } /** * Return the task state of the task <code>taskname</code> of the job * <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the task state of the task <code>taskname</code> of the job * <code>jobId</code> */ @Override @GET @Path("jobs/{jobid}/tasks/{taskname}") @Produces("application/json") public TaskStateData jobTask(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException, UnknownTaskRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname); JobState jobState = s.getJobState(jobId); for (TaskState ts : jobState.getTasks()) { if (ts.getId().getReadableName().equals(taskname)) { return mapper.map(ts, TaskStateData.class); } } throw new UnknownTaskRestException("task " + taskname + "not found"); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns the value of the task result of task <code>taskName</code> of the * job <code>jobId</code> <strong>the result is deserialized before sending * to the client, if the class is not found the content is replaced by the * string 'Unknown value type' </strong>. To get the serialized form of a * given result, one has to call the following restful service * jobs/{jobid}/tasks/{taskname}/result/serializedvalue * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the value of the task result */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/value") @Produces("*/*") public Serializable valueOfTaskResult(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/value"); TaskResult taskResult = s.getTaskResult(jobId, taskname); return getTaskResultValueAsStringOrExceptionStackTrace(taskResult); } /** * Returns the value of the task result for a set of tasks of the job * <code>jobId</code> filtered by a given tag. <strong>the result is * deserialized before sending to the client, if the class is not found the * content is replaced by the string 'Unknown value type' </strong>. To get * the serialized form of a given result, one has to call the following * restful service jobs/{jobid}/tasks/tag/{tasktag}/result/serializedvalue * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks. * @return the value of the task result */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/value") @Produces("application/json") public Map<String, String> valueOfTaskResultByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag/" + taskTag + "/result/value"); List<TaskResult> taskResults = s.getTaskResultsByTag(jobId, taskTag); Map<String, String> result = new HashMap<String, String>(taskResults.size()); for (TaskResult currentTaskResult : taskResults) { result.put(currentTaskResult.getTaskId().getReadableName(), getTaskResultValueAsStringOrExceptionStackTrace(currentTaskResult)); } return result; } private String getTaskResultValueAsStringOrExceptionStackTrace(TaskResult taskResult) { if (taskResult == null) { // task is not finished yet return null; } String value = null; // No entry if the task had exception if (taskResult.hadException()) { value = StackTraceUtil.getStackTrace(taskResult.getException()); } else { try { Serializable instanciatedValue = taskResult.value(); if (instanciatedValue != null) { value = instanciatedValue.toString(); } } catch (InternalSchedulerException e) { value = UNKNOWN_VALUE_TYPE; } catch (Throwable t) { value = "Unable to get the value due to " + t.getMessage(); } } return value; } /** * Returns the metadata of the task result of task <code>taskName</code> of the * job <code>jobId</code>. * * Metadata is a map containing additional information associated with a result. For example a file name if the result represents a file. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the metadata of the task result */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/metadata") @Produces("*/*") public Map<String, String> metadataOfTaskResult(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/value"); TaskResult taskResult = s.getTaskResult(jobId, taskname); taskResult = PAFuture.getFutureValue(taskResult); return taskResult.getMetadata(); } /** * Returns the metadata of the task result of task <code>taskName</code> of the * job <code>jobId</code>filtered by a given tag. * <p> * Metadata is a map containing additional information associated with a result. For example a file name if the result represents a file. * * @param sessionId a valid session id * @param jobId the id of the job * @param taskTag the tag used to filter the tasks. * @return a map containing for each task entry, the metadata of the task result */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/metadata") @Produces("application/json") public Map<String, Map<String, String>> metadataOfTaskResultByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag" + taskTag + "/result/serializedvalue"); List<TaskResult> trs = s.getTaskResultsByTag(jobId, taskTag); Map<String, Map<String, String>> result = new HashMap<>(trs.size()); for (TaskResult currentResult : trs) { TaskResult r = PAFuture.getFutureValue(currentResult); result.put(r.getTaskId().getReadableName(), r.getMetadata()); } return result; } /** * Returns the value of the task result of the task <code>taskName</code> of * the job <code>jobId</code> This method returns the result as a byte array * whatever the result is. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the value of the task result as a byte array. */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/serializedvalue") @Produces("*/*") public byte[] serializedValueOfTaskResult(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/serializedvalue"); TaskResult tr = s.getTaskResult(jobId, taskname); tr = PAFuture.getFutureValue(tr); return tr.getSerializedValue(); } /** * Returns the values of a set of tasks of the job <code>jobId</code> * filtered by a given tag. This method returns the result as a byte array * whatever the result is. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks. * @return the values of the set of tasks result as a byte array, indexed by * the readable name of the task. */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/serializedvalue") @Produces("application/json") public Map<String, byte[]> serializedValueOfTaskResultByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws Throwable { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag" + taskTag + "/result/serializedvalue"); List<TaskResult> trs = s.getTaskResultsByTag(jobId, taskTag); Map<String, byte[]> result = new HashMap<>(trs.size()); for (TaskResult currentResult : trs) { TaskResult r = PAFuture.getFutureValue(currentResult); result.put(r.getTaskId().getReadableName(), r.getSerializedValue()); } return result; } /** * Returns the task result of the task <code>taskName</code> of the job * <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the task result of the task <code>taskName</code> */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result") @Produces("application/json") public TaskResultData taskResult(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result"); TaskResult taskResult = s.getTaskResult(jobId, taskname); if (taskResult == null) { TaskIdData taskIdData = new TaskIdData(); taskIdData.setReadableName(taskname); TaskResultData taskResultData = new TaskResultData(); taskResultData.setId(taskIdData); return taskResultData; } return buildTaskResultData(taskResult); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } private TaskResultData buildTaskResultData(TaskResult taskResult) { return mapper.map(PAFuture.getFutureValue(taskResult), TaskResultData.class); } /** * Returns the task results of the set of task filtered by a given tag and * owned by the job <code>jobId</code> * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks. * @return the task results of the set of tasks filtered by the given tag. */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result") @Produces("application/json") public List<TaskResultData> taskResultByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskTag + "/result"); List<TaskResult> taskResults = s.getTaskResultsByTag(jobId, taskTag); ArrayList<TaskResultData> results = new ArrayList<TaskResultData>(taskResults.size()); for (TaskResult current : taskResults) { TaskResultData r = buildTaskResultData(PAFuture.getFutureValue(current)); results.add(r); } return results; } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns all the logs generated by the task (either stdout and stderr) * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return all the logs generated by the task (either stdout and stderr) or * an empty string if the result is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/log/all") @Produces("application/json") public String taskLog(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { return retrieveTaskLogsUsingDatabase(sessionId, jobId, taskname); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } private String retrieveTaskLogsUsingDatabase(String sessionId, String jobId, String taskName) throws NotConnectedRestException, UnknownJobException, UnknownTaskException, NotConnectedException, PermissionException, PermissionRestException { Scheduler scheduler = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskName + "/result/log/all"); TaskResult taskResult = scheduler.getTaskResult(jobId, taskName); if (taskResult != null && taskResult.getOutput() != null) { return taskResult.getOutput().getAllLogs(true); } return ""; } /** * Returns all the logs generated by a set of the tasks (either stdout and * stderr) filtered by a tag. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks. * @return the list of logs generated by each filtered task (either stdout * and stderr) or an empty string if the result is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/log/all") @Produces("application/json") public String taskLogByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag/" + taskTag + "/result/log/err"); List<TaskResult> trs = s.getTaskResultsByTag(jobId, taskTag); StringBuffer buf = new StringBuffer(); for (TaskResult tr : trs) { if (tr.getOutput() != null) { buf.append(tr.getOutput().getAllLogs(true)); } } return buf.toString(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns all the logs generated by the job (either stdout and stderr) * * @param sessionId * a valid session id * @param jobId * the id of the job * @return all the logs generated by the job (either stdout and stderr) or * an empty string if the result is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/result/log/all") @Produces("application/json") public String jobLogs(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/result/log/all"); JobResult jobResult = s.getJobResult(jobId); if (jobResult == null) { return ""; } StringBuilder jobOutput = new StringBuilder(); for (TaskResult tr : jobResult.getAllResults().values()) { if ((tr != null) && (tr.getOutput() != null)) { jobOutput.append(tr.getOutput().getAllLogs(true)); } } return jobOutput.toString(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns the standard error output (stderr) generated by the task * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the stderr generated by the task or an empty string if the result * is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/log/err") @Produces("application/json") public String taskLogErr(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/log/err"); TaskResult tr = s.getTaskResult(jobId, taskname); if ((tr != null) && (tr.getOutput() != null)) { return tr.getOutput().getStderrLogs(true); } else { return ""; } } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Returns the list of standard error outputs (stderr) generated by a set of * tasks filtered by a given tag. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks * @return the list of stderr generated by the set of tasks filtered by the * given tag or an empty string if the result is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/log/err") @Produces("application/json") public String taskLogErrByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag/" + taskTag + "/result/log/err"); List<TaskResult> trs = s.getTaskResultsByTag(jobId, taskTag); StringBuffer buf = new StringBuffer(); for (TaskResult tr : trs) { if (tr.getOutput() != null) { buf.append(tr.getOutput().getStderrLogs(true)); } } return buf.toString(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns the standard output (stderr) generated by the task * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return the stdout generated by the task or an empty string if the result * is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/log/out") @Produces("application/json") public String taskLogout(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/log/out"); TaskResult tr = s.getTaskResult(jobId, taskname); if ((tr != null) && (tr.getOutput() != null)) { return tr.getOutput().getStdoutLogs(true); } else { return ""; } } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Returns the standard output (stdout) generated by a set of tasks filtered * by a given tag. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks. * @return the stdout generated by the task or an empty string if the result * is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/result/log/out") @Produces("application/json") public String taskLogoutByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag/" + taskTag + "/result/log/out"); List<TaskResult> trs = s.getTaskResultsByTag(jobId, taskTag); StringBuffer result = new StringBuffer(); for (TaskResult tr : trs) { if (tr.getOutput() != null) { result.append(tr.getOutput().getStdoutLogs(true)); } } return result.toString(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Returns full logs generated by the task from user data spaces if task was * run using the precious logs option. Otherwise, logs are retrieved from * the database. In this last case they may be truncated. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return all the logs generated by the task (either stdout and stderr) or * an empty string if the result is not yet available */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/result/log/full") @Produces("application/json") public InputStream taskFullLogs(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname, @QueryParam("sessionid") String session) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException, IOException { try { if (sessionId == null) { sessionId = session; } Scheduler scheduler = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/result/log/all"); TaskResult taskResult = scheduler.getTaskResult(jobId, taskname); if (taskResult != null) { JobState jobState = scheduler.getJobState(taskResult.getTaskId().getJobId()); boolean hasPreciousLogs = false; for (Task task : jobState.getTasks()) { if (task.getName().equals(taskname)) { hasPreciousLogs = task.isPreciousLogs(); break; } } if (hasPreciousLogs) { return retrieveTaskLogsUsingDataspaces(sessionId, jobId, taskResult.getTaskId()); } else { logger.warn("Retrieving truncated logs for task '" + taskname + "'"); return IOUtils.toInputStream(retrieveTaskLogsUsingDatabase(sessionId, jobId, taskname)); } } else { return null; } } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Returns task server logs * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskname * the name of the task * @return task traces from the scheduler and resource manager */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/{taskname}/log/server") @Produces("application/json") public String taskServerLog(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("taskname") String taskname) throws NotConnectedRestException, UnknownJobRestException, UnknownTaskRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/" + taskname + "/log/server"); return s.getTaskServerLogs(jobId, taskname); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownTaskException e) { throw new UnknownTaskRestException(e); } } /** * Returns server logs for a set of tasks filtered by a given tag. * * @param sessionId * a valid session id * @param jobId * the id of the job * @param taskTag * the tag used to filter the tasks in the job. * @return task traces from the scheduler and resource manager */ @Override @GET @GZIP @Path("jobs/{jobid}/tasks/tag/{tasktag}/log/server") @Produces("application/json") public String taskServerLogByTag(@HeaderParam("sessionid") String sessionId, @PathParam("jobid") String jobId, @PathParam("tasktag") String taskTag) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/tasks/tag/" + taskTag + "/log/server"); return s.getTaskServerLogsByTag(jobId, taskTag); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * the method check if the session id is valid i.e. a scheduler client is * associated to the session id in the session map. If not, a * NotConnectedRestException is thrown specifying the invalid access * * * @return the scheduler linked to the session id, an * NotConnectedRestException, if no such mapping exists. * @throws NotConnectedRestException */ private SchedulerProxyUserInterface checkAccess(String sessionId, String path) throws NotConnectedRestException { Session session = sessionStore.get(sessionId); if (session == null) { throw new NotConnectedRestException("You are not connected to the scheduler, you should log on first"); } SchedulerProxyUserInterface schedulerProxy = session.getScheduler(); if (schedulerProxy == null) { throw new NotConnectedRestException("You are not connected to the scheduler, you should log on first"); } renewLeaseForClient(schedulerProxy); return schedulerProxy; } private SchedulerProxyUserInterface checkAccess(String sessionId) throws NotConnectedRestException { return checkAccess(sessionId, ""); } /** * Pauses the job represented by jobid * * @param sessionId * a valid session id * @param jobId * the id of the job * @return true if success, false if not */ @Override @PUT @Path("jobs/{jobid}/pause") @Produces("application/json") public boolean pauseJob(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { final Scheduler s = checkAccess(sessionId, "POST jobs/" + jobId + "/pause"); return s.pauseJob(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Restart all tasks in error in the job represented by jobid * * @param sessionId * a valid session id * @param jobId * the id of the job * @return true if success, false if not */ @Override @PUT @Path("jobs/{jobid}/restartAllInErrorTasks") @Produces("application/json") public boolean restartAllInErrorTasks(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "POST jobs/" + jobId + "/restartAllInErrorTasks"); return s.restartAllInErrorTasks(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Resumes the job represented by jobid * * @param sessionId * a valid session id * @param jobId * the id of the job * @return true if success, false if not */ @Override @PUT @Path("jobs/{jobid}/resume") @Produces("application/json") public boolean resumeJob(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "POST jobs/" + jobId + "/resume"); return s.resumeJob(jobId); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Submit job using flat command file * * @param sessionId * valid session id * @param commandFileContent * content of a command file: endline separated native commands * @param jobName * name of the job to create * @param selectionScriptContent * content of a selection script, or null * @param selectionScriptExtension * extension of the selectionscript to determine script engine * ("js", "py", "rb") * @return Id of the submitted job * @throws NotConnectedRestException * @throws IOException * @throws JobCreationRestException * @throws PermissionRestException * @throws SubmissionClosedRestException */ @Override @POST @Path("submitflat") @Produces("application/json") public JobIdData submitFlat(@HeaderParam("sessionid") String sessionId, @FormParam("commandFileContent") String commandFileContent, @FormParam("jobName") String jobName, @FormParam("selectionScriptContent") String selectionScriptContent, @FormParam("selectionScriptExtension") String selectionScriptExtension) throws NotConnectedRestException, IOException, JobCreationRestException, PermissionRestException, SubmissionClosedRestException { Scheduler s = checkAccess(sessionId, "submitflat"); try { File command = File.createTempFile("flatsubmit_commands_", ".txt"); command.deleteOnExit(); String selectionPath = null; File selection = null; if (selectionScriptContent != null && selectionScriptContent.trim().length() > 0) { selection = File.createTempFile("flatsubmit_selection_", "." + selectionScriptExtension); selection.deleteOnExit(); PrintWriter pw = new PrintWriter(new FileOutputStream(selection)); pw.print(selectionScriptContent); pw.close(); selectionPath = selection.getAbsolutePath(); } PrintWriter pw = new PrintWriter(new FileOutputStream(command)); pw.print(commandFileContent); pw.close(); Job j = FlatJobFactory.getFactory().createNativeJobFromCommandsFile(command.getAbsolutePath(), jobName, selectionPath, null); JobId id = s.submit(j); command.delete(); if (selection != null) { selection.delete(); } return mapper.map(id, JobIdData.class); } catch (IOException e) { throw new IOException("I/O Error: " + e.getMessage(), e); } catch (JobCreationException e) { throw new JobCreationRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (SubmissionClosedException e) { throw new SubmissionClosedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } /** * Submits a workflow to the scheduler from a workflow URL, creating hence a * new job resource. * * @param sessionId * a valid session id * @param url * url to the workflow content * @param pathSegment * variables of the workflow * @return the <code>jobid</code> of the newly created job * @throws NotConnectedRestException * @throws IOException * @throws JobCreationRestException * @throws PermissionRestException * @throws SubmissionClosedRestException */ @Override @POST @Path("{path:jobs}") @Produces("application/json") public JobIdData submitFromUrl(@HeaderParam("sessionid") String sessionId, @HeaderParam("link") String url, @PathParam("path") PathSegment pathSegment) throws JobCreationRestException, NotConnectedRestException, PermissionRestException, SubmissionClosedRestException, IOException { Scheduler s = checkAccess(sessionId, "jobs"); File tmpWorkflowFile = null; try { String jobXml = downloadWorkflowContent(sessionId, url); tmpWorkflowFile = File.createTempFile("job", "d"); IOUtils.write(jobXml, new FileOutputStream(tmpWorkflowFile)); WorkflowSubmitter workflowSubmitter = new WorkflowSubmitter(s); JobId jobId = workflowSubmitter.submit(tmpWorkflowFile, workflowVariablesTransformer.getWorkflowVariablesFromPathSegment(pathSegment)); return mapper.map(jobId, JobIdData.class); } catch (IOException e) { throw new IOException("Cannot save temporary job file on submission: " + e.getMessage(), e); } finally { FileUtils.deleteQuietly(tmpWorkflowFile); } } /** * Submits a job to the scheduler * * @param sessionId * a valid session id * @return the <code>jobid</code> of the newly created job * @throws IOException * if the job was not correctly uploaded/stored */ @Override @POST @Path("{path:submit}") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces("application/json") public JobIdData submit(@HeaderParam("sessionid") String sessionId, @PathParam("path") PathSegment pathSegment, MultipartFormDataInput multipart) throws JobCreationRestException, NotConnectedRestException, PermissionRestException, SubmissionClosedRestException, IOException { try { Scheduler scheduler = checkAccess(sessionId, "submit"); Map<String, List<InputPart>> formDataMap = multipart.getFormDataMap(); String name = formDataMap.keySet().iterator().next(); File tmpJobFile = null; try { InputPart part1 = multipart.getFormDataMap().get(name).get(0); // "file" String fileType = part1.getMediaType().toString().toLowerCase(); if (!fileType.contains(MediaType.APPLICATION_XML.toLowerCase())) { throw new JobCreationRestException("Unknown job descriptor type: " + fileType); } // is the name of the browser's input field InputStream is = part1.getBody(new GenericType<InputStream>() { }); tmpJobFile = File.createTempFile("job", "d"); IOUtils.copy(is, new FileOutputStream(tmpJobFile)); Map<String, String> jobVariables = workflowVariablesTransformer.getWorkflowVariablesFromPathSegment(pathSegment); WorkflowSubmitter workflowSubmitter = new WorkflowSubmitter(scheduler); JobId jobId = workflowSubmitter.submit(tmpJobFile, jobVariables); return mapper.map(jobId, JobIdData.class); } finally { if (tmpJobFile != null) { // clean the temporary file tmpJobFile.delete(); } } } catch (IOException e) { throw new IOException("I/O Error: " + e.getMessage(), e); } } private String normalizeFilePath(String filePath, String fileName) { if (filePath == null) { filePath = ""; } if (fileName != null && filePath.length() > 0 && !filePath.endsWith("/")) { filePath = (filePath + "/" + fileName); } else if (fileName != null) { filePath = fileName; } if (filePath.length() > 0 && filePath.startsWith("/")) { filePath = filePath.substring(1); } return filePath; } @Consumes(MediaType.APPLICATION_JSON) @POST @Path("{path:plannings}") @Produces("application/json") @Override public String submitPlannings(@HeaderParam("sessionid") String sessionId, @PathParam("path") PathSegment pathSegment, Map<String, String> jobContentXmlString) throws JobCreationRestException, NotConnectedRestException, PermissionRestException, SubmissionClosedRestException, IOException { checkAccess(sessionId, "plannings"); Map<String, String> jobVariables = workflowVariablesTransformer.getWorkflowVariablesFromPathSegment(pathSegment); if (jobContentXmlString == null || jobContentXmlString.size() != 1) { throw new JobCreationRestException("Cannot find job body: code " + HttpURLConnection.HTTP_BAD_REQUEST); } Map<String, Object> requestBody = new HashMap<>(2); requestBody.put("variables", jobVariables); requestBody.put("xmlContentString", jobContentXmlString.entrySet().iterator().next().getValue()); Response response = null; try { ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target(PortalConfiguration.JOBPLANNER_URL.getValueAsString()); response = target.request() .header("sessionid", sessionId) .post(Entity.entity(requestBody, "application/json")); if (HttpURLConnection.HTTP_OK != response.getStatus()) { throw new IOException(String.format("Cannot access resource %s: code %d", PortalConfiguration.JOBPLANNER_URL.getValueAsString(), response.getStatus())); } return response.readEntity(String.class); } finally { if (response != null) { response.close(); } } } /** * Pushes a file from the local file system into the given DataSpace * * @param sessionId * a valid session id * @param spaceName * the name of the DataSpace * @param filePath * the path inside the DataSpace where to put the file e.g. * "/myfolder" * @param multipart * the form data containing : - fileName the name of the file * that will be created on the DataSpace - fileContent the * content of the file * @return true if the transfer succeeded * @see org.ow2.proactive.scheduler.common.SchedulerConstants for spaces * names **/ @Override public boolean pushFile(@HeaderParam("sessionid") String sessionId, @PathParam("spaceName") String spaceName, @PathParam("filePath") String filePath, MultipartFormDataInput multipart) throws IOException, NotConnectedRestException, PermissionRestException { checkAccess(sessionId, "pushFile"); Session session = dataspaceRestApi.checkSessionValidity(sessionId); Map<String, List<InputPart>> formDataMap = multipart.getFormDataMap(); List<InputPart> fNL = formDataMap.get("fileName"); if ((fNL == null) || (fNL.size() == 0)) { throw new IllegalArgumentException("Illegal multipart argument definition (fileName), received " + fNL); } String fileName = fNL.get(0).getBody(String.class, null); List<InputPart> fCL = formDataMap.get("fileContent"); if ((fCL == null) || (fCL.size() == 0)) { throw new IllegalArgumentException("Illegal multipart argument definition (fileContent), received " + fCL); } InputStream fileContent = fCL.get(0).getBody(InputStream.class, null); if (fileName == null) { throw new IllegalArgumentException("Wrong file name : " + fileName); } filePath = normalizeFilePath(filePath, fileName); FileObject destfo = dataspaceRestApi.resolveFile(session, spaceName, filePath); URL targetUrl = destfo.getURL(); logger.info("[pushFile] pushing file to " + targetUrl); if (!destfo.isWriteable()) { RuntimeException ex = new IllegalArgumentException("File " + filePath + " is not writable in space " + spaceName); logger.error(ex); throw ex; } if (destfo.exists()) { destfo.delete(); } // used to create the necessary directories if needed destfo.createFile(); dataspaceRestApi.writeFile(fileContent, destfo, null); return true; } /** * Either Pulls a file from the given DataSpace to the local file system or * list the content of a directory if the path refers to a directory In the * case the path to a file is given, the content of this file will be * returns as an input stream In the case the path to a directory is given, * the input stream returned will be a text stream containing at each line * the content of the directory * * @param sessionId * a valid session id * @param spaceName * the name of the data space involved (GLOBAL or USER) * @param filePath * the path to the file or directory whose content must be * received **/ @Override public InputStream pullFile(@HeaderParam("sessionid") String sessionId, @PathParam("spaceName") String spaceName, @PathParam("filePath") String filePath) throws IOException, NotConnectedRestException, PermissionRestException { checkAccess(sessionId, "pullFile"); Session session = dataspaceRestApi.checkSessionValidity(sessionId); filePath = normalizeFilePath(filePath, null); FileObject sourcefo = dataspaceRestApi.resolveFile(session, spaceName, filePath); if (!sourcefo.exists() || !sourcefo.isReadable()) { RuntimeException ex = new IllegalArgumentException("File " + filePath + " does not exist or is not readable in space " + spaceName); logger.error(ex); throw ex; } if (sourcefo.getType().equals(FileType.FOLDER)) { logger.info("[pullFile] reading directory content from " + sourcefo.getURL()); // if it's a folder we return an InputStream listing its content StringBuilder sb = new StringBuilder(); String nl = System.lineSeparator(); for (FileObject fo : sourcefo.getChildren()) { sb.append(fo.getName().getBaseName() + nl); } return IOUtils.toInputStream(sb.toString()); } else if (sourcefo.getType().equals(FileType.FILE)) { logger.info("[pullFile] reading file content from " + sourcefo.getURL()); return sourcefo.getContent().getInputStream(); } else { RuntimeException ex = new IllegalArgumentException("File " + filePath + " has an unsupported type " + sourcefo.getType()); logger.error(ex); throw ex; } } /** * Deletes a file or recursively delete a directory from the given DataSpace * * @param sessionId * a valid session id * @param spaceName * the name of the data space involved (GLOBAL or USER) * @param filePath * the path to the file or directory which must be deleted **/ @Override public boolean deleteFile(@HeaderParam("sessionid") String sessionId, @PathParam("spaceName") String spaceName, @PathParam("filePath") String filePath) throws IOException, NotConnectedRestException, PermissionRestException { checkAccess(sessionId, "deleteFile"); Session session = dataspaceRestApi.checkSessionValidity(sessionId); filePath = normalizeFilePath(filePath, null); FileObject sourcefo = dataspaceRestApi.resolveFile(session, spaceName, filePath); if (!sourcefo.exists() || !sourcefo.isWriteable()) { RuntimeException ex = new IllegalArgumentException("File or Folder " + filePath + " does not exist or is not writable in space " + spaceName); logger.error(ex); throw ex; } if (sourcefo.getType().equals(FileType.FILE)) { logger.info("[deleteFile] deleting file " + sourcefo.getURL()); sourcefo.delete(); } else if (sourcefo.getType().equals(FileType.FOLDER)) { logger.info("[deleteFile] deleting folder (and all its descendants) " + sourcefo.getURL()); sourcefo.delete(Selectors.SELECT_ALL); } else { RuntimeException ex = new IllegalArgumentException("File " + filePath + " has an unsupported type " + sourcefo.getType()); logger.error(ex); throw ex; } return true; } /** * terminates the session id <code>sessionId</code> * * @param sessionId * a valid session id * @throws NotConnectedRestException * if the scheduler cannot be contacted * @throws PermissionRestException * if you are not authorized to perform the action */ @Override @PUT @Path("disconnect") @Produces("application/json") public void disconnect(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { final Scheduler s = checkAccess(sessionId, "disconnect"); logger.info("disconnection user " + sessionStore.get(sessionId) + " to session " + sessionId); s.disconnect(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } finally { sessionStore.terminate(sessionId); logger.debug("sessionid " + sessionId + " terminated"); } } /** * pauses the scheduler * * @param sessionId * a valid session id * @return true if success, false otherwise * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("pause") @Produces("application/json") public boolean pauseScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "pause"); return s.pause(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * stops the scheduler * * @param sessionId * a valid session id * @return true if success, false otherwise * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("stop") @Produces("application/json") public boolean stopScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "stop"); return s.stop(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * resumes the scheduler * * @param sessionId * a valid session id * @return true if success, false otherwise * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("resume") @Produces("application/json") public boolean resumeScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "resume"); return s.resume(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * changes the priority of a job * * @param sessionId * a valid session id * @param jobId * the job id * @param priorityName * a string representing the name of the priority * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws PermissionRestException * @throws JobAlreadyFinishedRestException */ @Override @PUT @Path("jobs/{jobid}/priority/byname/{name}") public void schedulerChangeJobPriorityByName(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId, @PathParam("name") String priorityName) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException, JobAlreadyFinishedRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/priority/byname/" + priorityName); s.changeJobPriority(jobId, JobPriority.findPriority(priorityName)); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (JobAlreadyFinishedException e) { throw new JobAlreadyFinishedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } } /** * changes the priority of a job * * @param sessionId * a valid session id * @param jobId * the job id * @param priorityValue * a string representing the value of the priority * @throws NumberFormatException * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws PermissionRestException * @throws JobAlreadyFinishedRestException */ @Override @PUT @Path("jobs/{jobid}/priority/byvalue/{value}") public void schedulerChangeJobPriorityByValue(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId, @PathParam("value") String priorityValue) throws NumberFormatException, NotConnectedRestException, UnknownJobRestException, PermissionRestException, JobAlreadyFinishedRestException { try { Scheduler s = checkAccess(sessionId, "jobs/" + jobId + "/priority/byvalue" + priorityValue); s.changeJobPriority(jobId, JobPriority.findPriority(Integer.parseInt(priorityValue))); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (JobAlreadyFinishedException e) { throw new JobAlreadyFinishedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } } /** * freezes the scheduler * * @param sessionId * a valid session id * @return true if success, false otherwise * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("freeze") @Produces("application/json") public boolean freezeScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "freeze"); return s.freeze(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * returns the status of the scheduler * * @param sessionId * a valid session id * @return the scheduler status * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @GET @Path("status") @Produces("application/json") public SchedulerStatusData getSchedulerStatus(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "status"); return SchedulerStatusData.valueOf(SchedulerStateListener.getInstance().getSchedulerStatus(s).name()); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * starts the scheduler * * @param sessionId * a valid session id * @return true if success, false otherwise * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("start") @Produces("application/json") public boolean startScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "start"); return s.start(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * kills and shutdowns the scheduler * * @param sessionId * a valid session id * @return true if success, false if not * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @PUT @Path("kill") @Produces("application/json") public boolean killScheduler(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "kill"); return s.kill(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Reconnect a new Resource Manager to the scheduler. Can be used if the * resource manager has crashed. * * @param sessionId * a valid session id * @param rmURL * the url of the resource manager * @return true if success, false otherwise. * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @POST @Path("linkrm") @Produces("application/json") public boolean linkRm(@HeaderParam("sessionid") final String sessionId, @FormParam("rmurl") String rmURL) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "linkrm"); return s.linkResourceManager(rmURL); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Tests whether or not the user is connected to the ProActive Scheduler * * @param sessionId * the session to test * @return true if the user connected to a Scheduler, false otherwise. * @throws NotConnectedRestException */ @Override @GET @Path("isconnected") @Produces("application/json") public boolean isConnected(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException { Scheduler s = checkAccess(sessionId, "isconnected"); return s.isConnected(); } /** * Login to the scheduler using a form containing 2 fields (username and * password). * * @param username * username * @param password * password * @return the session id associated to the login. * @throws LoginException */ @Override @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Path("login") @Produces("application/json") public String login(@FormParam("username") String username, @FormParam("password") String password) throws LoginException, SchedulerRestException { try { if ((username == null) || (password == null)) { throw new LoginException("Empty login/password"); } Session session = sessionStore.create(username); session.connectToScheduler(new CredData(username, password)); logger.info("Binding user " + username + " to session " + session.getSessionId()); return session.getSessionId(); } catch (ActiveObjectCreationException e) { throw new SchedulerRestException(e); } catch (SchedulerException e) { throw new SchedulerRestException(e); } catch (NodeException e) { throw new SchedulerRestException(e); } } /** * {@inheritDoc} */ @Override @PUT @Path("session") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces("application/json") public String loginOrRenewSession(@HeaderParam("sessionid") String sessionId, @FormParam("username") String username, @FormParam("password") String password) throws SchedulerRestException, LoginException, NotConnectedRestException { if (sessionId == null || !sessionStore.exists(sessionId)) { return login(username, password); } try { sessionStore.renewSession(sessionId); return sessionId; } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * {@inheritDoc} */ @Override @PUT @Path("session") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces("application/json") public String loginOrRenewSession(@HeaderParam("sessionid") String sessionId, @MultipartForm LoginForm multipart) throws KeyException, SchedulerRestException, LoginException, NotConnectedRestException { if (sessionId == null || !sessionStore.exists(sessionId)) { return loginWithCredential(multipart); } try { sessionStore.renewSession(sessionId); return sessionId; } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * {@inheritDoc} */ @Override @GET @Path("logins/sessionid/{sessionId}") @Produces("application/json") public String getLoginFromSessionId(@PathParam("sessionId") String sessionId) { if (sessionId != null && sessionStore.exists(sessionId)) { return sessionStore.get(sessionId).getUserName(); } return ""; } /** * Login to the scheduler using a multipart form can be used either by * submitting 2 fields ({@code username} and {@code password}) or by sending * a credential file with field name {@code credential}. * * @return the session id associated to this new connection. * @throws KeyException * @throws LoginException * @throws SchedulerRestException */ @Override @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("login") @Produces("application/json") public String loginWithCredential(@MultipartForm LoginForm multipart) throws LoginException, KeyException, SchedulerRestException { try { Session session; if (multipart.getCredential() != null) { Credentials credentials; try { session = sessionStore.createUnnamedSession(); credentials = Credentials.getCredentials(multipart.getCredential()); session.connectToScheduler(credentials); } catch (IOException e) { throw new LoginException(e.getMessage()); } } else { if ((multipart.getUsername() == null) || (multipart.getPassword() == null)) { throw new LoginException("empty login/password"); } session = sessionStore.create(multipart.getUsername()); CredData credData = new CredData(CredData.parseLogin(multipart.getUsername()), CredData.parseDomain(multipart.getUsername()), multipart.getPassword(), multipart.getSshKey()); session.connectToScheduler(credData); } return session.getSessionId(); } catch (PermissionException e) { throw new SchedulerRestException(e); } catch (ActiveObjectCreationException e) { throw new SchedulerRestException(e); } catch (SchedulerException e) { throw new SchedulerRestException(e); } catch (NodeException e) { throw new SchedulerRestException(e); } } /** * returns statistics about the scheduler * * @param sessionId * the session id associated to this new connection * @return a string containing the statistics * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @GET @Path("stats") @Produces("application/json") public Map<String, String> getStatistics(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { SchedulerProxyUserInterface s = checkAccess(sessionId, "stats"); return s.getMappedInfo("ProActiveScheduler:name=RuntimeData"); } /** * returns a string containing some data regarding the user's account * * @param sessionId * the session id associated to this new connection * @return a string containing some data regarding the user's account * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @GET @Path("stats/myaccount") @Produces("application/json") public Map<String, String> getStatisticsOnMyAccount(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { SchedulerProxyUserInterface s = checkAccess(sessionId, "stats/myaccount"); return s.getMappedInfo("ProActiveScheduler:name=MyAccount"); } /** * Users currently connected to the scheduler * * @param sessionId * the session id associated to this new connection\ * @return list of users * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @GET @GZIP @Path("users") @Produces("application/json") public List<SchedulerUserData> getUsers(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "users"); return map(s.getUsers(), SchedulerUserData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /** * Users having jobs in the scheduler * * @param sessionId * the session id associated to this new connection\ * @return list of users * @throws NotConnectedRestException * @throws PermissionRestException */ @Override @GET @GZIP @Path("userswithjobs") @Produces("application/json") public List<SchedulerUserData> getUsersWithJobs(@HeaderParam("sessionid") final String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId, "userswithjobs"); return map(s.getUsersWithJobs(), SchedulerUserData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } private static <T> List<T> map(List<?> toMaps, Class<T> type) { List<T> result = new ArrayList<>(toMaps.size()); for (Object toMap : toMaps) { result.add(mapper.map(toMap, type)); } return result; } /** * returns the version of the rest api * * @return returns the version of the rest api */ @GET @Path("version") public String getVersion() { return "{ " + "\"scheduler\" : \"" + SchedulerStateRest.class.getPackage().getSpecificationVersion() + "\", " + "\"rest\" : \"" + SchedulerStateRest.class.getPackage().getImplementationVersion() + "\"" + "}"; } /** * generates a credential file from user provided credentials * * @return the credential file generated by the scheduler * @throws LoginException * @throws SchedulerRestException */ @Override @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("createcredential") @Produces("*/*") public byte[] getCreateCredential(@MultipartForm LoginForm multipart) throws LoginException, SchedulerRestException { try { String url = PortalConfiguration.SCHEDULER_URL.getValueAsString(); SchedulerAuthenticationInterface auth = SchedulerConnection.join(url); PublicKey pubKey = auth.getPublicKey(); try { Credentials cred = Credentials.createCredentials(new CredData(CredData.parseLogin(multipart.getUsername()), CredData.parseDomain(multipart.getUsername()), multipart.getPassword(), multipart.getSshKey()), pubKey); return cred.getBase64(); } catch (KeyException e) { throw new SchedulerRestException(e); } } catch (ConnectionException e) { throw new SchedulerRestException(e); } } @GET @Path("usage/myaccount") @Produces("application/json") @Override public List<JobUsageData> getUsageOnMyAccount(@HeaderParam("sessionid") String sessionId, @QueryParam("startdate") @DateFormatter.DateFormat() Date startDate, @QueryParam("enddate") @DateFormatter.DateFormat() Date endDate) throws NotConnectedRestException, PermissionRestException { try { Scheduler scheduler = checkAccess(sessionId); return map(scheduler.getMyAccountUsage(startDate, endDate), JobUsageData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } @GET @Path("usage/account") @Produces("application/json") @Override public List<JobUsageData> getUsageOnAccount(@HeaderParam("sessionid") String sessionId, @QueryParam("user") String user, @QueryParam("startdate") @DateFormatter.DateFormat() Date startDate, @QueryParam("enddate") @DateFormatter.DateFormat() Date endDate) throws NotConnectedRestException, PermissionRestException { try { Scheduler scheduler = checkAccess(sessionId); return map(scheduler.getAccountUsage(user, startDate, endDate), JobUsageData.class); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } @GET @Path("/userspace") @Produces("application/json") @Override public List<String> userspaceURIs(@HeaderParam("sessionid") String sessionId) throws NotConnectedRestException, PermissionRestException { SchedulerProxyUserInterface proxy = checkAccess(sessionId); try { return proxy.getUserSpaceURIs(); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } @GET @Path("/globalspace") @Produces("application/json") @Override public List<String> globalspaceURIs(@HeaderParam("sessionid") String sessionId) throws NotConnectedRestException, PermissionRestException { SchedulerProxyUserInterface proxy = checkAccess(sessionId); try { return proxy.getGlobalSpaceURIs(); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } @Override public JobValidationData validate(PathSegment pathSegment, MultipartFormDataInput multipart) { File tmpFile = null; try { Map<String, List<InputPart>> formDataMap = multipart.getFormDataMap(); String name = formDataMap.keySet().iterator().next(); InputPart part1 = formDataMap.get(name).get(0); InputStream is = part1.getBody(new GenericType<InputStream>() { }); tmpFile = File.createTempFile("valid-job", "d"); IOUtils.copy(is, new FileOutputStream(tmpFile)); Map<String, String> jobVariables = workflowVariablesTransformer.getWorkflowVariablesFromPathSegment(pathSegment); return jobValidator.validateJobDescriptor(tmpFile, jobVariables); } catch (IOException e) { JobValidationData validation = new JobValidationData(); validation.setErrorMessage("Cannot read from the job validation request."); validation.setStackTrace(getStackTrace(e)); return validation; } finally { if (tmpFile != null) { tmpFile.delete(); } } } @Override public JobValidationData validateFromUrl(String sessionId, String url, PathSegment pathSegment) throws NotConnectedRestException { File tmpWorkflowFile = null; try { checkAccess(sessionId); String jobXml = downloadWorkflowContent(sessionId, url); tmpWorkflowFile = File.createTempFile("job", "d"); IOUtils.write(jobXml, new FileOutputStream(tmpWorkflowFile)); Map<String, String> jobVariables = workflowVariablesTransformer.getWorkflowVariablesFromPathSegment(pathSegment); return jobValidator.validateJobDescriptor(tmpWorkflowFile, jobVariables); } catch (JobCreationRestException | IOException e) { JobValidationData validation = new JobValidationData(); validation.setErrorMessage("Error while reading workflow at url: " + url); validation.setStackTrace(getStackTrace(e)); return validation; } finally { FileUtils.deleteQuietly(tmpWorkflowFile); } } @Override public void putThirdPartyCredential(String sessionId, String key, String value) throws NotConnectedRestException, PermissionRestException, SchedulerRestException { try { Scheduler s = checkAccess(sessionId); s.putThirdPartyCredential(key, value); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (KeyException e) { throw new SchedulerRestException(e); } } @Override public void removeThirdPartyCredential(String sessionId, String key) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId); s.removeThirdPartyCredential(key); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } @Override public Set<String> thirdPartyCredentialsKeySet(String sessionId) throws NotConnectedRestException, PermissionRestException { try { Scheduler s = checkAccess(sessionId); return s.thirdPartyCredentialsKeySet(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } /* * Atmosphere 2.0 framework based implementation of Scheduler Eventing mechanism for REST * clients. It is configured to use WebSocket as the underneath protocol between the client and * the server. */ /** * Initialize WebSocket based communication channel between the client and * the server. */ @GET @Path("/events") public String subscribe(@Context HttpServletRequest req, @HeaderParam("sessionid") String sessionId) throws NotConnectedRestException { checkAccess(sessionId); HttpSession session = checkNotNull(req.getSession(), "HTTP session object is null. HTTP session support is requried for REST Scheduler eventing."); AtmosphereResource atmosphereResource = checkNotNull((AtmosphereResource) req.getAttribute(AtmosphereResource.class.getName()), "No AtmosphereResource is attached with current request."); // use session id as the 'topic' (or 'id') of the broadcaster session.setAttribute(ATM_BROADCASTER_ID, sessionId); session.setAttribute(ATM_RESOURCE_ID, atmosphereResource.uuid()); Broadcaster broadcaster = lookupBroadcaster(sessionId, true); if (broadcaster != null) { atmosphereResource.setBroadcaster(broadcaster).suspend(); } return null; } /** * Accepts an {@link EventSubscription} instance which specifies the types * of SchedulerEvents which interest the client. When such Scheduler event * occurs, it will be communicated to the client in the form of * {@link EventNotification} utilizing the WebSocket channel initialized * previously. */ @POST @Path("/events") @Produces("application/json") public EventNotification publish(@Context HttpServletRequest req, EventSubscription subscription) throws NotConnectedRestException, PermissionRestException { HttpSession session = req.getSession(); String broadcasterId = (String) session.getAttribute(ATM_BROADCASTER_ID); final SchedulerProxyUserInterface scheduler = checkAccess(broadcasterId); SchedulerEventBroadcaster eventListener = new SchedulerEventBroadcaster(broadcasterId); try { final SchedulerEventBroadcaster activedEventListener = PAActiveObject.turnActive(eventListener); scheduler.addEventListener(activedEventListener, subscription.isMyEventsOnly(), EventUtil.toSchedulerEvents(subscription.getEvents())); AtmosphereResource atmResource = getAtmosphereResourceFactory().find((String) session.getAttribute(ATM_RESOURCE_ID)); atmResource.addEventListener(new WebSocketEventListenerAdapter() { @Override public void onDisconnect(@SuppressWarnings("rawtypes") WebSocketEvent event) { try { logger.info("#### websocket disconnected remove listener ####"); scheduler.removeEventListener(); } catch (Exception e) { logger.error(e); } PAActiveObject.terminateActiveObject(activedEventListener, true); } }); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (ActiveObjectCreationException | NodeException e) { throw new RuntimeException(e); } return new EventNotification(EventNotification.Action.NONE, null, null); } private AtmosphereResourceFactory getAtmosphereResourceFactory() { return ((AtmosphereResource) httpServletRequest.getAttribute("org.atmosphere.cpr.AtmosphereResource")).getAtmosphereConfig() .resourcesFactory(); } private Broadcaster lookupBroadcaster(String topic, boolean createNew) { AtmosphereResource atmosphereResource = (AtmosphereResource) httpServletRequest.getAttribute("org.atmosphere.cpr.AtmosphereResource"); return atmosphereResource.getAtmosphereConfig().getBroadcasterFactory().lookup(topic, createNew); } @GET @Path("/") public Response index() throws URISyntaxException { return Response.seeOther(new URI("doc/jaxrsdocs/scheduler/index.html")).build(); } private String downloadWorkflowContent(String sessionId, String workflowUrl) throws JobCreationRestException, IOException { if (StringUtils.isBlank(workflowUrl)) throw new JobCreationRestException("Cannot create workflow without url"); HttpResourceDownloader httpResourceDownloader = HttpResourceDownloader.getInstance(); return httpResourceDownloader.getResource(sessionId, workflowUrl, String.class); } protected static Map<String, String> createSortableTaskAttrMap() { HashMap<String, String> sortableTaskAttrMap = new HashMap<>(13); sortableTaskAttrMap.put("id", "id.taskId"); sortableTaskAttrMap.put("status", "taskStatus"); sortableTaskAttrMap.put("name", "taskName"); sortableTaskAttrMap.put("tag", "tag"); sortableTaskAttrMap.put("jobId", "jobData.id"); sortableTaskAttrMap.put("jobName", "jobData.jobName"); sortableTaskAttrMap.put("execDuration", "executionDuration"); sortableTaskAttrMap.put("nodeCount", "parallelEnvNodesNumber"); sortableTaskAttrMap.put("executions", "numberOfExecutionLeft"); sortableTaskAttrMap.put("nodeFailure", "numberOfExecutionOnFailureLeft"); sortableTaskAttrMap.put("host", "executionHostName"); sortableTaskAttrMap.put("startTime", "startTime"); sortableTaskAttrMap.put("finishedTime", "finishedTime"); sortableTaskAttrMap.put("description", "description"); sortableTaskAttrMap.put("scheduledAt", "scheduledTime"); return sortableTaskAttrMap; } @Override public RestPage<String> getTaskIds(String sessionId, long from, long to, boolean mytasks, boolean running, boolean pending, boolean finished, int offset, int limit) throws NotConnectedRestException, PermissionRestException { return getTaskIdsByTag(sessionId, null, from, to, mytasks, running, pending, finished, offset, limit); } @Override public RestPage<String> getTaskIdsByTag(String sessionId, String taskTag, long from, long to, boolean mytasks, boolean running, boolean pending, boolean finished, int offset, int limit) throws NotConnectedRestException, PermissionRestException { Scheduler s = checkAccess(sessionId, "tasks"); PageBoundaries boundaries = Pagination.getTasksPageBoundaries(offset, limit, TASKS_PAGE_SIZE); Page<TaskId> page = null; try { page = s.getTaskIds(taskTag, from, to, mytasks, running, pending, finished, boundaries.getOffset(), boundaries.getLimit()); List<TaskId> taskIds = page.getList(); List<String> taskNames = new ArrayList<>(taskIds.size()); for (TaskId taskId : taskIds) { taskNames.add(taskId.getReadableName()); } return new RestPage<String>(taskNames, page.getSize()); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } @Override public RestPage<TaskStateData> getTaskStates(String sessionId, long from, long to, boolean mytasks, boolean running, boolean pending, boolean finished, int offset, int limit, SortSpecifierContainer sortParams) throws NotConnectedRestException, PermissionRestException { return getTaskStatesByTag(sessionId, null, from, to, mytasks, running, pending, finished, offset, limit, mapToDBNamespace(sortParams)); } @Override public RestPage<TaskStateData> getTaskStatesByTag(String sessionId, String taskTag, long from, long to, boolean mytasks, boolean running, boolean pending, boolean finished, int offset, int limit, SortSpecifierContainer sortParams) throws NotConnectedRestException, PermissionRestException { Scheduler s = checkAccess(sessionId, "tasks/tag/" + taskTag); PageBoundaries boundaries = Pagination.getTasksPageBoundaries(offset, limit, TASKS_PAGE_SIZE); Page<TaskState> page = null; // if that method is called directly from REST without any sorting // parameters // sortParams will be null if (sortParams == null) { sortParams = new SortSpecifierContainer(); } try { page = s.getTaskStates(taskTag, from, to, mytasks, running, pending, finished, boundaries.getOffset(), boundaries.getLimit(), sortParams); List<TaskStateData> tasks = map(page.getList(), TaskStateData.class); return new RestPage<TaskStateData>(tasks, page.getSize()); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } } /** * Translates the tasks attributes names that are used to sort the result * For example the task status is called `status` client-side, it is * represented by `taskStatus` in the DB * * @param sortParams * The sort parameters using the client-side namespace * @return the sort parameters using the DB namespace */ private SortSpecifierContainer mapToDBNamespace(SortSpecifierContainer sortParams) { SortSpecifierContainer filteredSorts = new SortSpecifierContainer(); if (sortParams != null) { for (SortSpecifierContainer.SortSpecifierItem i : sortParams.getSortParameters()) { if (sortableTaskAttrMap.containsKey(i.getField())) { filteredSorts.add(sortableTaskAttrMap.get(i.getField()), i.getOrder()); } } } return filteredSorts; } /** * Change the START_AT generic information at job level and reset the * scheduledAt at task level * * @param sessionId * current session * @param jobId * id of the job that needs to be updated * @param startAt * its value should be ISO 8601 compliant * @throws NotConnectedRestException * @throws UnknownJobRestException * @throws PermissionRestException */ @Override @PUT @Path("jobs/{jobid}/startat/{startAt}") @Produces("application/json") public boolean changeStartAt(@HeaderParam("sessionid") final String sessionId, @PathParam("jobid") final String jobId, @PathParam("startAt") final String startAt) throws NotConnectedRestException, UnknownJobRestException, PermissionRestException { try { final Scheduler s = checkAccess(sessionId, "POST jobs/" + jobId + "/startat/" + startAt); return s.changeStartAt(JobIdImpl.makeJobId(jobId), startAt); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } @POST @Path("jobs") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public JobIdData copyAndResubmitWithGeneralInfo(@HeaderParam("sessionid") String sessionId, @QueryParam("jobid") final String jobId, Map<String, String> generalInformation) throws NotConnectedRestException, PermissionRestException, UnknownJobRestException, JobCreationRestException, SubmissionClosedRestException { final Scheduler s = checkAccess(sessionId, "POST jobs?jobid=" + jobId); try { s.copyJobAndResubmitWithGeneralInfo(JobIdImpl.makeJobId(jobId), generalInformation); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } catch (UnknownJobException e) { throw new UnknownJobRestException(e); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (SubmissionClosedException e) { throw new SubmissionClosedRestException(e); } catch (JobCreationException e) { throw new JobCreationRestException(e); } return null; } @GET @Override @Path("configuration/portal") @Produces("application/json") public Map<Object, Object> getPortalConfiguration(@HeaderParam("sessionid") String sessionId) throws NotConnectedRestException, PermissionRestException { try { final Scheduler s = checkAccess(sessionId, "GET configuration/portal"); return s.getPortalConfiguration(); } catch (PermissionException e) { throw new PermissionRestException(e); } catch (NotConnectedException e) { throw new NotConnectedRestException(e); } } }