/* Copyright (c) 2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.rest; import static org.locationtech.geogig.rest.Variants.JSON; import static org.locationtech.geogig.rest.Variants.XML; import static org.locationtech.geogig.rest.Variants.getVariantByExtension; import static org.locationtech.geogig.rest.repository.RESTUtils.getStringAttribute; import java.util.List; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.locationtech.geogig.rest.AsyncContext.AsyncCommand; import org.restlet.Context; import org.restlet.data.MediaType; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; import org.restlet.resource.Representation; import org.restlet.resource.Resource; import org.restlet.resource.Variant; import com.google.common.base.Optional; import com.google.common.base.Strings; /** * Resource for {@code /osm/tasks /tasksId[?params]} * <p> * Params: * <ul> * <li>If no taskId is given, returns a list of tasks, whether they're in a waiting, running, * finished, or failed status. Done tasks (either finished or failed) are automatically pruned after * 10 minutes. * <li>prune: boolean, whether to prune a finished task (requires a taskId) * <li>cancel: boolean, if true, an attempt to cancel the tasks given by {@code taskId} is made. * </ul> */ public class TaskStatusResource extends Resource { @Override public void init(Context context, Request request, Response response) { super.init(context, request, response); List<Variant> variants = getVariants(); variants.add(XML); variants.add(JSON); } @Override public Variant getPreferredVariant() { return getVariantByExtension(getRequest(), getVariants()).or(super.getPreferredVariant()); } @Override public Representation getRepresentation(Variant variant) { final Request request = getRequest(); final String taskId = getStringAttribute(request, "taskId"); final boolean prune = Boolean.valueOf(getRequest().getResourceRef().getQueryAsForm() .getFirstValue("prune")); final boolean cancel = Boolean.valueOf(getRequest().getResourceRef().getQueryAsForm() .getFirstValue("cancel")); final AsyncContext asyncContext = AsyncContext.get(); MediaType mediaType = variant.getMediaType(); final String rootPath = request.getRootRef().toString(); if (Strings.isNullOrEmpty(taskId)) { Iterable<AsyncCommand<? extends Object>> all = asyncContext.getAll(); return new TaskListResource(mediaType, rootPath, all); } Optional<AsyncCommand<?>> cmd; if (prune) { cmd = asyncContext.getAndPruneIfFinished(taskId); } else { cmd = asyncContext.get(taskId); } if (!cmd.isPresent()) { throw new RestletException("Task not found: " + taskId, Status.CLIENT_ERROR_NOT_FOUND); } AsyncCommand<?> command = cmd.get(); if (cancel) { command.tryCancel(); try { Thread.sleep(200); } catch (InterruptedException e) { // ignore } if (prune) { asyncContext.getAndPruneIfFinished(taskId); } } return Representations.newRepresentation(command, mediaType, rootPath); } private static class TaskListResource extends JettisonRepresentation { private Iterable<AsyncCommand<? extends Object>> all; public TaskListResource(MediaType mediaType, String baseURL, Iterable<AsyncCommand<? extends Object>> all) { super(mediaType, baseURL); this.all = all; } @Override protected void write(XMLStreamWriter w) throws XMLStreamException { w.writeStartElement("tasks"); for (AsyncCommand<? extends Object> c : all) { AsyncCommandRepresentation<?> rep = Representations.newRepresentation(c, getMediaType(), baseURL); rep.write(w); } w.writeEndElement(); } } }