package org.cytoscape.rest.internal.resource; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Set; import javax.validation.constraints.NotNull; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.NotFoundException; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.cytoscape.application.CyApplicationManager; import org.cytoscape.io.read.InputStreamTaskFactory; import org.cytoscape.io.write.CyNetworkViewWriterFactory; import org.cytoscape.io.write.CyWriter; import org.cytoscape.model.CyIdentifiable; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNetworkFactory; import org.cytoscape.model.CyNetworkManager; import org.cytoscape.model.CyNode; import org.cytoscape.model.CyRow; import org.cytoscape.model.CyTable; import org.cytoscape.model.CyTableManager; import org.cytoscape.model.subnetwork.CyRootNetworkManager; import org.cytoscape.property.CyProperty; import org.cytoscape.rest.internal.TaskFactoryManager; import org.cytoscape.rest.internal.CyActivator.WriterListener; import org.cytoscape.rest.internal.datamapper.MapperUtil; import org.cytoscape.rest.internal.reader.EdgeListReaderFactory; import org.cytoscape.rest.internal.serializer.GraphObjectSerializer; import org.cytoscape.rest.internal.task.HeadlessTaskMonitor; import org.cytoscape.task.create.NewNetworkSelectedNodesAndEdgesTaskFactory; import org.cytoscape.task.read.LoadNetworkURLTaskFactory; import org.cytoscape.view.model.CyNetworkView; import org.cytoscape.view.model.CyNetworkViewFactory; import org.cytoscape.view.model.CyNetworkViewManager; import org.cytoscape.view.model.VisualLexicon; import org.cytoscape.view.vizmap.VisualMappingManager; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; /** * Prepare services to be injected. * */ public abstract class AbstractResource { // TODO: do we need this level of version granularity? protected static final String API_VERSION = "v1"; protected static final String ERROR_TAG = "\"error\":"; /** * Create a informative error message instead of plain 500. * * @param errorMessage * @param ex * @param status * @return */ protected final WebApplicationException getError(final String errorMessage, final Exception ex, final Status status) { // This is necessary to avoid threading issues. final Exception cause = new Exception(ex.getMessage()); cause.setStackTrace(ex.getStackTrace()); final Exception wrapped = new IllegalStateException(errorMessage, cause); if (status == Response.Status.INTERNAL_SERVER_ERROR) { // Otherwise, 500. return new InternalServerErrorException(Response.status(status).type(MediaType.APPLICATION_JSON) .entity(wrapped).build()); } else { // All other types return new WebApplicationException(Response.status(status).type(MediaType.APPLICATION_JSON) .entity(wrapped).build()); } } @Context @NotNull protected CyApplicationManager applicationManager; @Context protected CyNetworkManager networkManager; @Context protected CyRootNetworkManager cyRootNetworkManager; @Context protected CyTableManager tableManager; @Context protected CyNetworkViewManager networkViewManager; @Context protected CyNetworkFactory networkFactory; @Context protected CyNetworkViewFactory networkViewFactory; @Context protected TaskFactoryManager tfManager; @Context protected InputStreamTaskFactory cytoscapeJsReaderFactory; @Context @NotNull protected VisualMappingManager vmm; @Context protected CyNetworkViewWriterFactory cytoscapeJsWriterFactory; @Context protected WriterListener vizmapWriterFactoryListener; @Context protected LoadNetworkURLTaskFactory loadNetworkURLTaskFactory; @Context protected CyProperty<?> props; @Context protected NewNetworkSelectedNodesAndEdgesTaskFactory newNetworkSelectedNodesAndEdgesTaskFactory; @Context protected EdgeListReaderFactory edgeListReaderFactory; protected final GraphObjectSerializer serializer; public AbstractResource() { this.serializer = new GraphObjectSerializer(); } protected final CyNetwork getCyNetwork(final Long id) { if (id == null) { throw new NotFoundException("SUID is null."); } final CyNetwork network = networkManager.getNetwork(id); if (network == null) { throw new NotFoundException("Could not find network with SUID: " + id); } return network; } protected final Collection<CyNetworkView> getCyNetworkViews(final Long id) { final Collection<CyNetworkView> views = networkViewManager.getNetworkViews(getCyNetwork(id)); if (views.isEmpty()) { throw new NotFoundException("No view is available for network with SUID: " + id); } return views; } protected final CyNode getNode(final CyNetwork network, final Long nodeId) { final CyNode node = network.getNode(nodeId); if (node == null) { throw new NotFoundException("Could not find node with SUID: " + nodeId); } return node; } protected final String getGraphObject(final CyNetwork network, final CyIdentifiable obj) { final CyRow row = network.getRow(obj); try { return this.serializer.serializeGraphObject(obj, row); } catch (IOException e) { throw getError("Could not serialize graph object with SUID: " + obj.getSUID(), e, Response.Status.INTERNAL_SERVER_ERROR); } } protected final String getNames(final Collection<String> names) throws IOException { final JsonFactory factory = new JsonFactory(); String result = null; ByteArrayOutputStream stream = new ByteArrayOutputStream(); JsonGenerator generator = null; generator = factory.createGenerator(stream); generator.writeStartArray(); for (final String name : names) { generator.writeString(name); } generator.writeEndArray(); generator.close(); result = stream.toString("UTF-8"); stream.close(); return result; } protected final String getNumberObjectString(final String fieldName, final Number value) { final JsonFactory factory = new JsonFactory(); String result = null; ByteArrayOutputStream stream = new ByteArrayOutputStream(); JsonGenerator generator = null; try { generator = factory.createGenerator(stream); generator.writeStartObject(); generator.writeFieldName(fieldName); generator.writeNumber(value.longValue()); generator.writeEndObject(); generator.close(); result = stream.toString("UTF-8"); stream.close(); } catch (IOException e) { throw getError("Could not serialize number: " + value, e, Response.Status.INTERNAL_SERVER_ERROR); } return result; } protected final Set<CyNetwork> getNetworksByQuery(final String query, final String column) { final Set<CyNetwork> networks = networkManager.getNetworkSet(); final Set<CyNetwork> matchedNetworks = new HashSet<>(); for (final CyNetwork network : networks) { // First, check shared table CyTable table = network.getDefaultNetworkTable(); if(table.getColumn(column) == null) { table = network.getTable(CyNetwork.class, CyNetwork.LOCAL_ATTRS); } // If column does not exists in the default table, check local one: if(table.getColumn(column) == null) { // Still not found? Give up and continue. // TODO: should we check hidden tables, too? continue; } final Object rawQuery = MapperUtil.getRawValue(query, table.getColumn(column).getType()); final Collection<CyRow> rows = table.getMatchingRows(column, rawQuery); if (rows.isEmpty() == false) { matchedNetworks.add(network); } } return matchedNetworks; } protected final String getNetworkString(final CyNetwork network) { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); CyWriter writer = cytoscapeJsWriterFactory.createWriter(stream, network); String jsonString = null; try { writer.run(new HeadlessTaskMonitor()); jsonString = stream.toString("UTF-8"); stream.close(); } catch (Exception e) { throw getError("Could not serialize network into JSON.", e, Response.Status.PRECONDITION_FAILED); } return jsonString; } protected final VisualLexicon getLexicon() { final Set<VisualLexicon> lexicon = vmm.getAllVisualLexicon(); if (lexicon.isEmpty()) { throw getError("Could not find visual lexicon.", new IllegalStateException(), Response.Status.INTERNAL_SERVER_ERROR); } // TODO: What happens if we have multiple renderer? return lexicon.iterator().next(); } }