Neo4j all the way Down!

ext_inside_server_inside_embedded

Just because you can, doesn’t mean you should. However sometimes it can be comforting to know how. I am going to show you how to run Neo4j Embedded and Neo4j Server at the same time…and an Unmanaged Extension inside that Neo4j Server. There aren’t any real good reasons why you’d want to do this, but it’s April Fools, so here we go.

First we’ll need to add a few dependencies to our project:

        <dependency>
            <groupId>io.undertow</groupId>
            <artifactId>undertow-core</artifactId>
            <version>${undertowVersion}</version>
        </dependency>
        <dependency>
            <groupId>org.neo4j</groupId>
            <artifactId>neo4j</artifactId>
            <version>${neo4jVersion}</version>
        </dependency>
        <dependency>
            <groupId>org.neo4j.app</groupId>
            <artifactId>neo4j-server</artifactId>
            <version>${neo4jVersion}</version>
        </dependency>

We’re going to be using Undertow again for our Server, and we’ll start off with creating our database using the GraphDatabaseFactory in our Neo4j embedded project.

public class Neo4jServer {

    private static final String STOREDIR = "/my/neo4j/data/graph.db";
    private static final String CONFIGDIR = "/my/neo4j/conf/";

    private static final GraphDatabaseService graphDb = new GraphDatabaseFactory()
            .newEmbeddedDatabaseBuilder( STOREDIR )
            .loadPropertiesFromFile(CONFIGDIR + "neo4j.properties")
            .newGraphDatabase();

Now we can configure out Neo4j Server directly by creating a ServerConfigurator and adding properties to it. In this case we want to add a Third Party Package (aka Unmanaged Extensions) like this:

    // Configure it directly
    private static final ServerConfigurator conf = 
        new ServerConfigurator((GraphDatabaseAPI)graphDb){{
        configuration().addProperty(
            Configurator.THIRD_PARTY_PACKAGES_KEY, "org.neo4j.examples.server.unmanaged=/examples/unmanaged");
    }};

Alternatively we can do our server configuration in a file as normal and pass that in instead.

    // Configure it via a File
    private static final PropertyFileConfigurator conf = 
        new PropertyFileConfigurator(new File(CONFIGDIR + "neo4j-server.properties"));

We’ll be using the WrappingNeoServer class to make it happen, passing in both our database and our configuration.

    private static final WrappingNeoServer neoWebServer = 
        new WrappingNeoServer((GraphDatabaseAPI)graphDb, conf);

One thing to note is that the GraphDatabaseAPI is being deprecated and won’t be there in the next Major release, but that doesn’t affect us right now. In our main method we will start the Undertow Server. In this case we are adding a path to a WarmupHandler that we’ll see below. We’re passing in the graphDb we created earlier. Notice that it is listening on port 7475, as we will use the default port 7474 to run the Neo4j Server. Lastly we start the neoWebServer.

    public static void main(final String[] args) {
        registerShutdownHook(graphDb);
        Undertow undertowServer = Undertow.builder()
                .addHttpListener(7475, "localhost")
                .setHandler(new PathHandler()
                        .addExactPath("/", new HelloWorldHandler())
                        .addExactPath("/warmup", new WarmupHandler(graphDb))
                ).build();
        undertowServer.start();

        neoWebServer.start();
    }

Our warm up handler is code we’ve seen before. This time it is slightly modified to be inside a Transaction. This is a change from 1.9.x where you could run reads outside of a transaction. In 2.0+ both reads and writes must be within a Transaction.

public class WarmupHandler implements HttpHandler {
    GraphDatabaseService graphDb;

    public WarmupHandler(GraphDatabaseService graphDb){
        this.graphDb = graphDb;
    }

    @Override
    public void handleRequest(final HttpServerExchange exchange) throws Exception {
        try (Transaction tx = graphDb.beginTx()) {
            Node start;
            for ( Node n : GlobalGraphOperations.at( graphDb ).getAllNodes() ) {
                n.getPropertyKeys();
                for ( Relationship relationship : n.getRelationships() ) {
                    start = relationship.getStartNode();
                }
            }
            for ( Relationship r : GlobalGraphOperations.at( graphDb ).getAllRelationships() ) {
                r.getPropertyKeys();
                start = r.getStartNode();
            }

            exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
            exchange.getResponseSender().send("Warmed up via Embedded and ready to go!");
            tx.success();
        } catch (Exception e) {
            exchange.setResponseCode(500);
            exchange.getResponseSender().send("500 Internal Server Error");
            throw new RuntimeException(e);
        }
    }
}

Now when we go to http://localhost:7475/warmup, we can see:

Screen Shot 2014-03-29 at 1.31.15 AM

…and if we try http://localhost:7474, we see the familiar Neo4j Server interface:

Screen Shot 2014-03-29 at 1.31.29 AM

Lastly if we try the unmanaged extension, it is there as well:

Screen Shot 2014-03-29 at 1.56.18 AM

One of the things I love about Neo4j is that it’s infinitely malleable to your needs, so go crazy. Also if you run into any issues or have Neo4j related you can find help on Stack Overflow, just tag your question [neo4j].

Tagged , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: