package org.cytoscape.rest.internal.commands.resources; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Singleton; import javax.validation.constraints.NotNull; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.cytoscape.command.AvailableCommands; import org.cytoscape.command.CommandExecutorTaskFactory; import org.cytoscape.rest.internal.commands.handlers.MessageHandler; import org.cytoscape.rest.internal.commands.handlers.TextHTMLHandler; import org.cytoscape.rest.internal.commands.handlers.TextPlainHandler; import org.cytoscape.work.FinishStatus; import org.cytoscape.work.ObservableTask; import org.cytoscape.work.SynchronousTaskManager; import org.cytoscape.work.TaskObserver; import org.ops4j.pax.logging.spi.PaxAppender; import org.ops4j.pax.logging.spi.PaxLevel; import org.ops4j.pax.logging.spi.PaxLoggingEvent; /** * * JAX-RS Resource for all Command-related API * * */ @Singleton @Path("/v1/commands") public class CommandResource implements PaxAppender, TaskObserver { @Context @NotNull private AvailableCommands available; @Context @NotNull private CommandExecutorTaskFactory ceTaskFactory; @Context @NotNull private SynchronousTaskManager<?> taskManager; private CustomFailureException taskException; private MessageHandler messageHandler; private boolean processingCommand = false; /** * Method handling HTTP GET requests to enumerate all namespaces. The * returned list will be sent to the client as "text/plain" media type. * * @return String that will be returned as a text/plain response. */ @GET @Path("/") @Produces(MediaType.TEXT_PLAIN) public String enumerateNamespaces() { final MessageHandler handler = new TextPlainHandler(); final List<String> namespaces = available.getNamespaces(); handler.appendCommand("Available namespaces:"); for (final String namespace : namespaces) { handler.appendMessage(" " + namespace); } return handler.getMessages(); } /** * Method handling HTTP GET requests to enumerate all namespaces. The * returned list will be sent to the client as "text/html" media type. * * @return String that will be returned as a text/html response. */ @GET @Path("/") @Produces(MediaType.TEXT_HTML) public String enumerateNamespacesHtml() { final MessageHandler handler = new TextHTMLHandler(); final List<String> namespaces = available.getNamespaces(); handler.appendCommand("Available namespaces:"); for (final String namespace : namespaces) { handler.appendMessage(namespace); } return handler.getMessages(); } /** * Method to enumerate all commands for a given namespace * * @param namespace * @return list of commands as text/plain */ @GET @Path("/{namespace}") @Produces(MediaType.TEXT_PLAIN) public String enumerateCommands(@PathParam("namespace") String namespace) { final MessageHandler handler = new TextPlainHandler(); final List<String> commands = available.getCommands(namespace); if (commands == null || commands.size() == 0) throw new WebApplicationException(404); handler.appendCommand("Available commands for '" + namespace + "':"); for (final String command : commands) { handler.appendMessage(" " + command); } return handler.getMessages(); } /** * Method to enumerate all commands for a given namespace * * @param namespace * @return list of commands as text/html */ @GET @Path("/{namespace}") @Produces(MediaType.TEXT_HTML) public String enumerateHTMLCommands(@PathParam("namespace") String namespace) { final MessageHandler handler = new TextHTMLHandler(); final List<String> commands = available.getCommands(namespace); if (commands == null || commands.size() == 0) throw new WebApplicationException(404); handler.appendCommand("Available commands for '" + namespace + "':"); for (final String command : commands) { handler.appendMessage(" " + command); } return handler.getMessages(); } /** * Method to enumerate all arguments for a given namespace and command or * execute a namespace and command if query strings are provided * * @param namespace * @param command * @param uriInfo * this provides access to the query strings * @return list of arguments as text/plain or the results of executing the * command */ @GET @Path("/{namespace}/{command}") @Produces(MediaType.TEXT_PLAIN) public String handleCommand(@PathParam("namespace") String namespace, @PathParam("command") String command, @Context UriInfo uriInfo) { final MessageHandler handler = new TextPlainHandler(); final MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters(true); return handleCommand(namespace, command, queryParameters, handler); } /** * Method to enumerate all arguments for a given namespace and command or * execute a namespace and command if query strings are provided * * @param namespace * @param command * @param uriInfo * this provides access to the query strings * @return list of arguments as text/html or the results of executing the * command */ @GET @Path("/{namespace}/{command}") @Produces(MediaType.TEXT_HTML) public String handleHTMLCommand(@PathParam("namespace") String namespace, @PathParam("command") String command, @Context UriInfo uriInfo) { final MessageHandler handler = new TextHTMLHandler(); final MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters(true); return handleCommand(namespace, command, queryParameters, handler); } private final String handleCommand(final String namespace, final String command, final MultivaluedMap<String, String> queryParameters, final MessageHandler handler) throws WebApplicationException { final List<String> args = available.getArguments(namespace, command); if ((queryParameters != null && queryParameters.size() > 0) || (args == null || args.size() == 0)) { // Execute! return executeCommand(namespace, command, queryParameters, handler); } handler.appendCommand("Available arguments for '" + namespace + " " + command + "':"); for (final String arg : args) { handler.appendMessage(" " + arg); } return handler.getMessages(); } private final String executeCommand( final String namespace, final String command, final MultivaluedMap<String, String> args, final MessageHandler handler) throws WebApplicationException { final List<String> commands = available.getCommands(namespace); if (commands == null || commands.size() == 0) { throw new CustomNotFoundException("Error: no such namespace: '" + namespace + "'"); } boolean nocom = true; for (String com : commands) { if (com.equalsIgnoreCase(command)) { nocom = false; break; } } if (nocom) { throw new CustomNotFoundException("Error: no such command: '" + command + "'"); } List<String> argList = available.getArguments(namespace, command); Map<String, Object> modifiedSettings = new HashMap<String, Object>(); for (String inputArg : args.keySet()) { boolean found = false; for (String arg : argList) { String[] bareArg = arg.split("="); if (bareArg[0].equalsIgnoreCase(inputArg)) { found = true; modifiedSettings.put(bareArg[0], stripQuotes(args.getFirst(inputArg))); break; } } if (!found) { throw new CustomNotFoundException( "Error: can't find argument '" + inputArg + "'"); } } processingCommand = true; messageHandler = handler; taskException = null; taskManager.execute(ceTaskFactory.createTaskIterator(namespace, command, modifiedSettings, this), this); String messages = messageHandler.getMessages(); processingCommand = false; if (taskException != null) throw taskException; return messages; } public void doAppend(PaxLoggingEvent event) { System.out .println(event.getLevel().toInt() + ": " + event.getMessage()); // Get prefix // Handle levels if (!processingCommand) { return; } PaxLevel level = event.getLevel(); if (level.toInt() == 40000) messageHandler.appendError(event.getMessage()); else if (level.toInt() == 30000) messageHandler.appendWarning(event.getMessage()); else messageHandler.appendMessage(event.getMessage()); } ////////////////// For Observable Task ////////////////////////// @Override public void taskFinished(ObservableTask t) { final Object res = t.getResults(String.class); if (res != null) messageHandler.appendResult(res); } @Override public void allFinished(FinishStatus status) { if (status.getType().equals(FinishStatus.Type.SUCCEEDED)) messageHandler.appendMessage("Finished"); else if (status.getType().equals(FinishStatus.Type.CANCELLED)) messageHandler.appendWarning("Cancelled by user"); else if (status.getType().equals(FinishStatus.Type.FAILED)) { if (status.getException() != null) { messageHandler.appendError("Failed: " + status.getException().getMessage()); taskException = new CustomFailureException("Failed: " + status.getException().getMessage()); } else { messageHandler.appendError("Failed"); taskException = new CustomFailureException(); } } } private final String stripQuotes(final String quotedString) { String tqString = quotedString.trim(); if (tqString.startsWith("\"") && tqString.endsWith("\"")) return tqString.substring(1, tqString.length() - 1); return tqString; } public class CustomNotFoundException extends WebApplicationException { public CustomNotFoundException() { super(404); } public CustomNotFoundException(String message) { super(Response.status(Response.Status.NOT_FOUND).entity(message) .type("text/plain").build()); } } public class CustomFailureException extends WebApplicationException { public CustomFailureException() { super(500); } public CustomFailureException(String message) { super(Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(message).type("text/plain").build()); } } }