Enabling Neo4j Web Admin Tool on the Embedded Server using Spring Data

As you’ve probably gathered from Jan’s recent blog posts we’re currently working on a project which makes use of Spring Data and Neo4j. For the time being we’re running the embedded version of Neo4j, which by default doesn’t have the Neo4j Management Tool enabled. This post will guide you through the hoops we navigated to get this enabled and configured using Spring.

The first thing to sort is the various Maven dependencies:

<properties>
    <spring.framework.version>3.1.1.RELEASE</spring.framework.version>
    <spring-data-neo4j.version>2.1.0.RC1</spring-data-neo4j.version>
    <spring-data-commons-core.version>1.3.0.RELEASE</spring-data-commons-core.version>
    <neo4j.version>1.7</neo4j.version>
</properties>

<repositories>
    <repository>
        <id>neo4j-release-repository</id>
        <name>Neo4j Maven 2 release repository</name>
        <url>http://m2.neo4j.org/releases</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>core</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>beans</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>context</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>aop</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>data-neo4j</artifactId>
        <version>${data-neo4j.version}</version>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-log4j12</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>${neo4j.version}</version>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-jdk14</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>${neo4j.version}</version>
        <classifier>static-web</classifier>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-jdk14</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>data-commons-core</artifactId>
        <version>${data-commons-core.version}</version>
    </dependency>
</dependencies>

This was more problematic than we expected, we encountered various issues caused by duplicate SLF4J bindings on the classpath (hence the exclusions in the dependencies).

LifecycleException: Failed to transition org.neo4j.kernel.logging.LogbackService from NONE to STOPPED

...

java.lang.ClassCastException: org.slf4j.impl.Log4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext

...

This error was caused by the following line (67) in org.neo4j.kernel.logging.LogbackService which doesn’t take into consideration the possibility of different loggers being bound.

LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory()

Configuring the module-context.xml

Now that’s out of the way we can move onto the interesting stuff…

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">

    <context:spring-configured/>
    <context:annotation-config/>

    <!--If we're not bothered about the management console we can just use this -->
    <!--<neo4j:config storeDirectory="target/neo4j-db"/>-->

    <!-- This additional config lets us use the Neo4j admin console -->     
    <bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase">
        <constructor-arg value="target/neo4j-db"/>
    </bean>
    <neo4j:config graphDatabaseService="graphDatabaseService"/>
    <bean id="config" class="com.my.repository.Neo4jServerConfigurator">
        <constructor-arg ref="graphDatabaseService"/>
        <constructor-arg>
            <map>
                <entry key="enable_remote_shell" value="true"/>
            </map>
        </constructor-arg>
    </bean>
    <bean id="serverWrapper" class="org.neo4j.server.WrappingNeoServerBootstrapper" init-method="start"
          destroy-method="stop">
        <constructor-arg ref="graphDatabaseService"/>
        <constructor-arg ref="config"/>
    </bean>

    <neo4j:repositories base-package="com.my.repository"/>

</beans>

There are definitely few things here that need explaining. We’re ultimately creating a WrappingNeoServerBootstrapper containing a custom configuration (to enable the Cypher shell) and the actual EmbeddedGraphDatabase. The tricky bit comes from the custom configuration. WrappingNeoServerBootstrapper accepts an instance of Configurator via its constructor, however the default implementation of this (EmbeddedServerConfigurator) isn’t very Spring friendly. To get around this we implemented a quick wrapper for it, Neo4jServerConfigurator which accepts a Map and also exposes getters.

public class Neo4jServerConfigurator implements Configurator {
	private final EmbeddedServerConfigurator configurator;

	public Neo4jServerConfigurator(GraphDatabaseAPI db, Map<String, Object> initialProperties) {
		configurator = new EmbeddedServerConfigurator(db);
		for (Map.Entry<String, Object> entry : initialProperties.entrySet()) {
			configurator.configuration().addProperty(entry.getKey(), entry.getValue());
		}
	}

	public Object getProperty(String property) {
		return configurator.configuration().getProperty(property);
	}

	public void setProperty(String property, Object value) {
		configurator.configuration().setProperty(property, value);
	}

	public void addProperty(String property, Object value) {
		configurator.configuration().addProperty(property, value);
	}

	@Override
	public Configuration configuration() {
		return configurator.configuration();
	}

	@Override
	public Map<String, String> getDatabaseTuningProperties() {
		return configurator.getDatabaseTuningProperties();
	}

	@Override
	public Set<ThirdPartyJaxRsPackage> getThirdpartyJaxRsClasses() {
		return configurator.getThirdpartyJaxRsClasses();
	}
}

That’s it! Load the context as normal and your Neo4j admin console should be available at http://127.0.0.1:7474/.

Any questions or feedback please comment, fire me an email or tweet @markglh.

This entry was posted in Mark's Blog and tagged , , , . Bookmark the permalink.

8 Responses to Enabling Neo4j Web Admin Tool on the Embedded Server using Spring Data

  1. Timothy Reaves says:

    When I follow this, EmbeddedServerConfigurator can not be resolved (version 1.8M07). And indeed, the JavaDocs do not find it.

  2. Mark says:

    Hi Timothy,
    Yes unfortunately it looks like things might have changed with the 1.8 release. I would imagine there has simply been some refactoring and we just need to find the replacement class. If I get chance to revisit this I’ll update the post with the new classnames.

    Thanks,
    Mark.

  3. Timothy Reaves says:

    I’m very intertested in an update to this as well.

  4. Timothy Reaves says:

    I can get the server running on 7474 by simply not using the custom configurator class; I just use the graphDatabaseService & serverWrapper beans. However, when I hit the URL, tomcat bombs with:
    DEBUG SettingsController – getSettings…
    09:35:47.501 [19699348@qtp-17215963-3] ERROR org.mortbay.log – /webadmin/js/webadmin.js
    java.lang.IllegalStateException: zip file closed
    at java.util.zip.ZipFile.ensureOpen(ZipFile.java:632) ~[na:1.7.0_03]
    at java.util.zip.ZipFile.access$200(ZipFile.java:56) ~[na:1.7.0_03]

  5. Mark says:

    Hi Timothy,
    I had a quick look at this earlier today and it looks like you can get away without the custom configurator class now – you can pass the properties to the EmbeddedGraphDatabase directly:

    <neo4j:config graphDatabaseService="graphDatabaseService"/>
    <bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase">
    <constructor-arg value="/var/neo4j/data/graph.db"/>
    <constructor-arg>
    <map>
    <entry key="enable_remote_shell" value="true"/>
    </map>
    </constructor>
    </bean>

    I can see in the logs this does set the remote shell to enabled but the console still doesn’t seem to appear. I’ll have another look when I get more time and hopefully figure this out… it’s no doubt something really simple!

  6. jason says:

    public class MyNeo4jConfigurator implements Configurator {

    @Override
    public Configuration configuration() {
    HashMap map = new HashMap();
    map.put(DEFAULT_WEBSERVER_ADDRESS, “0.0.0.0″);
    return new MapConfiguration(map);
    }

    @Override
    public Map getDatabaseTuningProperties() {
    return null;
    }

    @Override
    public Set getThirdpartyJaxRsClasses() {
    return new HashSet();
    }

    }

  7. Michael says:

    Any update on this? Using SDN4j 2.1.0-RELEASE – trying to get a console for my embedded db. (Web Interface would be good too).

    M

  8. Mark says:

    Hi Michael,
    The previous comment should work, does it not? I’ll have to investigate this further if not. I haven’t had chance to actually set this up and try it again things have been crazy busy here!

    Mark.

Leave a Reply