Prev |  | Next

## 6.1. Server Plugins

Plugins provide an easy way to extend the Neo4j REST API with new functionality, without the need to invent your own API. Think of plugins as server-side scripts that can add functions for retrieving and manipulating nodes, relationships, paths, properties or indices.

| Tip
If you want to have full control over your API, and are willing to put in the effort, and understand the risks, then Neo4j server also provides hooks for unmanaged extensions based on JAX-RS.

The needed classes reside in the org.neo4j:server-api jar file. See the linked page for downloads and instructions on how to include it using dependency management. For Maven projects, add the Server API dependencies in your `pom.xml` like this:

    <dependency>
      <groupId>org.neo4j</groupId>
      <artifactId>server-api</artifactId>
      <version>${neo4j-version}</version>
    </dependency>

Where ${neo4j-version} is the intended version.

To create a plugin, your code must inherit from the ServerPlugin class. Your plugin should also:

- ensure that it can produce an (Iterable of) Node, Relationship or Path,
- specify parameters,
- specify a point of extension and of course
- contain the application logic.
- make sure that the discovery point type in the `@PluginTarget` and the `@Source` parameter are of the same type.

An example of a plugin which augments the database (as opposed to nodes or relationships) follows:

Get all nodes or relationships plugin.

    @Description( "An extension to the Neo4j Server for getting all nodes or relationships" )
    public class GetAll extends ServerPlugin
    {
        @Name( "get_all_nodes" )
        @Description( "Get all nodes from the Neo4j graph database" )
        @PluginTarget( GraphDatabaseService.class )
        public Iterable<Node> getAllNodes( @Source GraphDatabaseService graphDb )
        {
            return graphDb.getAllNodes();
        }

        @Description( "Get all relationships from the Neo4j graph database" )
        @PluginTarget( GraphDatabaseService.class )
        public Iterable<Relationship> getAllRelationships( @Source GraphDatabaseService graphDb )
        {
            return new NestingIterable<Relationship, Node>( graphDb.getAllNodes() )
            {
                @Override
                protected Iterator<Relationship> createNestedIterator( Node item )
                {
                    return item.getRelationships( Direction.OUTGOING ).iterator();
                }
            };
        }
    }

The full source code is found here: GetAll.java

Find the shortest path between two nodes plugin.

    public class ShortestPath extends ServerPlugin
    {
        @Description( "Find the shortest path between two nodes." )
        @PluginTarget( Node.class )
        public Iterable<Path> shortestPath(
                @Source Node source,
                @Description( "The node to find the shortest path to." )
                    @Parameter( name = "target" ) Node target,
                @Description( "The relationship types to follow when searching for the shortest path(s). " +
                        "Order is insignificant, if omitted all types are followed." )
                    @Parameter( name = "types", optional = true ) String[] types,
                @Description( "The maximum path length to search for, default value (if omitted) is 4." )
                    @Parameter( name = "depth", optional = true ) Integer depth )
        {
            Expander expander;
            if ( types == null )
            {
                expander = Traversal.expanderForAllTypes();
            }
            else
            {
                expander = Traversal.emptyExpander();
                for ( int i = 0; i < types.length; i++ )
                {
                    expander = expander.add( DynamicRelationshipType.withName( types[i] ) );
                }
            }
            PathFinder<Path> shortestPath = GraphAlgoFactory.shortestPath(
                    expander, depth == null ? 4 : depth.intValue() );
            return shortestPath.findAllPaths( source, target );
        }
    }

The full source code is found here: ShortestPath.java

To deploy the code, simply compile it into a .jar file and place it onto the server classpath (which by convention is the plugins directory under the Neo4j server home directory). The .jar file must include the file META-INF/services/org.neo4j.server.plugins.ServerPlugin with the fully qualified name of the implementation class. In this case, we’d have only a single entry in our config file, though multiple entries are allowed, each on a separate line:

    org.neo4j.server.examples.GetAll
    # Any other plugins in the same jar file must be listed here

The code above makes an extension visible in the database representation (via the @PluginTarget annotation) whenever it is served from the Neo4j Server. Simply changing the @PluginTarget parameter to Node.class or Relationship.class allows us to target those parts of the data model should we wish. The functionality extensions provided by the plugin are automatically advertised in representations on the wire. For example, clients can discover the extension implemented by the above plugin easily by examining the representations they receive as responses from the server, e.g. by performing a `GET` on the default database URI:

    curl -v http://localhost:7474/db/data/

The response to the `GET` request will contain (by default) a JSON container that itself contains a container called "extensions" where the available plugins are listed. In the following case, we only have the `GetAll` plugin registered with the server, so only its extension functionality is available. Extension names will be automatically assigned, based on method names, if not specifically specified using the `@Name` annotation.

    {
    "extensions-info" : "http://localhost:7474/db/data/ext",
    "node" : "http://localhost:7474/db/data/node",
    "node_index" : "http://localhost:7474/db/data/index/node",
    "relationship_index" : "http://localhost:7474/db/data/index/relationship",
    "reference_node" : "http://localhost:7474/db/data/node/0",
    "extensions_info" : "http://localhost:7474/db/data/ext",
    "extensions" : {
      "GetAll" : {
        "get_all_nodes" : "http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes",
        "get_all_relationships" : "http://localhost:7474/db/data/ext/GetAll/graphdb/getAllRelationships"
      }
    }

Performing a `GET` on one of the two extension URIs gives back the meta information about the service:

    curl http://localhost:7474/db/data/ext/GetAll/graphdb/get_all_nodes

    {
      "extends" : "graphdb",
      "description" : "Get all nodes from the Neo4j graph database",
      "name" : "get_all_nodes",
      "parameters" : [ ]
    }

To use it, just `POST` to this URL, with parameters as specified in the description and encoded as JSON data content. F.ex for calling the `shortest path` extension (URI gotten from a `GET` to http://localhost:7474/db/data/node/123):

    curl -X POST http://localhost:7474/db/data/ext/GetAll/node/123/shortestPath -H "Content-Type: application/json" -d '{"target":"http://localhost:7474/db/data/node/456&depth=5"}'

If everything is OK a response code `200` and a list of zero or more items will be returned. If nothing is returned (null returned from extension) an empty result and response code `204` will be returned. If the extension throws an exception response code `500` and a detailed error message is returned.

Extensions that do any kind of write operation will have to manage their own transactions, i.e. transactions aren’t managed automatically.

Through this model, any plugin can naturally fit into the general hypermedia scheme that Neo4j espouses - meaning that clients can still take advantage of abstractions like Nodes, Relationships and Paths with a straightforward upgrade path as servers are enriched with plugins (old clients don’t break).

Copyright © 2011 Neo Technology

Prev | Up | Next
| Home |