View Javadoc

    1   /**
    2    * Copyright (c) 2002-2011 "Neo Technology,"
    3    * Network Engine for Objects in Lund AB [http://neotechnology.com]
    4    *
    5    * This file is part of Neo4j.
    6    *
    7    * Neo4j is free software: you can redistribute it and/or modify
    8    * it under the terms of the GNU General Public License as published by
    9    * the Free Software Foundation, either version 3 of the License, or
    10   * (at your option) any later version.
    11   *
    12   * This program is distributed in the hope that it will be useful,
    13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15   * GNU General Public License for more details.
    16   *
    17   * You should have received a copy of the GNU General Public License
    18   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19   */
    20  package org.neo4j.server.rest.web;
    21
    22  import java.util.Collections;
    23  import java.util.LinkedHashSet;
    24  import java.util.Map;
    25
    26  import javax.ws.rs.*;
    27  import javax.ws.rs.core.Context;
    28  import javax.ws.rs.core.MediaType;
    29  import javax.ws.rs.core.Response;
    30  import javax.ws.rs.core.UriInfo;
    31
    32  import org.neo4j.graphdb.NotFoundException;
    33  import org.neo4j.server.database.Database;
    34  import org.neo4j.server.rest.domain.EndNodeNotFoundException;
    35  import org.neo4j.server.rest.domain.StartNodeNotFoundException;
    36  import org.neo4j.server.rest.domain.StartNodeSameAsEndNodeException;
    37  import org.neo4j.server.rest.domain.TraverserReturnType;
    38  import org.neo4j.server.rest.repr.BadInputException;
    39  import org.neo4j.server.rest.repr.InputFormat;
    40  import org.neo4j.server.rest.repr.OutputFormat;
    41  import org.neo4j.server.rest.repr.PropertiesRepresentation;
    42  import org.neo4j.server.rest.web.DatabaseActions.RelationshipDirection;
    43
    44  @Path("/")
    45  public class RestfulGraphDatabase {
    46      @SuppressWarnings("serial")
    47      public static class AmpersandSeparatedCollection extends LinkedHashSet<String> {
    48          public AmpersandSeparatedCollection(String path) {
    49              for (String e : path.split("&")) {
    50                  if (e.trim().length() > 0) {
    51                      add(e);
    52                  }
    53              }
    54          }
    55      }
    56
    57      private static final String PATH_NODES = "node";
    58      private static final String PATH_NODE = PATH_NODES + "/{nodeId}";
    59      private static final String PATH_NODE_PROPERTIES = PATH_NODE + "/properties";
    60      private static final String PATH_NODE_PROPERTY = PATH_NODE_PROPERTIES + "/{key}";
    61      private static final String PATH_NODE_RELATIONSHIPS = PATH_NODE + "/relationships";
    62      private static final String PATH_RELATIONSHIP = "relationship/{relationshipId}";
    63      private static final String PATH_NODE_RELATIONSHIPS_W_DIR = PATH_NODE_RELATIONSHIPS + "/{direction}";
    64      private static final String PATH_NODE_RELATIONSHIPS_W_DIR_N_TYPES = PATH_NODE_RELATIONSHIPS_W_DIR + "/{types}";
    65      private static final String PATH_RELATIONSHIP_PROPERTIES = PATH_RELATIONSHIP + "/properties";
    66      private static final String PATH_RELATIONSHIP_PROPERTY = PATH_RELATIONSHIP_PROPERTIES + "/{key}";
    67      private static final String PATH_NODE_TRAVERSE = PATH_NODE + "/traverse/{returnType}";
    68      private static final String PATH_NODE_PATH = PATH_NODE + "/path";
    69      private static final String PATH_NODE_PATHS = PATH_NODE + "/paths";
    70
    71      protected static final String PATH_NODE_INDEX = "index/node";
    72      protected static final String PATH_NAMED_NODE_INDEX = PATH_NODE_INDEX + "/{indexName}";
    73      protected static final String PATH_NODE_INDEX_GET = PATH_NAMED_NODE_INDEX + "/{key}/{value}";
    74      protected static final String PATH_NODE_INDEX_QUERY = PATH_NAMED_NODE_INDEX + "/{key}"; // http://localhost/db/data/index/node/foo?query=somelucenestuff
    75      protected static final String PATH_NODE_INDEX_ID = PATH_NODE_INDEX_GET + "/{id}";
    76      protected static final String PATH_NODE_INDEX_REMOVE_KEY = PATH_NAMED_NODE_INDEX + "/{key}/{id}";
    77      protected static final String PATH_NODE_INDEX_REMOVE = PATH_NAMED_NODE_INDEX + "/{id}";
    78
    79      protected static final String PATH_RELATIONSHIP_INDEX = "index/relationship";
    80      protected static final String PATH_NAMED_RELATIONSHIP_INDEX = PATH_RELATIONSHIP_INDEX + "/{indexName}";
    81      protected static final String PATH_RELATIONSHIP_INDEX_GET = PATH_NAMED_RELATIONSHIP_INDEX + "/{key}/{value}";
    82      protected static final String PATH_RELATIONSHIP_INDEX_QUERY = PATH_NAMED_RELATIONSHIP_INDEX + "/{key}";
    83      protected static final String PATH_RELATIONSHIP_INDEX_ID = PATH_RELATIONSHIP_INDEX_GET + "/{id}";
    84      protected static final String PATH_RELATIONSHIP_INDEX_REMOVE_KEY = PATH_NAMED_RELATIONSHIP_INDEX + "/{key}/{id}";
    85      protected static final String PATH_RELATIONSHIP_INDEX_REMOVE = PATH_NAMED_RELATIONSHIP_INDEX + "/{id}";
    86
    87      private final DatabaseActions server;
    88      private final OutputFormat output;
    89      private final InputFormat input;
    90
    91      public RestfulGraphDatabase(@Context UriInfo uriInfo, @Context Database database, @Context InputFormat input, @Context OutputFormat output) {
    92          this.input = input;
    93          this.output = output;
    94          this.server = new DatabaseActions(database);
    95      }
    96
    97      private static Response nothing() {
    98          return Response.noContent().build();
    99      }
    100
    101     private long extractNodeId(String uri) throws BadInputException {
    102         try {
    103             return Long.parseLong(uri.substring(uri.lastIndexOf("/") + 1));
    104         } catch (NumberFormatException ex) {
    105             throw new BadInputException(ex);
    106         } catch (NullPointerException ex) {
    107             throw new BadInputException(ex);
    108         }
    109     }
    110
    111     @GET
    112     public Response getRoot() {
    113         return output.ok(server.root());
    114     }
    115
    116     // Nodes
    117
    118     @POST
    119     @Path(PATH_NODES)
    120     public Response createNode(String body) {
    121         try {
    122             return output.created(server.createNode(input.readMap(body)));
    123         } catch (ArrayStoreException ase) {
    124             return generateBadRequestDueToMangledJsonResponse(body);
    125         } catch (BadInputException e) {
    126             return output.badRequest(e);
    127         }
    128     }
    129
    130     private Response generateBadRequestDueToMangledJsonResponse(String body) {
    131         return Response.status(400).type(MediaType.TEXT_PLAIN).entity("Invalid JSON array in POST body: " + body).build();
    132     }
    133
    134     @GET
    135     @Path(PATH_NODE)
    136     public Response getNode(@PathParam("nodeId") long nodeId) {
    137         try {
    138             return output.ok(server.getNode(nodeId));
    139         } catch (NodeNotFoundException e) {
    140             return output.notFound(e);
    141         }
    142     }
    143
    144     @DELETE
    145     @Path(PATH_NODE)
    146     public Response deleteNode(@PathParam("nodeId") long nodeId) {
    147         try {
    148             server.deleteNode(nodeId);
    149             return nothing();
    150         } catch (NodeNotFoundException e) {
    151             return output.notFound(e);
    152         } catch (OperationFailureException e) {
    153             return output.conflict(e);
    154         }
    155     }
    156
    157     // Node properties
    158
    159     @PUT
    160     @Path(PATH_NODE_PROPERTIES)
    161     public Response setAllNodeProperties(@PathParam("nodeId") long nodeId, String body) {
    162         try {
    163             server.setAllNodeProperties(nodeId, input.readMap(body));
    164         } catch (BadInputException e) {
    165             return output.badRequest(e);
    166         } catch (ArrayStoreException ase) {
    167             return generateBadRequestDueToMangledJsonResponse(body);
    168         } catch (NodeNotFoundException e) {
    169             return output.notFound(e);
    170         }
    171         return nothing();
    172     }
    173
    174     @GET
    175     @Path(PATH_NODE_PROPERTIES)
    176     public Response getAllNodeProperties(@PathParam("nodeId") long nodeId) {
    177         final PropertiesRepresentation properties;
    178         try {
    179             properties = server.getAllNodeProperties(nodeId);
    180         } catch (NodeNotFoundException e) {
    181             return output.notFound(e);
    182         }
    183
    184         if (properties.isEmpty()) {
    185             return nothing();
    186         }
    187
    188         return output.ok(properties);
    189     }
    190
    191     @PUT
    192     @Path(PATH_NODE_PROPERTY)
    193     public Response setNodeProperty(@PathParam("nodeId") long nodeId, @PathParam("key") String key, String body) {
    194         try {
    195             server.setNodeProperty(nodeId, key, input.readValue(body));
    196         } catch (BadInputException e) {
    197             return output.badRequest(e);
    198         } catch (ArrayStoreException ase) {
    199             return generateBadRequestDueToMangledJsonResponse(body);
    200         } catch (NodeNotFoundException e) {
    201             return output.notFound(e);
    202         }
    203         return nothing();
    204     }
    205
    206     @GET
    207     @Path(PATH_NODE_PROPERTY)
    208     public Response getNodeProperty(@PathParam("nodeId") long nodeId, @PathParam("key") String key) {
    209         try {
    210             return output.ok(server.getNodeProperty(nodeId, key));
    211         } catch (NodeNotFoundException e) {
    212             return output.notFound(e);
    213         } catch (NoSuchPropertyException e) {
    214             return output.notFound(e);
    215         }
    216     }
    217
    218     @DELETE
    219     @Path(PATH_NODE_PROPERTY)
    220     public Response deleteNodeProperty(@PathParam("nodeId") long nodeId, @PathParam("key") String key) {
    221         try {
    222             server.removeNodeProperty(nodeId, key);
    223         } catch (NodeNotFoundException e) {
    224             return output.notFound(e);
    225         } catch (NoSuchPropertyException e) {
    226             return output.notFound(e);
    227         }
    228         return nothing();
    229     }
    230
    231     @DELETE
    232     @Path(PATH_NODE_PROPERTIES)
    233     public Response deleteAllNodeProperties(@PathParam("nodeId") long nodeId) {
    234         try {
    235             server.removeAllNodeProperties(nodeId);
    236         } catch (NodeNotFoundException e) {
    237             return output.notFound(e);
    238         }
    239         return nothing();
    240     }
    241
    242     // Relationships
    243
    244     @POST
    245     @Path(PATH_NODE_RELATIONSHIPS)
    246     public Response createRelationship(@PathParam("nodeId") long startNodeId, String body) {
    247         final Map<String, Object> data;
    248         final long endNodeId;
    249         final String type;
    250         final Map<String, Object> properties;
    251         try {
    252             data = input.readMap(body);
    253             endNodeId = extractNodeId((String) data.get("to"));
    254             type = (String) data.get("type");
    255             properties = (Map<String, Object>) data.get("data");
    256         } catch (BadInputException e) {
    257             return output.badRequest(e);
    258         } catch (ClassCastException e) {
    259             return output.badRequest(e);
    260         }
    261         try {
    262             return output.created(server.createRelationship(startNodeId, endNodeId, type, properties));
    263         } catch (StartNodeNotFoundException e) {
    264             return output.notFound(e);
    265         } catch (EndNodeNotFoundException e) {
    266             return output.badRequest(e);
    267         } catch (StartNodeSameAsEndNodeException e) {
    268             return output.badRequest(e);
    269         } catch (PropertyValueException e) {
    270             return output.badRequest(e);
    271         } catch (BadInputException e) {
    272             return output.badRequest(e);
    273         }
    274     }
    275
    276     @GET
    277     @Path(PATH_RELATIONSHIP)
    278     public Response getRelationship(@PathParam("relationshipId") long relationshipId) {
    279         try {
    280             return output.ok(server.getRelationship(relationshipId));
    281         } catch (RelationshipNotFoundException e) {
    282             return output.notFound(e);
    283         }
    284     }
    285
    286     @DELETE
    287     @Path(PATH_RELATIONSHIP)
    288     public Response deleteRelationship(@PathParam("relationshipId") long relationshipId) {
    289         try {
    290             server.deleteRelationship(relationshipId);
    291         } catch (RelationshipNotFoundException e) {
    292             return output.notFound(e);
    293         }
    294         return nothing();
    295     }
    296
    297     @GET
    298     @Path(PATH_NODE_RELATIONSHIPS_W_DIR)
    299     public Response getNodeRelationships(@PathParam("nodeId") long nodeId, @PathParam("direction") RelationshipDirection direction) {
    300         try {
    301             return output.ok(server.getNodeRelationships(nodeId, direction, Collections.<String> emptyList()));
    302         } catch (NodeNotFoundException e) {
    303             return output.notFound(e);
    304         }
    305     }
    306
    307     @GET
    308     @Path(PATH_NODE_RELATIONSHIPS_W_DIR_N_TYPES)
    309     public Response getNodeRelationships(@PathParam("nodeId") long nodeId, @PathParam("direction") RelationshipDirection direction,
    310             @PathParam("types") AmpersandSeparatedCollection types) {
    311         try {
    312             return output.ok(server.getNodeRelationships(nodeId, direction, types));
    313         } catch (NodeNotFoundException e) {
    314             return output.notFound(e);
    315         }
    316     }
    317
    318     // Relationship properties
    319
    320     @GET
    321     @Path(PATH_RELATIONSHIP_PROPERTIES)
    322     public Response getAllRelationshipProperties(@PathParam("relationshipId") long relationshipId) {
    323         final PropertiesRepresentation properties;
    324         try {
    325             properties = server.getAllRelationshipProperties(relationshipId);
    326         } catch (RelationshipNotFoundException e) {
    327             return output.notFound(e);
    328         }
    329         if (properties.isEmpty()) {
    330             return nothing();
    331         } else {
    332             return output.ok(properties);
    333         }
    334     }
    335
    336     @GET
    337     @Path(PATH_RELATIONSHIP_PROPERTY)
    338     public Response getRelationshipProperty(@PathParam("relationshipId") long relationshipId, @PathParam("key") String key) {
    339         try {
    340             return output.ok(server.getRelationshipProperty(relationshipId, key));
    341         } catch (RelationshipNotFoundException e) {
    342             return output.notFound(e);
    343         } catch (NoSuchPropertyException e) {
    344             return output.notFound(e);
    345         }
    346     }
    347
    348     @PUT
    349     @Path(PATH_RELATIONSHIP_PROPERTIES)
    350     @Consumes(MediaType.APPLICATION_JSON)
    351     public Response setAllRelationshipProperties(@PathParam("relationshipId") long relationshipId, String body) {
    352         try {
    353             server.setAllRelationshipProperties(relationshipId, input.readMap(body));
    354         } catch (BadInputException e) {
    355             return output.badRequest(e);
    356         } catch (RelationshipNotFoundException e) {
    357             return output.notFound(e);
    358         }
    359         return nothing();
    360     }
    361
    362     @PUT
    363     @Path(PATH_RELATIONSHIP_PROPERTY)
    364     @Consumes(MediaType.APPLICATION_JSON)
    365     public Response setRelationshipProperty(@PathParam("relationshipId") long relationshipId, @PathParam("key") String key, String body) {
    366         try {
    367             server.setRelationshipProperty(relationshipId, key, input.readValue(body));
    368         } catch (BadInputException e) {
    369             return output.badRequest(e);
    370         } catch (RelationshipNotFoundException e) {
    371             return output.notFound(e);
    372         }
    373         return nothing();
    374     }
    375
    376     @DELETE
    377     @Path(PATH_RELATIONSHIP_PROPERTIES)
    378     public Response deleteAllRelationshipProperties(@PathParam("relationshipId") long relationshipId) {
    379         try {
    380             server.removeAllRelationshipProperties(relationshipId);
    381         } catch (RelationshipNotFoundException e) {
    382             return output.notFound(e);
    383         }
    384         return nothing();
    385     }
    386
    387     @DELETE
    388     @Path(PATH_RELATIONSHIP_PROPERTY)
    389     public Response deleteRelationshipProperty(@PathParam("relationshipId") long relationshipId, @PathParam("key") String key) {
    390         try {
    391             server.removeRelationshipProperty(relationshipId, key);
    392         } catch (RelationshipNotFoundException e) {
    393             return output.notFound(e);
    394         } catch (NoSuchPropertyException e) {
    395             return output.notFound(e);
    396         }
    397         return nothing();
    398     }
    399
    400     // Index
    401
    402     @GET
    403     @Path(PATH_NODE_INDEX)
    404     public Response getNodeIndexRoot() {
    405         if (server.getNodeIndexNames().length == 0) {
    406             return output.noContent();
    407         }
    408         return output.ok(server.nodeIndexRoot());
    409     }
    410
    411     @POST
    412     @Path(PATH_NODE_INDEX)
    413     @Consumes(MediaType.APPLICATION_JSON)
    414     public Response jsonCreateNodeIndex(String json) {
    415         try {
    416             return output.created(server.createNodeIndex(input.readMap(json)));
    417         } catch (BadInputException e) {
    418             return output.badRequest(e);
    419         }
    420     }
    421
    422     @GET
    423     @Path(PATH_RELATIONSHIP_INDEX)
    424     public Response getRelationshipIndexRoot() {
    425         if (server.getRelationshipIndexNames().length == 0) {
    426             return output.noContent();
    427         }
    428         return output.ok(server.relationshipIndexRoot());
    429     }
    430
    431     @POST
    432     @Path(PATH_RELATIONSHIP_INDEX)
    433     @Consumes(MediaType.APPLICATION_JSON)
    434     public Response jsonCreateRelationshipIndex(String json) {
    435         try {
    436             return output.created(server.createRelationshipIndex(input.readMap(json)));
    437         } catch (BadInputException e) {
    438             return output.badRequest(e);
    439         }
    440     }
    441
    442     @POST
    443     @Path(PATH_NODE_INDEX_GET)
    444     @Consumes(MediaType.APPLICATION_JSON)
    445     public Response addToNodeIndex(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value, String objectUri) {
    446         try {
    447             return output.created(server.addToNodeIndex(indexName, key, value, extractNodeId(input.readUri(objectUri).toString())));
    448         } catch (BadInputException e) {
    449             return output.badRequest(e);
    450         }
    451     }
    452
    453     @POST
    454     @Path(PATH_RELATIONSHIP_INDEX_GET)
    455     public Response addToRelationshipIndex(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value,
    456             String objectUri) {
    457         try {
    458             return output.created(server.addToRelationshipIndex(indexName, key, value, extractNodeId(input.readUri(objectUri).toString())));
    459         } catch (BadInputException e) {
    460             return output.badRequest(e);
    461         }
    462     }
    463
    464     @GET
    465     @Path(PATH_NODE_INDEX_ID)
    466     public Response getNodeFromIndexUri(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value,
    467             @PathParam("id") long id) {
    468         try {
    469             return output.ok(server.getIndexedNode(indexName, key, value, id));
    470         } catch (NotFoundException nfe) {
    471             return output.notFound(nfe);
    472         }
    473     }
    474
    475     @GET
    476     @Path(PATH_RELATIONSHIP_INDEX_ID)
    477     public Response getRelationshipFromIndexUri(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value,
    478             @PathParam("id") long id) {
    479         return output.ok(server.getIndexedRelationship(indexName, key, value, id));
    480     }
    481
    482     @GET
    483     @Path(PATH_NODE_INDEX_GET)
    484     public Response getIndexedNodes(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value) {
    485         try {
    486             return output.ok(server.getIndexedNodesByExactMatch(indexName, key, value));
    487         } catch (NotFoundException nfe) {
    488             return output.notFound(nfe);
    489         }
    490     }
    491
    492     @GET
    493     @Path(PATH_NODE_INDEX_QUERY)
    494     public Response getIndexedNodesByQuery(@PathParam("indexName") String indexName, @PathParam("key") String key, @QueryParam("query") String query) {
    495         try {
    496             return output.ok(server.getIndexedNodesByQuery(indexName, key, query));
    497         } catch (NotFoundException nfe) {
    498             return output.notFound(nfe);
    499         }
    500     }
    501
    502     @GET
    503     @Path(PATH_RELATIONSHIP_INDEX_GET)
    504     public Response getIndexedRelationships(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value) {
    505         try {
    506             return output.ok(server.getIndexedRelationships(indexName, key, value));
    507         } catch (NotFoundException nfe) {
    508             return output.notFound(nfe);
    509         }
    510     }
    511
    512     @GET
    513     @Path(PATH_RELATIONSHIP_INDEX_QUERY)
    514     public Response getIndexedRelationshipsByQuery(@PathParam("indexName") String indexName, @PathParam("key") String key, @QueryParam("query") String query) {
    515         try {
    516             return output.ok(server.getIndexedRelationshipsByQuery(indexName, key, query));
    517         } catch (NotFoundException nfe) {
    518             return output.notFound(nfe);
    519         }
    520     }
    521
    522     @DELETE
    523     @Path(PATH_NODE_INDEX_ID)
    524     public Response deleteFromNodeIndex(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value,
    525             @PathParam("id") long id) {
    526         try {
    527             server.removeFromNodeIndex(indexName, key, value, id);
    528             return nothing();
    529         } catch (NotFoundException nfe) {
    530             return output.notFound(nfe);
    531         }
    532     }
    533
    534     @DELETE
    535     @Path(PATH_NODE_INDEX_REMOVE_KEY)
    536     public Response deleteFromNodeIndexNoValue(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("id") long id) {
    537         try {
    538             server.removeFromNodeIndexNoValue(indexName, key, id);
    539             return nothing();
    540         } catch (NotFoundException nfe) {
    541             return output.notFound(nfe);
    542         }
    543     }
    544
    545     @DELETE
    546     @Path(PATH_NODE_INDEX_REMOVE)
    547     public Response deleteFromNodeIndexNoKeyValue(@PathParam("indexName") String indexName, @PathParam("id") long id) {
    548         try {
    549             server.removeFromNodeIndexNoKeyValue(indexName, id);
    550             return nothing();
    551         } catch (NotFoundException nfe) {
    552             return output.notFound(nfe);
    553         }
    554     }
    555
    556     @DELETE
    557     @Path(PATH_RELATIONSHIP_INDEX_ID)
    558     public Response deleteFromRelationshipIndex(@PathParam("indexName") String indexName, @PathParam("key") String key, @PathParam("value") String value,
    559             @PathParam("id") long id) {
    560         try {
    561             server.removeFromRelationshipIndex(indexName, key, value, id);
    562             return nothing();
    563         } catch (NotFoundException nfe) {
    564             return output.notFound(nfe);
    565         }
    566     }
    567
    568     @DELETE
    569     @Path(PATH_RELATIONSHIP_INDEX_REMOVE_KEY)
    570     public Response deleteFromRelationshipIndexnoValue(@PathParam("indexName") String indexName, @PathParam("key") String key,
    571             @PathParam("id") long id) {
    572         try {
    573             server.removeFromRelationshipIndexNoValue(indexName, key, id);
    574             return nothing();
    575         } catch (NotFoundException nfe) {
    576             return output.notFound(nfe);
    577         }
    578     }
    579
    580     @DELETE
    581     @Path(PATH_RELATIONSHIP_INDEX_REMOVE)
    582     public Response deleteFromRelationshipIndex(@PathParam("indexName") String indexName, @PathParam("value") String value,
    583             @PathParam("id") long id) {
    584         try {
    585             server.removeFromRelationshipIndexNoKeyValue(indexName, id);
    586             return nothing();
    587         } catch (NotFoundException nfe) {
    588             return output.notFound(nfe);
    589         }
    590     }
    591
    592     // Traversal
    593
    594     @POST
    595     @Path(PATH_NODE_TRAVERSE)
    596     public Response traverse(@PathParam("nodeId") long startNode, @PathParam("returnType") TraverserReturnType returnType, String body) {
    597         try {
    598             return output.ok(server.traverse(startNode, input.readMap(body), returnType));
    599         } catch (BadInputException e) {
    600             return output.badRequest(e);
    601         } catch (NotFoundException e) {
    602             return output.notFound(e);
    603         }
    604     }
    605
    606     @POST
    607     @Path(PATH_NODE_PATH)
    608     public Response singlePath(@PathParam("nodeId") long startNode, String body) {
    609         final Map<String, Object> description;
    610         final long endNode;
    611         try {
    612             description = input.readMap(body);
    613             endNode = extractNodeId((String) description.get("to"));
    614             return output.ok(server.findSinglePath(startNode, endNode, description));
    615         } catch (BadInputException e) {
    616             return output.badRequest(e);
    617         } catch (ClassCastException e) {
    618             return output.badRequest(e);
    619         } catch (NotFoundException e) {
    620             return output.notFound();
    621         }
    622
    623     }
    624
    625     @POST
    626     @Path(PATH_NODE_PATHS)
    627     public Response allPaths(@PathParam("nodeId") long startNode, String body) {
    628         final Map<String, Object> description;
    629         final long endNode;
    630         try {
    631             description = input.readMap(body);
    632             endNode = extractNodeId((String) description.get("to"));
    633             return output.ok(server.findPaths(startNode, endNode, description));
    634         } catch (BadInputException e) {
    635             return output.badRequest(e);
    636         } catch (ClassCastException e) {
    637             return output.badRequest(e);
    638         }
    639     }
    640
    641 }