/* Copyright 2014 MITRE Corporation * * Licensed 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. */ package org.mitre.provenance.services; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.mitre.provenance.PLUSException; import org.mitre.provenance.dag.ViewedCollection; import org.mitre.provenance.db.neo4j.Neo4JPLUSObjectFactory; import org.mitre.provenance.db.neo4j.Neo4JStorage; import org.mitre.provenance.plusobject.PLUSObject; import org.mitre.provenance.plusobject.PLUSSerializer; import org.mitre.provenance.plusobject.ProvenanceCollection; import org.mitre.provenance.plusobject.json.JSONConverter; import org.mitre.provenance.user.User; import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.TransactionFailureException; import com.google.gson.GsonBuilder; import com.sun.syndication.feed.synd.SyndFeed; import com.sun.syndication.io.FeedException; import com.sun.syndication.io.SyndFeedOutput; /** * This class provides utility methods as helpers for other service implementations. * @author moxious */ public class ServiceUtility { public static User getUser(HttpServletRequest req) { Object o = req.getSession().getAttribute("plus_user"); if(o instanceof User) return (User)o; // TODO return User.DEFAULT_USER_GOD; } // End getUser /** * Execute a simple cypher query that returns *ONLY* a list of nodes, and craft a response based on the query results. * @param req the request object * @param cypherQuery the query to execute * @param nodeColumn the name of the column that the query will result in, which contains the node information * @return a JSON response of the query results. * @throws PLUSException */ public static Response OK_ExecuteQuery(HttpServletRequest req, String cypherQuery, String nodeColumn) throws PLUSException { ViewedCollection col = new ViewedCollection(ServiceUtility.getUser(req)); try (Transaction tx = Neo4JStorage.beginTx()) { ExecutionResult result = Neo4JStorage.execute(cypherQuery); Iterator<Node> nodes = result.columnAs(nodeColumn); while(nodes.hasNext()) { try { PLUSObject obj = Neo4JPLUSObjectFactory.newObject(nodes.next()); if(obj != null) col.addNode(obj); } catch (PLUSException e) { e.printStackTrace(); return ServiceUtility.ERROR(e.getMessage()); } } tx.success(); } catch(TransactionFailureException exc) { // TODO // Again this is bad, but it's a workaround. // IGNORE exception. // exc.printStackTrace(); // return ServiceUtility.ERROR(exc.getMessage()); } return ServiceUtility.OK(col); } public static Response OK(Boolean b) { return Response.ok(new GsonBuilder().setPrettyPrinting().create().toJson(b), MediaType.APPLICATION_JSON).build(); } public static Response OK(Map<String,Object> map) { return Response.ok(new GsonBuilder().setPrettyPrinting().create().toJson(map), MediaType.APPLICATION_JSON).build(); } public static Response OK(List<?> list) { String json = new GsonBuilder().setPrettyPrinting().create().toJson(list); return Response.ok(json, MediaType.APPLICATION_JSON).build(); } /** * Examine the "format" parameter, and the "Accept" header to guess at which representation is best for return. * Options:<br/> * text/turtle or format=ttl: PROV-TTL<br/> * application/rdf+xml or format=rdf: PROV-RDF<br/> * application/provenance+xml or format=xml: PROV-XML<br/> * json or format=json: D3 JSON<br/> * Default: D3 JSON * @param req an original request * @return suggested format for the response to a given request */ public static PLUSSerializer.Format suggestFormat(HttpServletRequest req) { String acceptedTypes = req.getHeader("Accept"); if(acceptedTypes == null) acceptedTypes = ""; String format = req.getParameter("format"); if(format == null) format = ""; boolean acceptsTTL = acceptedTypes.contains("text/turtle") || "ttl".equals(format) || "prov-ttl".equals(format); boolean acceptsRDF = acceptedTypes.contains("application/rdf+xml") || "rdf".equals(format) || "prov-rdf".equals(format); boolean acceptsXML = acceptedTypes.contains("application/provenance+xml") || "xml".equals(format) || "prov-xml".equals(format); boolean acceptsJSON = acceptedTypes.contains("json") || "json".equals(format); if(acceptsJSON) return PLUSSerializer.Format.D3_JSON; if(acceptsRDF) return PLUSSerializer.Format.PROV_RDF; if(acceptsXML) return PLUSSerializer.Format.PROV_XML; if(acceptsTTL) return PLUSSerializer.Format.PROV_TTL; return PLUSSerializer.Format.D3_JSON; } // End suggestFormat public static MediaType formatToMediaType(PLUSSerializer.Format fmt) { switch(fmt) { case PROV_XML: return new MediaType("application", "provenance+xml"); case PROV_TTL: return new MediaType("application", "x-turtle"); case PROV_RDF: return new MediaType("application", "rdf+xml"); case D3_JSON: default: return MediaType.APPLICATION_JSON_TYPE; } } /** * Convenience function for returning HTTP OK response, with variable representation format depending on user request. * @param col the collection * @param req the request * @return an OK response containing a serialized collection */ public static Response OK(ProvenanceCollection col, HttpServletRequest req) { PLUSSerializer.Format fmt = suggestFormat(req); PLUSSerializer serializer = new PLUSSerializer(); try { String data = serializer.serialize(col, fmt); MediaType responseType = formatToMediaType(fmt); return Response.ok(data, responseType).build(); } catch(Exception exc) { exc.printStackTrace(); return ERROR(exc.getMessage()); } } // End OK /** * Convenience function for returning an HTTP OK response, with the D3 formatted JSON results from a * provenance collection. If you want the response type to depend on the paramters of the request, then see alternative methods. * @param col * @return an application/json response containing D3-JSON * @see ServiceUtility#OK(ProvenanceCollection, HttpServletRequest) */ public static Response OK(ProvenanceCollection col) { return Response.ok(JSONConverter.provenanceCollectionToD3Json(col), MediaType.APPLICATION_JSON).build(); } /** * Convenience function for serializing a feed as rss/xml and returning an OK response. * @param feed * @return an OK response containing an RSS/XML feed. * @throws FeedException */ public static Response OK(SyndFeed feed) throws FeedException { return Response.ok(new SyndFeedOutput().outputString(feed), "application/rss+xml").build(); } /** * Convenience function for returning an internal server error response, with a message. * @param msg * @return an INTERNAL_SERVER_ERROR response containing a message */ public static Response ERROR(String msg) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } /** * Indicate that access to an item is forbidden. * @param msg * @return a FORBIDDEN response with a given message */ public static Response FORBIDDEN(String msg) { return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } /** * Convenience function for returning an HTTP not found response, with a message. * @param msg * @return a NOT_FOUND response containing a given message */ public static Response NOT_FOUND(String msg) { return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); } /** * Convenience function for returning an HTTP bad request response, with a message. * @param msg * @return a BAD_REQUEST response containing a given message */ public static Response BAD_REQUEST(String msg) { return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } }