• No results found

Server extensions enable us to run Java code inside the server. Using server extensions, we can extend the REST API, or replace it entirely.

Extensions take the form of JAX-RS annotated classes. JAX-RS is a Java API for building RESTful resources. Using JAX-RS annotations, we decorate each extension class to in‐ dicate to the server which HTTP requests it handles. Additional annotations control request and response formats, HTTP headers, and the formatting of URI templates. Here’s an implementation of a simple server extension that allows a client to request the distance between two members of a social network:

@Path("/distance")

public class SocialNetworkExtension

{

private final ExecutionEngine executionEngine;

public SocialNetworkExtension( @Context GraphDatabaseService db ) {

this.executionEngine = new ExecutionEngine( db ); }

@GET

@Path("/{name1}/{name2}")

public String getDistance ( @PathParam("name1") String name1, @PathParam("name2") String name2 ) {

String query = "START first=node:user(name={name1}),\n" + "second=node:user(name={name2})\n" +

"MATCH p=shortestPath(first-[*..4]-second)\n" + "RETURN length(p) AS depth";

Map<String, Object> params = new HashMap<String, Object>(); params.put( "name1", name1 );

params.put( "name2", name2 );

ExecutionResult result = executionEngine.execute( query, params ); return String.valueOf( result.columnAs( "depth" ).next() ); }

}

Of particular interest here are the various annotations:

• @Path("/distance") specifies that this extension will respond to requests directed to relative URIs beginning /distance.

• The @Path("/{name1}/{name2}") annotation on getDistance() further qualifies the URI template associated with this extension. The fragment here is concatenated with /distance to produce /distance/{name1}/{name2}, where {name1} and {name2} are placeholders for any characters occurring between the forward slashes. Later on, in “Testing server extensions” on page 87, we’ll register this extension under the /socnet relative URI. At that point, these several different parts of the path ensure that HTTP requests directed to a relative URI beginning /socnet/distance/{name1}/ {name2} (for example, http://<server>/socnet/distance/Ben/Mike) will be dispatch‐ ed to an instance of this extension.

• @GET specifies that getDistance() should be invoked only if the request is an HTTP GET. @Produces indicates that the response entity body will be formatted as text/ plain.

• The two @PathParam annotations prefacing the parameters to getDistance() serve to map the contents of the {name1} and {name2} path placeholders to the method’s name1 and name2 parameters. Given the URI http://<server>/socnet/distance/Ben/ Mike, getDistance() will be invoked with Ben for name1 and Mike for name2. • The @Context annotation in the constructor causes this extension to be handed a

reference to the embedded graph database inside the server. The server infrastruc‐ ture takes care of creating an extension and injecting it with a graph database in‐ stance, but the very presence of the GraphDatabaseService parameter here makes

this extension exceedingly testable. As we’ll see later, in “Testing server exten‐ sions”, we can unit test extensions without having to run them inside a server. Server extensions can be powerful elements in our application architecture. Their chief benefits include:

Complex transactions

Extensions enable us to execute an arbitrarily complex sequence of operations in the context of a single transaction.

Choice of APIs

Each extension is injected with a reference to the embedded graph database at the heart of the server. This gives us access to the full range of APIs—Core API, traversal framework, graph algorithm package, and Cypher—for developing our extension’s behavior.

Encapsulation

Because each extension is hidden behind a RESTful interface, we can improve and modify its implementation over time.

Response formats

We control the response: both the representation format and the HTTP headers. This enables us to create response messages whose contents employ terminology from our domain, rather than the graph-based terminology of the standard REST API (users, products, and orders for example, rather than nodes, relationships, and properties). Further, in controlling the HTTP headers attached to the response, we can leverage the HTTP protocol for things such as caching and conditional requests. When considering using server extensions, we should bear in mind the following points: JVM only

As with developing against embedded Neo4j, we’ll have to use a JVM-based lan‐ guage.

GC behaviors

We can do arbitrarily complex (and dangerous) things inside a server extension. We need to monitor garbage collection behaviors to ensure we don’t introduce any untoward side effects.

Clustering

As we discuss in more detail later, in “Availability” on page 157, Neo4j clusters for high availability and horizontal read scaling using master-slave replication. In this section we discuss some of the strategies to consider when using clustered Neo4j.

Replication

Although all writes to a cluster are coordinated through the master, Neo4j does allow writing through slaves, but even then, the slave that’s being written to syncs with the master before returning to the client. Because of the additional network traffic and coordination protocol, writing through slaves can be an order of magnitude slower than writing directly to the master. The only reasons for writing through slaves are to increase the durability guarantees of each write (the write is made durable on two instances, rather than one) and to ensure we can read our own writes when employing cache sharding (see “Cache sharding” on page 80 and “Read your own writes” on page 82 later in this chapter). Because newer versions of Neo4j enable us to specify that writes to the master must be replicated out to one or more slaves, thereby increasing the durability guarantees of writes to the master, the case for writing through slaves is now less com‐ pelling. Today it is recommended that all writes be directed to the master, and then replicated to slaves using the ha.tx_push_factor and ha.tx_push_strategy configu‐ ration settings.