blob: 65be798e8c45723c26e7625cb71d17146b72b744 [file] [log] [blame]
/*
*/
package org.taverna.server.master;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.Response.noContent;
import static javax.ws.rs.core.Response.ok;
import static javax.ws.rs.core.Response.status;
import static org.apache.commons.logging.LogFactory.getLog;
import static org.joda.time.format.ISODateTimeFormat.dateTime;
import static org.joda.time.format.ISODateTimeFormat.dateTimeParser;
import static org.taverna.server.master.common.Roles.SELF;
import static org.taverna.server.master.common.Roles.USER;
import static org.taverna.server.master.common.Status.Initialized;
import static org.taverna.server.master.common.Status.Operating;
import static org.taverna.server.master.utils.RestUtils.opt;
import java.util.Date;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBException;
import org.apache.commons.logging.Log;
import org.joda.time.DateTime;
import org.ogf.usage.JobUsageRecord;
import org.springframework.beans.factory.annotation.Required;
import org.taverna.server.master.api.RunBean;
import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Workflow;
import org.taverna.server.master.exceptions.BadStateChangeException;
import org.taverna.server.master.exceptions.FilesystemAccessException;
import org.taverna.server.master.exceptions.NoDirectoryEntryException;
import org.taverna.server.master.exceptions.NoListenerException;
import org.taverna.server.master.exceptions.NoUpdateException;
import org.taverna.server.master.exceptions.NotOwnerException;
import org.taverna.server.master.exceptions.OverloadedException;
import org.taverna.server.master.exceptions.UnknownRunException;
import org.taverna.server.master.interfaces.TavernaRun;
import org.taverna.server.master.interfaces.TavernaSecurityContext;
import org.taverna.server.master.rest.InteractionFeedREST;
import org.taverna.server.master.rest.TavernaServerInputREST;
import org.taverna.server.master.rest.TavernaServerListenersREST;
import org.taverna.server.master.rest.TavernaServerRunREST;
import org.taverna.server.master.rest.TavernaServerSecurityREST;
import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
import org.taverna.server.master.utils.InvocationCounter.CallCounted;
import org.taverna.server.port_description.OutputDescription;
/**
* RESTful interface to a single workflow run.
*
* @author Donal Fellows
*/
abstract class RunREST implements TavernaServerRunREST, RunBean {
private Log log = getLog("Taverna.Server.Webapp");
private String runName;
private TavernaRun run;
private TavernaServerSupport support;
private ContentsDescriptorBuilder cdBuilder;
@Override
@Required
public void setSupport(TavernaServerSupport support) {
this.support = support;
}
@Override
@Required
public void setCdBuilder(ContentsDescriptorBuilder cdBuilder) {
this.cdBuilder = cdBuilder;
}
@Override
public void setRunName(String runName) {
this.runName = runName;
}
@Override
public void setRun(TavernaRun run) {
this.run = run;
}
@Override
@CallCounted
@PerfLogged
public RunDescription getDescription(UriInfo ui) {
return new RunDescription(run, ui);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Response destroy() throws NoUpdateException {
try {
support.unregisterRun(runName, run);
} catch (UnknownRunException e) {
log.fatal("can't happen", e);
}
return noContent().build();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public TavernaServerListenersREST getListeners() {
return makeListenersInterface().connect(run);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public TavernaServerSecurityREST getSecurity() throws NotOwnerException {
TavernaSecurityContext secContext = run.getSecurityContext();
if (!support.getPrincipal().equals(secContext.getOwner()))
throw new NotOwnerException();
// context.getBean("run.security", run, secContext);
return makeSecurityInterface().connect(secContext, run);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getExpiryTime() {
return dateTime().print(new DateTime(run.getExpiry()));
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getCreateTime() {
return dateTime().print(new DateTime(run.getCreationTimestamp()));
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getFinishTime() {
Date f = run.getFinishTimestamp();
return f == null ? "" : dateTime().print(new DateTime(f));
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getStartTime() {
Date f = run.getStartTimestamp();
return f == null ? "" : dateTime().print(new DateTime(f));
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getStatus() {
return run.getStatus().toString();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Workflow getWorkflow() {
return run.getWorkflow();
}
@Override
@CallCounted
public String getMainProfileName() {
String name = run.getWorkflow().getMainProfileName();
return (name == null ? "" : name);
}
@Override
@CallCounted
public ProfileList getProfiles() {
return support.getProfileDescriptor(run.getWorkflow());
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed({ USER, SELF })
public DirectoryREST getWorkingDirectory() {
return makeDirectoryInterface().connect(run);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String setExpiryTime(String expiry) throws NoUpdateException,
IllegalArgumentException {
DateTime wanted = dateTimeParser().parseDateTime(expiry.trim());
Date achieved = support.updateExpiry(run, wanted.toDate());
return dateTime().print(new DateTime(achieved));
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Response setStatus(String status) throws NoUpdateException {
Status newStatus = Status.valueOf(status.trim());
support.permitUpdate(run);
if (newStatus == Operating && run.getStatus() == Initialized) {
if (!support.getAllowStartWorkflowRuns())
throw new OverloadedException();
String issue = run.setStatus(newStatus);
if (issue == null)
issue = "starting run...";
return status(202).entity(issue).type("text/plain").build();
}
run.setStatus(newStatus); // Ignore the result
return ok(run.getStatus().toString()).type("text/plain").build();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public TavernaServerInputREST getInputs(UriInfo ui) {
return makeInputInterface().connect(run, ui);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getOutputFile() {
String o = run.getOutputBaclavaFile();
return o == null ? "" : o;
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String setOutputFile(String filename) throws NoUpdateException,
FilesystemAccessException, BadStateChangeException {
support.permitUpdate(run);
if (filename != null && filename.length() == 0)
filename = null;
run.setOutputBaclavaFile(filename);
String o = run.getOutputBaclavaFile();
return o == null ? "" : o;
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public OutputDescription getOutputDescription(UriInfo ui)
throws BadStateChangeException, FilesystemAccessException,
NoDirectoryEntryException {
if (run.getStatus() == Initialized)
throw new BadStateChangeException(
"may not get output description in initial state");
return cdBuilder.makeOutputDescriptor(run, ui);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed({ USER, SELF })
public InteractionFeedREST getInteractionFeed() {
return makeInteractionFeed().connect(run);
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getName() {
return run.getName();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String setName(String name) throws NoUpdateException {
support.permitUpdate(run);
run.setName(name);
return run.getName();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getStdout() throws NoListenerException {
return support.getProperty(run, "io", "stdout");
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public String getStderr() throws NoListenerException {
return support.getProperty(run, "io", "stderr");
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Response getUsage() throws NoListenerException, JAXBException {
String ur = support.getProperty(run, "io", "usageRecord");
if (ur.isEmpty())
return noContent().build();
return ok(JobUsageRecord.unmarshal(ur), APPLICATION_XML).build();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Response getLogContents() {
FileConcatenation fc = support.getLogs(run);
if (fc.isEmpty())
return Response.noContent().build();
return Response.ok(fc, TEXT_PLAIN).build();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public Response getRunBundle() {
FileConcatenation fc = support.getProv(run);
if (fc.isEmpty())
return Response.status(404).entity("no provenance currently available").build();
return Response.ok(fc, "application/vnd.wf4ever.robundle+zip").build();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public boolean getGenerateProvenance() {
return run.getGenerateProvenance();
}
@Override
@CallCounted
@PerfLogged
@RolesAllowed(USER)
public boolean setGenerateProvenance(boolean newValue) throws NoUpdateException {
support.permitUpdate(run);
run.setGenerateProvenance(newValue);
return run.getGenerateProvenance();
}
/**
* Construct a RESTful interface to a run's filestore.
*
* @return The handle to the interface, as decorated by Spring.
*/
protected abstract DirectoryREST makeDirectoryInterface();
/**
* Construct a RESTful interface to a run's input descriptors.
*
* @return The handle to the interface, as decorated by Spring.
*/
protected abstract InputREST makeInputInterface();
/**
* Construct a RESTful interface to a run's listeners.
*
* @return The handle to the interface, as decorated by Spring.
*/
protected abstract ListenersREST makeListenersInterface();
/**
* Construct a RESTful interface to a run's security.
*
* @return The handle to the interface, as decorated by Spring.
*/
protected abstract RunSecurityREST makeSecurityInterface();
/**
* Construct a RESTful interface to a run's interaction feed.
*
* @return The handle to the interaface, as decorated by Spring.
*/
protected abstract InteractionFeed makeInteractionFeed();
@Override
@CallCounted
public Response runOptions() {
return opt();
}
@Override
@CallCounted
public Response workflowOptions() {
return opt();
}
@Override
@CallCounted
public Response profileOptions() {
return opt();
}
@Override
@CallCounted
public Response expiryOptions() {
return opt("PUT");
}
@Override
@CallCounted
public Response createTimeOptions() {
return opt();
}
@Override
@CallCounted
public Response startTimeOptions() {
return opt();
}
@Override
@CallCounted
public Response finishTimeOptions() {
return opt();
}
@Override
@CallCounted
public Response statusOptions() {
return opt("PUT");
}
@Override
@CallCounted
public Response outputOptions() {
return opt("PUT");
}
@Override
@CallCounted
public Response nameOptions() {
return opt("PUT");
}
@Override
@CallCounted
public Response stdoutOptions() {
return opt();
}
@Override
@CallCounted
public Response stderrOptions() {
return opt();
}
@Override
@CallCounted
public Response usageOptions() {
return opt();
}
@Override
@CallCounted
public Response logOptions() {
return opt();
}
@Override
@CallCounted
public Response runBundleOptions() {
return opt();
}
@Override
@CallCounted
public Response generateProvenanceOptions() {
return opt("PUT");
}
}