Conflicts:
	spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/Neo4jTemplate.java
	spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/GraphRepositoryTest.java
This commit is contained in:
Rickard Öberg
2012-01-10 11:29:41 +08:00
105 changed files with 2676 additions and 348 deletions

View File

@@ -6,9 +6,9 @@
<exclude module="jmxtools"/>
<exclude module="jmxri"/>
</dependency>
<dependency org="org.aspectj" name="aspectjrt" rev="1.6.11.RELEASE"/>
<dependency org="org.aspectj" name="aspectjtools" rev="1.6.11.RELEASE"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.5"/>
<dependency org="org.aspectj" name="aspectjrt" rev="1.6.12"/>
<dependency org="org.aspectj" name="aspectjtools" rev="1.6.12"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.6.M02"/>
<dependency org="junit" name="junit" rev="4.8.1"/>
</dependencies>
</ivy-module>

View File

@@ -1,6 +1,18 @@
Spring Data Graph Changelog
===========================
Changes in version 2.0.0.RELEASE (2011-12-23)
---------------------------------------------
* Updated Neo4j, Cypher-DSL to 1.6.M02, SpringFramework to 3.0.7.RELEASE, Spring-Data-Commons to 1.2.0.RELEASE
* updated documentation and examples, added chapter on how to deploy an SDN backed sample app to Heroku
* allow customization for index names, e.g. for multi-tenancy
* object typed fields are not converted if the actual instance can be stored by Neo4j
* cleanup of maven dependencies on neo4j, logging frameworks,
* allow injection of custom ConversionServices
* template.fetch() reloads entities and collections
* support for default field values on load via the @GraphProperty annotation
Changes in version 2.0.0.RC1 (2011-11-11)
-----------------------------------------
* Updated Neo4j to 1.5 AspectJ to 1.6.12

View File

@@ -7,7 +7,7 @@
<name>Spring Data Neo4j Distribution</name>
<description><![CDATA[Within the Spring Data project the Spring Data Neo4j library provides integration with the Neo4j graph database, offering object graph mapping, a Neo4jTemplate and Spring-Data-Commons Repository implementations.
]]></description>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>spring-data-neo4j-parent</module>
@@ -161,6 +161,7 @@
<module>spring-data-neo4j-examples/cineasts-aspects</module>
<module>spring-data-neo4j-examples/cineasts-rest</module>
<module>spring-data-neo4j-examples/myrestaurants-social</module>
<module>spring-data-neo4j-examples/todos</module>
</modules>
</profile>
<profile>
@@ -243,7 +244,7 @@
</build>
</profile>
</profiles>
<!--distributionManagement>
<distributionManagement>
<downloadUrl>http://www.springsource.com/spring-data</downloadUrl>
<site>
<id>static.springframework.org</id>
@@ -259,7 +260,7 @@
<name>Spring Snapshot Repository</name>
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement-->
</distributionManagement>
<build>
<extensions>
<extension>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-neo4j-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-neo4j-aspects</artifactId>
@@ -122,8 +122,8 @@
</dependency>
<dependency>
<groupId>com.tinkerpop</groupId>
<artifactId>gremlin</artifactId>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<optional>true</optional>
</dependency>

View File

@@ -65,6 +65,7 @@ public class IndexTest extends EntityTestBase {
assertEquals(friendship, friendshipFinder.findByPropertyValue("Friendship.years", 1));
}
@Test
@Transactional
public void testGetRelationshipFromLookedUpNode() {

View File

@@ -66,7 +66,7 @@ public class GremlinQueryEngineTest extends EntityTestBase {
@Test
@Transactional
public void testQueryList() throws Exception {
final String queryString = "t = new Table(); [g.v(michael),g.v(david)].each{ n -> n.as('person.name').as('person.age').table(t,['person.name','person.age']){ it.age }{ it.name } >> -1}; t;" ;
final String queryString = "t = new Table(); [g.v(michael),g.v(david)].each{ n -> n.as('person.name').as('person.age').table(t,['person.name','person.age']){ it.age }{ it.name }.iterate()}; t;" ;
final Collection<Object> result = IteratorUtil.asCollection(queryEngine.query(queryString, MapUtil.map("michael", getNodeId(michael), "david", getNodeId(testTeam.david))));
assertEquals(asList(testTeam.simpleRowFor(michael, "person"), testTeam.simpleRowFor(testTeam.david, "person")), result);

View File

@@ -81,6 +81,8 @@ public class IndexingNodeTypeRepresentationStrategyTest extends EntityTestBase {
assertEquals(node(subThing), subThingHits.getSingle());
assertEquals(thing.getClass().getName(), node(thing).getProperty(IndexingNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
assertEquals(subThing.getClass().getName(), node(subThing).getProperty(IndexingNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
thingHits.close();
subThingHits.close();
}
@Test

View File

@@ -83,6 +83,7 @@ public class IndexingRelationshipTypeRepresentationStrategyTest extends EntityTe
Relationship rel = linkHits.getSingle();
assertEquals(rel(link), rel);
assertEquals(link.getClass().getName(), rel.getProperty("__type__"));
linkHits.close();
}
@Test
@@ -104,6 +105,7 @@ public class IndexingRelationshipTypeRepresentationStrategyTest extends EntityTe
IndexHits<Relationship> linkHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
assertNull(linkHits.getSingle());
linkHits.close();
}
@Test

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-neo4j-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-neo4j-cross-store</artifactId>
@@ -125,8 +125,8 @@
</dependency>
<dependency>
<groupId>com.tinkerpop</groupId>
<artifactId>gremlin</artifactId>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<optional>true</optional>
</dependency>

View File

@@ -64,6 +64,7 @@ public class CrossStoreNodeEntityState<ENTITY extends NodeBacked> extends Defaul
final String foreignId = createForeignId(id);
IndexHits<Node> indexHits = getForeignIdIndex().get(FOREIGN_ID, foreignId);
Node node = indexHits.hasNext() ? indexHits.next() : null;
indexHits.close();
if (node == null) {
node = template.createNode();
persistForeignId(node, id);

View File

@@ -52,7 +52,7 @@ public class DataGraphNamespaceHandlerCrossStoreTest {
Neo4jTemplate template = config.template;
Assert.assertNotNull("template", template);
EmbeddedGraphDatabase graphDatabaseService = (EmbeddedGraphDatabase) template.getGraphDatabaseService();
Assert.assertEquals("store-dir", "target/config-test", graphDatabaseService.getStoreDir());
Assert.assertTrue("store-dir", graphDatabaseService.getStoreDir().endsWith("target/config-test"));
Assert.assertNotNull("graphDatabaseService", config.graphDatabaseService);
Assert.assertNotNull("transactionManager", config.transactionManager);
config.graphDatabaseService.shutdown();

View File

@@ -55,7 +55,6 @@
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase"
destroy-method="shutdown" scope="singleton">
<constructor-arg index="0" value="target/data/recommendation" />
</bean>
<bean id="graphDatabase" class="org.springframework.data.neo4j.support.DelegatingGraphDatabase">

View File

@@ -11,11 +11,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5</neo4j.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<neo4j.version>1.6.M02</neo4j.version>
<spring-data-graph.version>2.1.0.BUILD-SNAPSHOT</spring-data-graph.version>
<aspectj.version>1.6.12</aspectj.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
</properties>

View File

@@ -11,11 +11,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5</neo4j.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<neo4j.version>1.6.M02</neo4j.version>
<spring-data-graph.version>2.1.0.BUILD-SNAPSHOT</spring-data-graph.version>
<aspectj.version>1.6.12</aspectj.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
</properties>
@@ -85,7 +85,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>

View File

@@ -11,13 +11,13 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5</neo4j.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<spring-data-graph.version>2.1.0.BUILD-SNAPSHOT</spring-data-graph.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
<aspectj.version>1.6.12</aspectj.version>
<neo4j.version>1.6.M02</neo4j.version>
</properties>
<repositories>
@@ -295,14 +295,11 @@
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<!-- the eclipse plugin interacts with the aspectj-maven-plugin
BUT ONLY if the ajdtVersion config value is set (remove it and it won't) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<!-- The ajdtVersion configuration parameter is optional. The valid values are none, 1.4, and 1.5. none indicates that AJDT should not be enabled even though Aspectj is enabled in maven. 1.4 generates the org.eclipse.ajdt.ui.prefs file in the .settings directory. 1.5 (or later) includes the configuration into the .classpath file and is the default value. -->
<ajdtVersion>1.5</ajdtVersion>
<additionalProjectnatures>
<projectnature>org.eclipse.jdt.core.javanature</projectnature>
@@ -310,47 +307,6 @@
</additionalProjectnatures>
</configuration>
</plugin>
<!--plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin-->
</plugins>
</build>

View File

@@ -5,9 +5,9 @@ apply plugin: 'idea'
apply plugin: 'eclipse'
springVersion = "3.0.5.RELEASE"
springDataNeo4jVersion = "1.1.0.RELEASE"
aspectjVersion = "1.6.11.RELEASE"
springVersion = "3.0.7.RELEASE"
springDataNeo4jVersion = "2.0.0.RELEASE"
aspectjVersion = "1.6.12"
apply from:'https://github.com/SpringSource/spring-data-neo4j/raw/master/build/gradle/springdataneo4j.gradle'

View File

@@ -11,11 +11,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<aspectj.version>1.6.12</aspectj.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-data-neo4j.version>2.0.0.BUILD-SNAPSHOT</spring-data-neo4j.version>
<spring-data-neo4j.version>2.1.0.BUILD-SNAPSHOT</spring-data-neo4j.version>
</properties>
<repositories>

View File

@@ -1,14 +1,14 @@
<ivy-module version="2.0" xmlns:m="http://ant.apache.org/ivy/maven">
<info organisation="org.springframework.data.neo4j.examples" module="hello-worlds"/>
<dependencies>
<dependency org="org.springframework.data" name="spring-data-neo4j" rev="1.0.0.RELEASE">
<dependency org="org.springframework.data" name="spring-data-neo4j" rev="2.0.0.RELEASE">
<exclude module="jms"/>
<exclude module="jmxtools"/>
<exclude module="jmxri"/>
</dependency>
<dependency org="org.aspectj" name="aspectjrt" rev="1.6.11.RELEASE"/>
<dependency org="org.aspectj" name="aspectjtools" rev="1.6.11.RELEASE"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.3"/>
<dependency org="org.aspectj" name="aspectjrt" rev="1.6.12"/>
<dependency org="org.aspectj" name="aspectjtools" rev="1.6.12"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.6.M02"/>
<dependency org="junit" name="junit" rev="4.8.1"/>
</dependencies>
</ivy-module>

View File

@@ -5,11 +5,8 @@ apply plugin: 'idea'
apply plugin: 'eclipse'
springVersion = "3.0.5.RELEASE"
springDataNeo4jVersion = "1.1.0.RELEASE"
aspectjVersion = "1.6.11.RELEASE"
apply from:'https://github.com/SpringSource/spring-data-neo4j/raw/master/build/gradle/springdataneo4j.gradle'
springVersion = "3.0.7.RELEASE"
springDataNeo4jVersion = "2.0.0.RELEASE"
configurations {
runtime

View File

@@ -7,8 +7,6 @@
<import file="${settings.dir}/install-ivy.xml" />
<import file="${settings.dir}/path.xml" />
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties" classpath="${lib.dir}/aspectjtools.jar"/>
<target name="lib.retrieve" depends="install-ivy">
<mkdir dir="${lib.dir}" />
<ivy:settings file="${settings.dir}/ivysettings.xml" />
@@ -29,14 +27,8 @@
<target name="compile" description="Compile production classes" depends="lib.retrieve">
<mkdir dir="${main.target}" />
<iajc sourceroots="${main.src}" destDir="${main.target}" classpathref="path.libs" source="1.6">
<aspectpath>
<pathelement location="${lib.dir}/spring-aspects.jar"/>
</aspectpath>
<aspectpath>
<pathelement location="${lib.dir}/spring-data-neo4j.jar"/>
</aspectpath>
</iajc>
<javac sourcepath="${main.src}" destDir="${main.target}" classpathref="path.libs" source="1.6">
</javac>
</target>
<target name="compile-tests" description="Compile unit test classes" depends="compile">
@@ -44,14 +36,8 @@
<copy todir="${main.target}" failonerror="false">
<fileset dir="${main.resources}"/>
</copy>
<iajc sourceroots="${test.src}" destDir="${test.target}" classpathref="path.testing" source="1.6">
<aspectpath>
<pathelement location="${lib.dir}/spring-aspects.jar"/>
</aspectpath>
<aspectpath>
<pathelement location="${lib.dir}/spring-data-neo4j.jar"/>
</aspectpath>
</iajc>
<javac sourcepath="${test.src}" destDir="${test.target}" classpathref="path.testing" source="1.6">
</javac>
</target>
<target name="test" depends="clean, compile, compile-tests">

View File

@@ -11,11 +11,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<aspectj.version>1.6.12</aspectj.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-data-neo4j.version>2.0.0.BUILD-SNAPSHOT</spring-data-neo4j.version>
<spring-data-neo4j.version>2.1.0.BUILD-SNAPSHOT</spring-data-neo4j.version>
</properties>
<repositories>

View File

@@ -1,14 +1,12 @@
<ivy-module version="2.0" xmlns:m="http://ant.apache.org/ivy/maven">
<info organisation="org.springframework.data.neo4j.examples" module="hello-worlds"/>
<dependencies>
<dependency org="org.springframework.data" name="spring-data-neo4j" rev="1.0.0.RELEASE">
<dependency org="org.springframework.data" name="spring-data-neo4j" rev="2.0.0.RELEASE">
<exclude module="jms"/>
<exclude module="jmxtools"/>
<exclude module="jmxri"/>
</dependency>
<dependency org="org.aspectj" name="aspectjrt" rev="1.6.11.RELEASE"/>
<dependency org="org.aspectj" name="aspectjtools" rev="1.6.11.RELEASE"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.3"/>
<dependency org="org.neo4j" name="neo4j-community" rev="1.6.M02"/>
<dependency org="junit" name="junit" rev="4.8.1"/>
</dependencies>
</ivy-module>

View File

@@ -7,11 +7,11 @@
<version>2.0.0-BUILD-SNAPSHOT</version>
<name>imdb</name>
<properties>
<spring.version>3.0.6.RELEASE</spring.version>
<spring.version>3.0.7.RELEASE</spring.version>
<aspectj.version>1.6.12</aspectj.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<data-graph.version>2.0.0.BUILD-SNAPSHOT</data-graph.version>
<data-graph.version>2.1.0.BUILD-SNAPSHOT</data-graph.version>
</properties>
<repositories>

View File

@@ -4,14 +4,14 @@
<groupId>com.springone.myrestaurants</groupId>
<artifactId>myrestaurants-social</artifactId>
<packaging>war</packaging>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<name>myrestaurants-social</name>
<properties>
<spring.version>3.0.6.RELEASE</spring.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<spring.version>3.0.7.RELEASE</spring.version>
<spring-data-graph.version>2.1.0.BUILD-SNAPSHOT</spring-data-graph.version>
<aspectj.version>1.6.12</aspectj.version>
<slf4j.version>1.6.1</slf4j.version>
<neo4j.version>1.5</neo4j.version>
<neo4j.version>1.6.M02</neo4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-security.version>3.0.6.RELEASE</spring-security.version>
</properties>

View File

@@ -0,0 +1,9 @@
target/
.idea
*.iml
.project
.classpath
.settings/
*.swp
*~
.DS_Store

View File

@@ -0,0 +1 @@
web: sh target/bin/webapp

View File

@@ -0,0 +1,60 @@
#!/bin/sh
while getopts ":r" opt; do
case $opt in
r)
export APP_URL=`heroku info --raw | sed -n 's/^web_url=\(.*\)$/\1/p'`
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
shift
done
APP_URL=${APP_URL:="http://localhost:8080/"}
REST_ENDPOINT="${APP_URL}api/todos"
CURL_OPTS="--verbose --fail -H Accept:application/json -H Content-Type:application/json"
list() {
curl ${CURL_OPTS} ${REST_ENDPOINT}
}
make() {
TITLE=$1
curl -d "{ \"title\" :\"${TITLE}\"}" ${CURL_OPTS} ${REST_ENDPOINT}
}
remove() {
ID=$1
REST_ENDPOINT="${REST_ENDPOINT}/${ID}"
curl -X DELETE ${CURL_OPTS} ${REST_ENDPOINT}
}
usage() {
echo "Usage: $0 { list | mk | rm }"
}
if [ "$1" == "" ]; then
usage
exit 1
fi
case "$1" in
list)
list
;;
mk)
if [ "$2" == "" ]; then usage; echo " mk \"text\""; exit 1; fi
make "$2"
;;
rm)
if [ "$2" == "" ]; then usage; echo " rm {id}"; exit 1; fi
remove "$2"
;;
*)
usage
;;
esac

View File

@@ -0,0 +1,362 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.neo4j.app</groupId>
<artifactId>sdn-todos</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sdn-todos</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5</neo4j.version>
<spring.version>3.0.6.RELEASE</spring.version>
<spring-data-neo4j.version>${project.version}</spring-data-neo4j.version>
<aspectj.version>1.6.12</aspectj.version>
<slf4j.version>1.6.1</slf4j.version>
</properties>
<repositories>
<repository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>neo4j-public-repository</id>
<url>http://m2.neo4j.org/</url>
<name>Publicly available Maven 2 repository for Neo4j</name>
</repository>
<repository>
<id>spring-maven-milestone</id>
<url>http://maven.springframework.org/milestone</url>
<name>Spring Framework Maven MILESTONE Repository</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-maven-snapshot</id>
<url>http://maven.springframework.org/snapshot</url>
<name>Springframework Maven SNAPSHOT Repository</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
<pluginRepository>
<id>spring-maven-milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<!-- General dependencies for any application -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.flexjson</groupId>
<artifactId>flexjson</artifactId>
<version>2.1</version>
</dependency>
<!-- self-hosting servlet dependencies -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>7.4.4.v20110707</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>2.1.v20100127</version>
</dependency>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
<version>${spring-data-neo4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>${spring-data-neo4j.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
<version>${neo4j.version}</version>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-udc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
<classifier />
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<classifier />
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>assemble</goal></goals>
<configuration>
<assembleDirectory>target</assembleDirectory>
<extraJvmArguments>-Xmx512m</extraJvmArguments>
<programs>
<program>
<mainClass>Main</mainClass>
<name>webapp</name>
</program>
</programs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version> <!-- NB: do use 1.3 or 1.3.x due to MASPECTJ-90 - wait for 1.4 -->
<dependencies>
<!-- NB: You must use Maven 2.0.9 or above or these are ignored (see
MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<configuration>
<printSummary>false</printSummary>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
</plugin>
<!-- IDE -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.7</version> <!-- Note 2.8 does not work with AspectJ aspect path -->
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
<wtpversion>2.0</wtpversion>
<additionalBuildcommands>
<buildCommand>
<name>org.eclipse.ajdt.core.ajbuilder</name>
<arguments>
<aspectPath>org.springframework.aspects</aspectPath>
</arguments>
</buildCommand>
<buildCommand>
<name>org.springframework.ide.eclipse.core.springbuilder</name>
</buildCommand>
</additionalBuildcommands>
<additionalProjectnatures>
<projectnature>org.eclipse.ajdt.ui.ajnature</projectnature>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-idea-plugin</artifactId>
<version>2.2</version>
<configuration>
<downloadSources>true</downloadSources>
<dependenciesAsLibraries>true</dependenciesAsLibraries>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.4.2.v20110526</version>
<configuration>
<webAppConfig>
<contextPath>/${project.name}</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,100 @@
Spring Data Neo4j Todos
=======================
A simple todo list using Spring Data Neo4j, configured for easy deployment to Heroku.
Get Ready
---------
To prepare for deploying to Heroku, you must first become a Hero:
* Create an account on [Heroku](http://heroku.com)
* Install the `heroku` command-line client
* See the [Heroku Quickstart](http://devcenter.heroku.com/articles/quickstart) for details
Feel ready? OK, then get a copy of this project and checkout the heroku-deploy branch:
```bash
git clone git://github.com/SpringSource/spring-data-neo4j.git
cp -r spring-data-neo4j/spring-data-neo4j-examples/todos .
cd todos
```
Local build and run
-------------------
* Run a local Neo4j server (using default config)
```bash
export NEO4J_REST_URL=http://localhost:7474/db/data
export NEO4J_LOGIN=""
export NEO4J_PASSWORD=""
```
* Build, then run locally
```bash
mvn package
sh target/bin/webapp
```
* check that it is working (in another terminal, but same project directory)
```bash
./bin/todo mk "tweet high praises to @neo4j"
./bin/todo list
```
Satisfied? Then `ctrl-c` to kill the application.
Deploy on Heroku
----------------
Initialize a local git repository:
```bash
git init .
git add .
git commit -m "the start of my own todo application"
```
Create a new provisioning stack on Heroku for the app. Heroku will notice that you've got
a git repository, adding itself as a remote.
```bash
heroku create --stack cedar`
heroku addons:add neo4j
git push heroku master
```
Check to make sure it is running with `heroku ps`, which should show a `web.1` process with an "up" state.
Finally try it out using the `-r` flag to indicate remote access.
```bash
./bin/todo -r mk "tweet thanks for the good work @mesirii @akollegger"
./bin/todo -r list
```
To see the Neo4j graph you just created through Heroku, use `heroku config` to reveal the NEO4J_URL
which can take you to Neo4j's Webadmin. Have fun!
For details about preparing your own Spring Data Neo4j application for deployment to Heroku,
see the Heroku chapter in "Good Relationships: The Spring Data Neo4j Guide Book".
Don't forget to decommission the Heroku application when you're done with it.
Use `heroku destroy` to free up the instance.
CLI Tool
--------
A simplistic `todo` script in the `bin` directory can directly create, delete and list todos.
Usage: todo [-r] [ list | mk | rm ]
todo -r - access the remote (Heroku) todo application
todo list - list current todos
todo mk "a new todo" - to create a todo
todo rm 1 - to remove the todo with id 1

View File

@@ -0,0 +1,23 @@
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class Main
{
public static void main(String[] args) throws Exception
{
String webappDirLocation = "src/main/webapp/";
String webPort = System.getenv("PORT");
if(webPort == null || webPort.isEmpty()) {
webPort = "8080";
}
Server server = new Server(Integer.valueOf(webPort));
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setDescriptor(webappDirLocation+"/WEB-INF/web.xml");
root.setResourceBase(webappDirLocation);
root.setParentLoaderPriority(true);
server.setHandler(root);
server.start();
server.join();
}
}

View File

@@ -0,0 +1,76 @@
package org.neo4j.app.todos;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.neo4j.graphdb.Node;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;
@NodeEntity
@Configurable
public class Todo {
@SuppressWarnings("unused")
@GraphId
private Long id;
private String title;
private Boolean isDone = false;
public Todo() {;}
public Todo(Node n) {
setPersistentState(n);
}
public Long getId() {
return getNodeId();
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public Boolean getIsDone() {
return this.isDone;
}
public void setIsDone(Boolean done) {
this.isDone = done;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Done: ").append(getIsDone()).append(", ");
sb.append("Title: ").append(getTitle());
return sb.toString();
}
public String toJson() {
return new JSONSerializer().exclude("*.class", "*.persistentState", "*.entityState").serialize(this);
}
public static String toJsonArray(Iterable<Todo> collection) {
return new JSONSerializer().exclude("*.class", "*.persistentState", "*.entityState").serialize(collection);
}
public static Todo fromJsonToTodo(String json) {
return new JSONDeserializer<Todo>().use(null, Todo.class).deserialize(json);
}
public static Collection<Todo> fromJsonArrayToTodoes(String json) {
return new JSONDeserializer<List<Todo>>().use(null, ArrayList.class).use("values", Todo.class).deserialize(json);
}
}

View File

@@ -0,0 +1,127 @@
package org.neo4j.app.todos;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils;
@RequestMapping("/todos")
@Controller
public class TodoController {
@Autowired TodoRepository todos;
/**
* POST /todos { "title": "text of the todo" }
*
* Creates a new todo.
*
* @param json
* @return json containing the id of the newly created todo
*/
@RequestMapping(method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<String> createFromJson(@RequestBody String json) {
Todo createdTodo = todos.save(Todo.fromJsonToTodo(json));
HttpHeaders headers= new HttpHeaders();
headers.add("Content-Type", "application/text");
return new ResponseEntity<String>("{\"id\":" + createdTodo.getId() + "}", headers, HttpStatus.CREATED);
}
/**
* GET /todos/{id}
*
* Gets the full json for a todo.
*
* @param id
* @return json representation of the requested todo, or HttpStatus.NOT_FOUND
*/
@RequestMapping(value = "/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
@ResponseBody
public ResponseEntity<String> showJson(@PathVariable("id") Long id) {
Todo todo = todos.findOne(id);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/text; charset=utf-8");
if (todo == null) {
return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<String>(todo.toJson(), headers, HttpStatus.OK);
}
/**
* Get /todos/
*
* Gets a json array of all todos.
*
* @return json array representation of all todos
*/
@RequestMapping(headers = "Accept=application/json")
@ResponseBody
public ResponseEntity<String> listJson() {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/text; charset=utf-8");
return new ResponseEntity<String>(Todo.toJsonArray(todos.findAll()), headers, HttpStatus.OK);
}
/**
* PUT /todos/ { "id": id, "title": "text of todo", idDone: true|false }
*
* Updates an existing todo.
*
* @param json full json representation of the todo to update
* @return
*/
@RequestMapping(method = RequestMethod.PUT, headers = "Accept=application/json")
public ResponseEntity<String> updateFromJson(@RequestBody String json) {
HttpHeaders headers= new HttpHeaders();
headers.add("Content-Type", "application/text");
if (todos.save(Todo.fromJsonToTodo(json)) == null) {
return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<String>(headers, HttpStatus.OK);
}
/**
* DELETE /todos/{id}
*
* Deletes an existing todo.
*
* @param id
* @return
*/
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE, headers = "Accept=application/json")
public ResponseEntity<String> deleteFromJson(@PathVariable("id") Long id) {
Todo todo = todos.findOne(id);
HttpHeaders headers= new HttpHeaders();
headers.add("Content-Type", "application/text");
if (todo == null) {
return new ResponseEntity<String>(headers, HttpStatus.NOT_FOUND);
}
todos.delete(todo);
return new ResponseEntity<String>(headers, HttpStatus.OK);
}
String encodeUrlPathSegment(String pathSegment, HttpServletRequest httpServletRequest) {
String enc = httpServletRequest.getCharacterEncoding();
if (enc == null) {
enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
}
try {
pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
}
catch (UnsupportedEncodingException uee) {}
return pathSegment;
}
}

View File

@@ -0,0 +1,7 @@
package org.neo4j.app.todos;
import org.springframework.data.neo4j.repository.GraphRepository;
public interface TodoRepository extends GraphRepository<Todo> {
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<neo4j:repositories base-package="org.neo4j.app.todos"/>
<tx:annotation-driven mode="aspectj"/>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService"
class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg index="0" value="${NEO4J_REST_URL}" />
<constructor-arg index="1" value="${NEO4J_LOGIN}" />
<constructor-arg index="2" value="${NEO4J_PASSWORD}" />
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
</beans>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!--
This will automatically locate any and all property files you have
within your classpath, provided they fall under the META-INF/spring
directory. The located property files are parsed and their values can
then be used within application context files in the form of
${propertyKey}.
-->
<context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>
<!--
Turn on AspectJ @Configurable support. As a result, any time you
instantiate an object, Spring will attempt to perform dependency
injection on that object. This occurs for instantiation via the "new"
keyword, as well as via reflection. This is possible because AspectJ
is used to "weave" Roo-based applications at compile time. In effect
this feature allows dependency injection of any object at all in your
system, which is a very useful feature (without @Configurable you'd
only be able to dependency inject objects acquired from Spring or
subsequently presented to a specific Spring dependency injection
method). Roo applications use this useful feature in a number of
areas, such as @PersistenceContext injection into entities.
-->
<context:spring-configured/>
<!--
This declaration will cause Spring to locate every @Component,
@Repository and @Service in your application. In practical terms this
allows you to write a POJO and then simply annotate the new POJO as an
@Service and Spring will automatically detect, instantiate and
dependency inject your service at startup time. Importantly, you can
then also have your new service injected into any other class that
requires it simply by declaring a field for your service inside the
relying class and Spring will inject it. Note that two exclude filters
are declared. The first ensures that Spring doesn't spend time
introspecting Roo-specific ITD aspects. The second ensures Roo doesn't
instantiate your @Controller classes, as these should be instantiated
by a web tier application context. Refer to web.xml for more details
about the web tier application context setup services.
Furthermore, this turns on @Autowired, @PostConstruct etc support. These
annotations allow you to use common Spring and Java Enterprise Edition
annotations in your classes without needing to do any special configuration.
The most commonly used annotation is @Autowired, which instructs Spring to
dependency inject an object into your class.
-->
<context:component-scan base-package="org.neo4j.app.todos">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
</beans>

View File

@@ -0,0 +1,17 @@
log4j.rootLogger=error, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=application.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<!--[if lt IE 7 ]><html class="ie ie6" lang="en"> <![endif]-->
<!--[if IE 7 ]><html class="ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="en"> <!--<![endif]-->
<head>
<!-- Basic Page Needs
================================================== -->
<meta charset="utf-8">
<title>Your Page Title Here :)</title>
<meta name="description" content="">
<meta name="author" content="">
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Mobile Specific Metas
================================================== -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<!-- CSS
================================================== -->
<link rel="stylesheet" href="stylesheets/base.css">
<link rel="stylesheet" href="stylesheets/skeleton.css">
<link rel="stylesheet" href="stylesheets/layout.css">
<!-- Favicons
================================================== -->
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
</head>
<body>
<!-- Primary Page Layout
================================================== -->
<div class="container">
<h1 style="margin-top: 100px; text-align:center">Sorry. Couldn't Find That Page!</h1>
</div>
<!-- End Document
================================================== -->
</body>
</html>

View File

@@ -0,0 +1 @@
styleSheet=resources/styles/alt.css

View File

@@ -0,0 +1 @@
styleSheet=resources/styles/standard.css

View File

@@ -0,0 +1,9 @@
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- The controllers are autodetected POJOs labeled with the @Controller annotation. -->
<context:component-scan base-package="org.neo4j.app.todos" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
</beans>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>sdn-todos</display-name>
<description>Neo4j SDN Todo Application</description>
<!-- Enable escaping of form submission contents -->
<context-param>
<param-name>defaultHtmlEscape</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Handles Spring requests -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/uncaughtException</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/resourceNotFound</location>
</error-page>
</web-app>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,107 @@
<!DOCTYPE html>
<!--[if lt IE 7 ]><html class="ie ie6" lang="en"> <![endif]-->
<!--[if IE 7 ]><html class="ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 9)|!(IE)]><!--><html lang="en"> <!--<![endif]-->
<head>
<!-- Basic Page Needs
================================================== -->
<meta charset="utf-8">
<title>SDN Todos</title>
<meta name="description" content="An SDN Demo Application">
<meta name="author" content="Andreas Kollegger">
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Mobile Specific Metas
================================================== -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<!-- CSS
================================================== -->
<link rel="stylesheet" href="stylesheets/base.css">
<link rel="stylesheet" href="stylesheets/skeleton.css">
<link rel="stylesheet" href="stylesheets/layout.css">
<!-- Favicons
================================================== -->
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
</head>
<body>
<!-- Primary Page Layout
================================================== -->
<!-- Delete everything in this .container and get started on your own site! -->
<div class="container">
<div class="sixteen columns">
<h1 class="remove-bottom" style="margin-top: 40px">Spring Data Neo4j: Todos</h1>
<h5>heroku-deploy</h5>
<hr />
</div>
<div class="row">
<div class="one-third column">
<h3>About SDN Todos</h3>
<p>SDN Todos is a small demonstration application of Spring Data Neo4j. SDN is used to define a simple 'Todo' domain entity, exposed with a JSON endpoint.
Inspired by the SproutCore (now <a href="http://www.emberjs.com/">ember.js</a>) demo application, this was originally intended as a server-side
companion using Neo4j. Now, it is a headless baseline implementation with a simple CLI.
</p>
</div>
<div class="one-third column">
<h3>Highlights</h3>
<p>SDN Todos is small, yet demonstrative:</p>
<ul class="square">
<li><strong>Spring Data Neo4j simplicity</strong>: annotated POJO, companion Repository and MVC controller</li>
<li><strong>Heroku ready</strong>: run locally, or easily deploy into the cloud</li>
<li><strong>CLI clarity</strong>: no distracting UI, just simple tooling</li>
</ul>
</div>
<div class="one-third column">
<h3>Docs &amp; Support</h3>
<p>To learn more about developing with Spring Data Neo4j, visit <a href="https://spring.neo4j.org">spring.neo4j.org</a>.
For questions or comments, join us on the <a href="http://groups.google.com/group/neo4j">Neo4j google group</a>.
</div>
</div>
<!-- UI -->
<div class="row">
<h5>Todos (a simply jquery pull from the json endpoint):</h5>
<div id="todos">
</div>
<hr />
</div>
<!-- footer -->
<div class="row">
<div class="eight columns">
<a href="http://neo4j.org"><image src="images/icon_neo4j.png"/></a>
</div>
<div class="eight columns">
<a href="http://spring.neo4j.org"><image style="float:right" src="images/icon_sdn.png"/></a>
</div>
</div>
</div><!-- container -->
<!-- JS
================================================== -->
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="javascripts/sdn-todos.js"></script>
<!-- End Document
================================================== -->
</body>
</html>

View File

@@ -0,0 +1,25 @@
$(document).ready(function() {
$.ajaxSetup ({
cache: false
});
var jsonUrl = "api/todos";
$.getJSON(
jsonUrl,
function(json) {
var items = [];
$.each(json, function(i, todo) {
items.push('<li>' + todo.title + '</li>');
});
$('<ul/>', {
'class': 'disc',
html: items.join('')
}).appendTo('div#todos');
}
);
});

View File

@@ -0,0 +1,42 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
$(document).ready(function() {
/* Tabs Activiation
================================================== */
var tabs = $('ul.tabs');
tabs.each(function(i) {
//Get all tabs
var tab = $(this).find('> li > a');
tab.click(function(e) {
//Get Location of tab's content
var contentLocation = $(this).attr('href');
//Let go if not a hashed one
if(contentLocation.charAt(0)=="#") {
e.preventDefault();
//Make Tab Active
tab.removeClass('active');
$(this).addClass('active');
//Show Tab Content & add active class
$(contentLocation).show().addClass('active').siblings().hide().removeClass('active');
}
});
});
});

View File

@@ -0,0 +1,17 @@
log4j.rootLogger=error, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=application.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

View File

@@ -0,0 +1,5 @@
# www.robotstxt.org/
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
User-agent: *

View File

@@ -0,0 +1,338 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Content
==================================================
#Reset & Basics
#Basic Styles
#Site Styles
#Typography
#Links
#Lists
#Images
#Buttons
#Tabs
#Forms
#Misc */
/* #Reset & Basics (Inspired by E. Meyers)
================================================== */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline; }
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block; }
body {
line-height: 1; }
ol, ul {
list-style: none; }
blockquote, q {
quotes: none; }
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none; }
table {
border-collapse: collapse;
border-spacing: 0; }
/* #Basic Styles
================================================== */
body {
background: #fff;
font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #444;
-webkit-font-smoothing: antialiased; /* Fix for webkit rendering */
-webkit-text-size-adjust: 100%;
}
/* #Typography
================================================== */
h1, h2, h3, h4, h5, h6 {
color: #181818;
font-family: "Georgia", "Times New Roman", Helvetica, Arial, sans-serif;
font-weight: normal; }
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }
h1 { font-size: 46px; line-height: 50px; margin-bottom: 14px;}
h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; }
h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; }
h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; }
h5 { font-size: 17px; line-height: 24px; }
h6 { font-size: 14px; line-height: 21px; }
.subheader { color: #777; }
p { margin: 0 0 20px 0; }
p img { margin: 0; }
p.lead { font-size: 21px; line-height: 27px; color: #777; }
em { font-style: italic; }
strong { font-weight: bold; color: #333; }
small { font-size: 80%; }
/* Blockquotes */
blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; }
blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; }
blockquote cite { display: block; font-size: 12px; color: #555; }
blockquote cite:before { content: "\2014 \0020"; }
blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; }
hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; }
/* #Links
================================================== */
a, a:visited { color: #333; text-decoration: underline; outline: 0; }
a:hover, a:focus { color: #000; }
p a, p a:visited { line-height: inherit; }
/* #Lists
================================================== */
ul, ol { margin-bottom: 20px; }
ul { list-style: none outside; }
ol { list-style: decimal; }
ol, ul.square, ul.circle, ul.disc { margin-left: 30px; }
ul.square { list-style: square outside; }
ul.circle { list-style: circle outside; }
ul.disc { list-style: disc outside; }
ul ul, ul ol,
ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; }
ul ul li, ul ol li,
ol ol li, ol ul li { margin-bottom: 6px; }
li { line-height: 18px; margin-bottom: 12px; }
ul.large li { line-height: 21px; }
li p { line-height: 21px; }
/* #Images
================================================== */
img.scale-with-grid {
max-width: 100%;
height: auto; }
/* #Buttons
================================================== */
a.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
background: #eee; /* Old browsers */
background: #eee -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */
background: #eee -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.2)), color-stop(100%,rgba(0,0,0,.2))); /* Chrome,Safari4+ */
background: #eee -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */
background: #eee -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */
background: #eee -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */
background: #eee linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */
border: 1px solid #aaa;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
padding: 4px 12px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
color: #444;
display: inline-block;
font-size: 11px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px rgba(255, 255, 255, .75);
cursor: pointer;
margin-bottom: 20px;
line-height: 21px;
font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; }
a.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover {
color: #222;
background: #ddd; /* Old browsers */
background: #ddd -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */
background: #ddd -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.3)), color-stop(100%,rgba(0,0,0,.3))); /* Chrome,Safari4+ */
background: #ddd -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */
background: #ddd -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */
background: #ddd -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */
background: #ddd linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */
border: 1px solid #888;
border-top: 1px solid #aaa;
border-left: 1px solid #aaa; }
a.button:active,
button:active,
input[type="submit"]:active,
input[type="reset"]:active,
input[type="button"]:active {
border: 1px solid #666;
background: #ccc; /* Old browsers */
background: #ccc -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */
background: #ccc -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.35)), color-stop(100%,rgba(10,10,10,.4))); /* Chrome,Safari4+ */
background: #ccc -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */
background: #ccc -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */
background: #ccc -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */
background: #ccc linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ }
.button.full-width,
button.full-width,
input[type="submit"].full-width,
input[type="reset"].full-width,
input[type="button"].full-width {
width: 100%;
padding-left: 0 !important;
padding-right: 0 !important;
text-align: center; }
/* #Tabs (activate in tabs.js)
================================================== */
ul.tabs {
display: block;
margin: 0 0 20px 0;
padding: 0;
border-bottom: solid 1px #ddd; }
ul.tabs li {
display: block;
width: auto;
height: 30px;
padding: 0;
float: left;
margin-bottom: 0; }
ul.tabs li a {
display: block;
text-decoration: none;
width: auto;
height: 29px;
padding: 0px 20px;
line-height: 30px;
border: solid 1px #ddd;
border-width: 1px 1px 0 0;
margin: 0;
background: #f5f5f5;
font-size: 13px; }
ul.tabs li a.active {
background: #fff;
height: 30px;
position: relative;
top: -4px;
padding-top: 4px;
border-left-width: 1px;
margin: 0 0 0 -1px;
color: #111;
-moz-border-radius-topleft: 2px;
-webkit-border-top-left-radius: 2px;
border-top-left-radius: 2px;
-moz-border-radius-topright: 2px;
-webkit-border-top-right-radius: 2px;
border-top-right-radius: 2px; }
ul.tabs li:first-child a.active {
margin-left: 0; }
ul.tabs li:first-child a {
border-width: 1px 1px 0 1px;
-moz-border-radius-topleft: 2px;
-webkit-border-top-left-radius: 2px;
border-top-left-radius: 2px; }
ul.tabs li:last-child a {
-moz-border-radius-topright: 2px;
-webkit-border-top-right-radius: 2px;
border-top-right-radius: 2px; }
ul.tabs-content { margin: 0; display: block; }
ul.tabs-content > li { display:none; }
ul.tabs-content > li.active { display: block; }
/* Clearfixing tabs for beautiful stacking */
ul.tabs:before,
ul.tabs:after {
content: '\0020';
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0; }
ul.tabs:after {
clear: both; }
ul.tabs {
zoom: 1; }
/* #Forms
================================================== */
form {
margin-bottom: 20px; }
fieldset {
margin-bottom: 20px; }
input[type="text"],
input[type="password"],
input[type="email"],
textarea,
select {
border: 1px solid #ccc;
padding: 6px 4px;
outline: none;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #777;
margin: 0;
width: 210px;
max-width: 100%;
display: block;
margin-bottom: 20px;
background: #fff; }
select {
padding: 0; }
input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
textarea:focus {
border: 1px solid #aaa;
color: #444;
-moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
-webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
box-shadow: 0 0 3px rgba(0,0,0,.2); }
textarea {
min-height: 60px; }
label,
legend {
display: block;
font-weight: bold;
font-size: 13px; }
select {
width: 220px; }
input[type="checkbox"] {
display: inline; }
label span,
legend span {
font-weight: normal;
font-size: 13px;
color: #444; }
/* #Misc
================================================== */
.remove-bottom { margin-bottom: 0 !important; }
.half-bottom { margin-bottom: 10px !important; }
.add-bottom { margin-bottom: 20px !important; }
/* #SDN styling
================================================== */
#footer { background: #eee; }

View File

@@ -0,0 +1,58 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Content
==================================================
#Site Styles
#Page Styles
#Media Queries
#Font-Face */
/* #Site Styles
================================================== */
/* #Page Styles
================================================== */
/* #Media Queries
================================================== */
/* Smaller than standard 960 (devices and browsers) */
@media only screen and (max-width: 959px) {}
/* Tablet Portrait size to standard 960 (devices and browsers) */
@media only screen and (min-width: 768px) and (max-width: 959px) {}
/* All Mobile Sizes (devices and browser) */
@media only screen and (max-width: 767px) {}
/* Mobile Landscape Size to Tablet Portrait (devices and browsers) */
@media only screen and (min-width: 480px) and (max-width: 767px) {}
/* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */
@media only screen and (max-width: 479px) {}
/* #Font-Face
================================================== */
/* This is the proper syntax for an @font-face file
Just create a "fonts" folder at the root,
copy your FontName into code below and remove
comment brackets */
/* @font-face {
font-family: 'FontName';
src: url('../fonts/FontName.eot');
src: url('../fonts/FontName.eot?iefix') format('eot'),
url('../fonts/FontName.woff') format('woff'),
url('../fonts/FontName.ttf') format('truetype'),
url('../fonts/FontName.svg#webfontZam02nTh') format('svg');
font-weight: normal;
font-style: normal; }
*/

View File

@@ -0,0 +1,236 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Contents
==================================================
#Base 960 Grid
#Tablet (Portrait)
#Mobile (Portrait)
#Mobile (Landscape)
#Clearing */
/* #Base 960 Grid
================================================== */
.container { position: relative; width: 960px; margin: 0 auto; padding: 0; }
.column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; }
.row { margin-bottom: 20px; }
/* Nested Column Classes */
.column.alpha, .columns.alpha { margin-left: 0; }
.column.omega, .columns.omega { margin-right: 0; }
/* Base Grid */
.container .one.column { width: 40px; }
.container .two.columns { width: 100px; }
.container .three.columns { width: 160px; }
.container .four.columns { width: 220px; }
.container .five.columns { width: 280px; }
.container .six.columns { width: 340px; }
.container .seven.columns { width: 400px; }
.container .eight.columns { width: 460px; }
.container .nine.columns { width: 520px; }
.container .ten.columns { width: 580px; }
.container .eleven.columns { width: 640px; }
.container .twelve.columns { width: 700px; }
.container .thirteen.columns { width: 760px; }
.container .fourteen.columns { width: 820px; }
.container .fifteen.columns { width: 880px; }
.container .sixteen.columns { width: 940px; }
.container .one-third.column { width: 300px; }
.container .two-thirds.column { width: 620px; }
/* Offsets */
.container .offset-by-one { padding-left: 60px; }
.container .offset-by-two { padding-left: 120px; }
.container .offset-by-three { padding-left: 180px; }
.container .offset-by-four { padding-left: 240px; }
.container .offset-by-five { padding-left: 300px; }
.container .offset-by-six { padding-left: 360px; }
.container .offset-by-seven { padding-left: 420px; }
.container .offset-by-eight { padding-left: 480px; }
.container .offset-by-nine { padding-left: 540px; }
.container .offset-by-ten { padding-left: 600px; }
.container .offset-by-eleven { padding-left: 660px; }
.container .offset-by-twelve { padding-left: 720px; }
.container .offset-by-thirteen { padding-left: 780px; }
.container .offset-by-fourteen { padding-left: 840px; }
.container .offset-by-fifteen { padding-left: 900px; }
/* #Tablet (Portrait)
================================================== */
/* Note: Design for a width of 768px */
@media only screen and (min-width: 768px) and (max-width: 959px) {
.container { width: 768px; }
.container .column,
.container .columns { margin-left: 10px; margin-right: 10px; }
.column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; }
.column.omega, .columns.omega { margin-right: 0; margin-left: 10px; }
.container .one.column { width: 28px; }
.container .two.columns { width: 76px; }
.container .three.columns { width: 124px; }
.container .four.columns { width: 172px; }
.container .five.columns { width: 220px; }
.container .six.columns { width: 268px; }
.container .seven.columns { width: 316px; }
.container .eight.columns { width: 364px; }
.container .nine.columns { width: 412px; }
.container .ten.columns { width: 460px; }
.container .eleven.columns { width: 508px; }
.container .twelve.columns { width: 556px; }
.container .thirteen.columns { width: 604px; }
.container .fourteen.columns { width: 652px; }
.container .fifteen.columns { width: 700px; }
.container .sixteen.columns { width: 748px; }
.container .one-third.column { width: 236px; }
.container .two-thirds.column { width: 492px; }
/* Offsets */
.container .offset-by-one { padding-left: 48px; }
.container .offset-by-two { padding-left: 96px; }
.container .offset-by-three { padding-left: 144px; }
.container .offset-by-four { padding-left: 192px; }
.container .offset-by-five { padding-left: 240px; }
.container .offset-by-six { padding-left: 288px; }
.container .offset-by-seven { padding-left: 336px; }
.container .offset-by-eight { padding-left: 348px; }
.container .offset-by-nine { padding-left: 432px; }
.container .offset-by-ten { padding-left: 480px; }
.container .offset-by-eleven { padding-left: 528px; }
.container .offset-by-twelve { padding-left: 576px; }
.container .offset-by-thirteen { padding-left: 624px; }
.container .offset-by-fourteen { padding-left: 672px; }
.container .offset-by-fifteen { padding-left: 720px; }
}
/* #Mobile (Portrait)
================================================== */
/* Note: Design for a width of 320px */
@media only screen and (max-width: 767px) {
.container { width: 300px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 300px; }
/* Offsets */
.container .offset-by-one,
.container .offset-by-two,
.container .offset-by-three,
.container .offset-by-four,
.container .offset-by-five,
.container .offset-by-six,
.container .offset-by-seven,
.container .offset-by-eight,
.container .offset-by-nine,
.container .offset-by-ten,
.container .offset-by-eleven,
.container .offset-by-twelve,
.container .offset-by-thirteen,
.container .offset-by-fourteen,
.container .offset-by-fifteen { padding-left: 0; }
}
/* #Mobile (Landscape)
================================================== */
/* Note: Design for a width of 480px */
@media only screen and (min-width: 480px) and (max-width: 767px) {
.container { width: 420px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 420px; }
}
/* #Clearing
================================================== */
/* Self Clearing Goodness */
.container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; }
/* Use clearfix class on parent to clear nested columns,
or wrap each row of columns in a <div class="row"> */
.clearfix:before,
.clearfix:after,
.row:before,
.row:after {
content: '\0020';
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0; }
.row:after,
.clearfix:after {
clear: both; }
.row,
.clearfix {
zoom: 1; }
/* You can also use a <br class="clear" /> to clear columns */
.clear {
clear: both;
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0;
}

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-parent</artifactId>
<name>Spring Data Neo4j Parent</name>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<description><![CDATA[Spring Data Neo4j parent project. Defines dependencies and common configuration for the build process.
]]></description>
@@ -124,12 +124,13 @@
<org.mockito.version>1.8.4</org.mockito.version>
<org.slf4j.version>1.6.1</org.slf4j.version>
<org.springframework.version>3.0.6.RELEASE</org.springframework.version>
<data.commons.version>1.2.0.M2</data.commons.version>
<neo4j.version>1.5</neo4j.version>
<data.commons.version>1.2.0.RC1</data.commons.version>
<neo4j.version>1.6.M02</neo4j.version>
<neo4j.spatial.version>0.7-SNAPSHOT</neo4j.spatial.version>
<aspectj.version>1.6.12</aspectj.version>
<blueprints.version>1.0</blueprints.version>
<gremlin.version>1.3</gremlin.version>
<blueprints.version>1.1</blueprints.version>
<gremlin.version>1.4</gremlin.version>
<pipes.version>1.4</pipes.version>
</properties>
<profiles>
<profile>
@@ -338,6 +339,61 @@
</dependency>
<dependency>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<version>${gremlin.version}</version>
<type>jar</type>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ha</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-management</artifactId>
</exclusion>
<!-- Sail support not needed -->
<exclusion>
<groupId>com.tinkerpop.blueprints</groupId>
<artifactId>blueprints-sail-graph</artifactId>
</exclusion>
<!-- Maven support in groovy not needed -->
<exclusion>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
</exclusion>
<!-- "readline" not needed - we only expose gremlin through webadmin -->
<exclusion>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.tinkerpop.blueprints</groupId>
<artifactId>blueprints-neo4j-graph</artifactId>
<version>${blueprints.version}</version>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ha</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-management</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.tinkerpop.blueprints</groupId>
<artifactId>blueprints-core</artifactId>
@@ -352,8 +408,8 @@
</dependency>
<dependency>
<groupId>com.tinkerpop</groupId>
<artifactId>gremlin</artifactId>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<version>${gremlin.version}</version>
<optional>true</optional>
</dependency>
@@ -414,7 +470,7 @@
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher-dsl</artifactId>
<version>${neo4j.version}</version>
<version>1.6.M02</version>
<!--optional>true</optional-->
</dependency>
<!--dependency>
@@ -439,6 +495,12 @@
<groupId>org.neo4j</groupId>
<artifactId>neo4j-spatial</artifactId>
<version>${neo4j.spatial.version}</version>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>

View File

@@ -4,12 +4,12 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-neo4j-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-neo4j-rest</artifactId>
<packaging>jar</packaging>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<name>Spring Data Neo4j REST Wrapper</name>
<description><![CDATA[Spring Data Neo4j Wrapper for the Neo4j REST API, provides a Graph Database proxy for the remote invocation.
]]></description>
@@ -48,7 +48,7 @@
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-rest-graphdb</artifactId>
<version>1.5</version>
<version>1.6.M02</version>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
@@ -150,8 +150,8 @@
</dependency>
<dependency>
<groupId>com.tinkerpop</groupId>
<artifactId>gremlin</artifactId>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<optional>true</optional>
</dependency>

View File

@@ -23,7 +23,6 @@ import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.RequestLogHandler;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.kernel.AbstractGraphDatabase;
import org.neo4j.server.AddressResolver;
import org.neo4j.server.Bootstrapper;
import org.neo4j.server.NeoServerWithEmbeddedWebServer;
import org.neo4j.server.configuration.PropertyFileConfigurator;
@@ -38,7 +37,6 @@ import org.neo4j.server.web.Jetty6WebServer;
import org.neo4j.test.ImpermanentGraphDatabase;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
@@ -78,11 +76,7 @@ public class LocalTestServer {
return new GraphDatabaseFactory() {
@Override
public AbstractGraphDatabase createDatabase(String databaseStoreDirectory, Map<String, String> databaseProperties) {
try {
return new ImpermanentGraphDatabase();
} catch (IOException e) {
throw new RuntimeException(e);
}
return new ImpermanentGraphDatabase();
}
};
}
@@ -97,12 +91,6 @@ public class LocalTestServer {
return serverModules;
}
};
final AddressResolver addressResolver = new AddressResolver() {
@Override
public String getHostname() {
return hostname;
}
};
final JettyStartupListener startupListener = new JettyStartupListener();
final Jetty6WebServer jettyWebServer = new Jetty6WebServer() {
@Override
@@ -123,8 +111,7 @@ public class LocalTestServer {
super.stop();
}
};
neoServer = new NeoServerWithEmbeddedWebServer(bootstrapper
, addressResolver, new StartupHealthCheck(), new PropertyFileConfigurator(new File(url.getPath())), jettyWebServer, serverModules) {
neoServer = new NeoServerWithEmbeddedWebServer(bootstrapper, new StartupHealthCheck(), new PropertyFileConfigurator(new File(url.getPath())), jettyWebServer, serverModules) {
@Override
protected int getWebServerPort() {
return port;

View File

@@ -37,6 +37,7 @@ public class RestIndexTest extends RestTestBase {
IndexHits<Node> hits = nodeIndex().get("name", "test");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
hits.close();
}
@Test
@@ -45,6 +46,7 @@ public class RestIndexTest extends RestTestBase {
IndexHits<Node> hits = nodeIndex().query("name", "tes*");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
hits.close();
}
@Test
@@ -53,12 +55,14 @@ public class RestIndexTest extends RestTestBase {
IndexHits<Node> hits = nodeIndex().query("age", "age:[30 TO 40]");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
hits.close();
}
@Test
public void testNotFoundInNodeIndex() {
IndexHits<Node> hits = nodeIndex().get("foo", "bar");
Assert.assertEquals("no index results", false, hits.hasNext());
hits.close();
}
@Test
@@ -68,12 +72,14 @@ public class RestIndexTest extends RestTestBase {
IndexHits<Relationship> hits = relationshipIndex().get("name", value);
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(relationship(), hits.next());
hits.close();
}
@Test
public void testNotFoundInRelationshipIndex() {
IndexHits<Relationship> hits = relationshipIndex().get("foo", "bar");
Assert.assertEquals("no index results", false, hits.hasNext());
hits.close();
}
@Test
@@ -86,6 +92,8 @@ public class RestIndexTest extends RestTestBase {
nodeIndex().remove(node(), "time", value);
IndexHits<Node> hitsAfterRemove = nodeIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
hits.close();
hitsAfterRemove.close();
}
@Test
@@ -98,6 +106,8 @@ public class RestIndexTest extends RestTestBase {
relationshipIndex().remove(relationship(), "time", value);
IndexHits<Relationship> hitsAfterRemove = relationshipIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
hits.close();
hitsAfterRemove.close();
}
private Index<Node> nodeIndex() {

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-neo4j-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-neo4j</artifactId>
@@ -200,8 +200,8 @@
</dependency>
<dependency>
<groupId>com.tinkerpop</groupId>
<artifactId>gremlin</artifactId>
<groupId>com.tinkerpop.gremlin</groupId>
<artifactId>gremlin-groovy</artifactId>
<optional>true</optional>
</dependency>

View File

@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.querydsl;
import com.mysema.query.annotations.*;
import com.mysema.query.apt.DefaultConfiguration;
import com.mysema.query.apt.Processor;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.neo4j.annotation.NodeEntity;
import javax.annotation.processing.AbstractProcessor;

View File

@@ -314,6 +314,11 @@ public abstract class AbstractGraphRepository<S extends PropertyContainer, T> im
}
}
@Override
public Class getStoredJavaType(Object entity) {
return template.getStoredJavaType(entity);
}
@Override
public void delete(T entity) {
template.delete(entity);

View File

@@ -123,4 +123,6 @@ public interface CRUDRepository<T> extends PagingAndSortingRepository<T, Long> {
*/
Page<T> findAll(Pageable pageable);
Class getStoredJavaType(Object entity);
}

View File

@@ -43,6 +43,11 @@ public class NodeGraphRepositoryImpl<T> extends AbstractGraphRepository<Node, T>
return template.createRelationshipBetween(start,end,relationshipEntityClass,relationshipType,false);
}
@Override
public <R> R createDuplicateRelationshipBetween(T start, Object end, Class<R> relationshipEntityClass, String relationshipType) {
return template.createRelationshipBetween(start,end,relationshipEntityClass,relationshipType,true);
}
@Override
public <R> R getRelationshipBetween(T start, Object end, Class<R> relationshipEntityClass, String relationshipType) {
return template.getRelationshipBetween(start,end,relationshipEntityClass,relationshipType);

View File

@@ -21,6 +21,7 @@ package org.springframework.data.neo4j.repository;
*/
public interface RelationshipOperationsRepository<T> {
<R> R createRelationshipBetween(T start, Object end, Class<R> relationshipEntityClass, String relationshipType);
<R> R createDuplicateRelationshipBetween(T start, Object end, Class<R> relationshipEntityClass, String relationshipType);
<R> R getRelationshipBetween(T start, Object end, Class<R> relationshipEntityClass, String relationshipType);
void deleteRelationshipBetween(T start, Object end, String type);
}

View File

@@ -23,8 +23,6 @@ import org.springframework.data.neo4j.support.query.QueryEngine;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import java.util.Map;
/**
* @author mh
* @since 31.10.11
@@ -66,6 +64,7 @@ class CypherGraphRepositoryQuery extends GraphRepositoryQuery {
private String getSortOrder(Sort sort) {
String result = "";
for (Sort.Order order : sort) {
if (!result.isEmpty()) result += ", ";
result += order.getProperty() + " " + order.getDirection();
}
return result;

View File

@@ -622,4 +622,11 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
ParameterCheck.notNull(entity,"entity");
return getMappingPolicy(entity.getClass());
}
@Override
public Class getStoredJavaType(Object entity) {
final PropertyContainer container = entity instanceof PropertyContainer ? (PropertyContainer)entity: getPersistentState(entity);
if (container==null) return null;
return getInfrastructure().getTypeRepresentationStrategies().getJavaType(container);
}
}

View File

@@ -34,6 +34,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
/**
@@ -260,14 +261,15 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
}
@Override
public boolean isEntity() {
return super.isEntity() && (hasRelationshipEntityType() || hasNodeEntityType());
return super.isEntity() && (isRelationshipEntity(getType()) || isNodeEntity(getType()));
}
private boolean hasRelationshipEntityType() {
return getType().isAnnotationPresent(RelationshipEntity.class);
private boolean isRelationshipEntity(final Class<?> type) {
return type.isAnnotationPresent(RelationshipEntity.class);
}
private boolean hasNodeEntityType() {
return getType().isAnnotationPresent(NodeEntity.class);
private boolean isNodeEntity(Class<?> type) {
return type.isAnnotationPresent(NodeEntity.class);
}
public String getIndexKey() {
@@ -300,4 +302,17 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
public boolean isTransient() {
return false;
}
@Override
public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
final Iterable<? extends TypeInformation<?>> result = super.getPersistentEntityType();
for (Iterator<? extends TypeInformation<?>> it = result.iterator(); it.hasNext(); ) {
final TypeInformation<?> typeInformation = it.next();
final Class type = typeInformation.getType();
if (isNodeEntity(type) || isRelationshipEntity(type)) continue;
System.out.println("removing "+getName()+" "+type+" "+typeInformation.getActualType().getType());
it.remove();
}
return result;
}
}

View File

@@ -59,7 +59,12 @@ public class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4j
@Override
public void verify() {
super.verify();
if (!isManaged() && getIdProperty()==null) throw new MappingException("No id property in "+this);
if (isManaged()) {
return;
}
final Neo4jPersistentProperty idProperty = getIdProperty();
if (idProperty == null) throw new MappingException("No id property in " + this);
if (idProperty.getType().isPrimitive()) throw new MappingException("The type of the id-property in " + idProperty+" must not be a primitive type but an object type like java.lang.Long");
}
public boolean useShortNames() {

View File

@@ -83,13 +83,7 @@ public class SourceStateTransmitter<S extends PropertyContainer> {
}
private <T> T getProperty(BeanWrapper<Neo4jPersistentEntity<Object>, Object> wrapper, Neo4jPersistentProperty property, Class<T> type, boolean fieldAccessOnly) {
try {
return wrapper.getProperty(property, type, fieldAccessOnly);
} catch (IllegalAccessException e) {
throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e);
} catch (InvocationTargetException e) {
throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e.getTargetException());
}
return wrapper.getProperty(property, type, fieldAccessOnly);
}
private <R> Object getProperty(BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, Neo4jPersistentProperty property) {

View File

@@ -38,7 +38,7 @@ public class GremlinExecutor {
private volatile ScriptEngine engine;
private ScriptEngine createScriptEngine() {
return new ScriptEngineManager().getEngineByName("gremlin");
return new ScriptEngineManager().getEngineByName("gremlin-groovy");
}
private static final AtomicInteger executionCount = new AtomicInteger();
@@ -62,7 +62,7 @@ public class GremlinExecutor {
private Bindings createBindings(Map<String, Object> params) {
final Bindings bindings = new SimpleBindings();
bindings.put(GRAPH_VARIABLE, new Neo4jGraph(graphDatabaseService));
bindings.put(GRAPH_VARIABLE, new Neo4jGraph(graphDatabaseService,false));
if (params==null) return bindings;
for (Map.Entry<String, Object> entry : params.entrySet()) {
bindings.put(entry.getKey(),entry.getValue());

View File

@@ -0,0 +1,115 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.support.typerepresentation;
import java.lang.annotation.Annotation;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.ClosableIterable;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.support.index.ClosableIndexHits;
import org.springframework.data.neo4j.support.index.IndexProvider;
import org.springframework.data.neo4j.support.index.IndexType;
public abstract class AbstractIndexingTypeRepresentationStrategy<S extends PropertyContainer> implements
TypeRepresentationStrategy<S> {
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
protected String INDEX_NAME;
protected final GraphDatabase graphDb;
protected final EntityTypeCache typeCache;
protected final IndexProvider indexProvider;
private final Class<? extends PropertyContainer> clazz;
private final Class<? extends Annotation> typeEntityClass;
public AbstractIndexingTypeRepresentationStrategy(GraphDatabase graphDb, IndexProvider indexProvider,
final String indexName, final Class<? extends PropertyContainer> clazz,
final Class<? extends Annotation> typeEntityClass) {
this.graphDb = graphDb;
this.indexProvider = indexProvider;
typeCache = new EntityTypeCache();
INDEX_NAME = indexName;
this.clazz = clazz;
this.typeEntityClass = typeEntityClass;
}
@SuppressWarnings("unchecked")
public Index<S> getTypesIndex() {
return (Index<S>) graphDb.createIndex(clazz, INDEX_NAME, IndexType.SIMPLE);
}
@Override
public void postEntityCreation(S state, Class<?> type) {
addToTypesIndex(state, type);
state.setProperty(TYPE_PROPERTY_NAME, type.getName());
}
@Override
public long count(Class<?> entityClass) {
long count = 0;
final IndexHits<S> hits = getTypesIndex().get(INDEX_KEY, entityClass.getName());
while (hits.hasNext()) {
hits.next();
count++;
}
return count;
}
@Override
public void preEntityRemoval(S state) {
getTypesIndex().remove(state);
}
@Override
public <U> ClosableIterable<S> findAll(Class<U> clazz) {
return findAllRelBacked(clazz);
}
@Override
public <U> Class<U> getJavaType(S propertyContainer) {
if (propertyContainer == null)
throw new IllegalArgumentException("Relationship or Node is null");
String className = (String) propertyContainer.getProperty(TYPE_PROPERTY_NAME);
return typeCache.getClassForName(className);
}
protected void addToTypesIndex(S relationshipOrNode, Class<?> entityClass) {
Class<?> type = entityClass;
while (type.getAnnotation(typeEntityClass) != null) {
String value = entityClass.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(entityClass);
getTypesIndex().add(relationshipOrNode, INDEX_KEY, value);
type = type.getSuperclass();
}
}
@SuppressWarnings("hiding")
private <Object> ClosableIterable<S> findAllRelBacked(Class<Object> clazz) {
String value = clazz.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(clazz);
final IndexHits<S> allEntitiesOfType = getTypesIndex().get(INDEX_KEY, value);
return new ClosableIndexHits<S>(allEntitiesOfType);
}
}

View File

@@ -17,89 +17,31 @@
package org.springframework.data.neo4j.support.typerepresentation;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.ClosableIterable;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.index.ClosableIndexHits;
import org.springframework.data.neo4j.support.index.IndexProvider;
import org.springframework.data.neo4j.support.index.IndexType;
public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
public class IndexingNodeTypeRepresentationStrategy extends AbstractIndexingTypeRepresentationStrategy<Node> implements
NodeTypeRepresentationStrategy {
public static final String INDEX_NAME = "__types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private final GraphDatabase graphDb;
private final EntityTypeCache typeCache;
private final IndexProvider indexProvider;
public IndexingNodeTypeRepresentationStrategy(GraphDatabase graphDb, IndexProvider indexProvider) {
this.graphDb = graphDb;
this.indexProvider = indexProvider;
typeCache = new EntityTypeCache();
}
private Index<Node> getNodeTypesIndex() {
return graphDb.createIndex(Node.class, INDEX_NAME, IndexType.SIMPLE);
super(graphDb, indexProvider, INDEX_NAME, Node.class, NodeEntity.class);
}
@Override
public void postEntityCreation(Node state, Class<?> type) {
addToNodeTypesIndex(state, type);
state.setProperty(TYPE_PROPERTY_NAME, type.getName());
}
private void addToNodeTypesIndex(Node node, Class<?> entityClass) {
protected void addToTypesIndex(Node node, Class<?> entityClass) {
Class<?> klass = entityClass;
while (klass.getAnnotation(NodeEntity.class) != null) {
String value = klass.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(klass);
getNodeTypesIndex().add(node, INDEX_KEY, value);
getTypesIndex().add(node, INDEX_KEY, value);
klass = klass.getSuperclass();
}
}
@Override
public <U> ClosableIterable<Node> findAll(Class<U> clazz) {
return findAllNodeBacked(clazz);
}
private <Object> ClosableIterable<Node> findAllNodeBacked(Class<Object> clazz) {
String value = clazz.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(clazz);
final IndexHits<Node> allEntitiesOfType = getNodeTypesIndex().get(INDEX_KEY, value);
return new ClosableIndexHits<Node>(allEntitiesOfType);
}
@Override
public long count(Class<?> entityClass) {
long count = 0;
for (Object o : getNodeTypesIndex().get(INDEX_KEY, entityClass.getName())) {
count += 1;
}
return count;
}
@SuppressWarnings("unchecked")
@Override
public Class<?> getJavaType(Node node) {
if (node == null) throw new IllegalArgumentException("Node is null");
String className = (String) node.getProperty(TYPE_PROPERTY_NAME);
return typeCache.getClassForName(className);
}
@Override
public void preEntityRemoval(Node state) {
getNodeTypesIndex().remove(state);
}
}
}

View File

@@ -17,92 +17,18 @@
package org.springframework.data.neo4j.support.typerepresentation;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.ClosableIterable;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.core.RelationshipTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.index.ClosableIndexHits;
import org.springframework.data.neo4j.support.index.IndexProvider;
import org.springframework.data.neo4j.support.index.IndexType;
public class IndexingRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy {
public class IndexingRelationshipTypeRepresentationStrategy extends
AbstractIndexingTypeRepresentationStrategy<Relationship> implements RelationshipTypeRepresentationStrategy {
public static final String INDEX_NAME = "__rel_types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private final GraphDatabase graphDb;
private final EntityTypeCache typeCache;
private final IndexProvider indexProvider;
public IndexingRelationshipTypeRepresentationStrategy(GraphDatabase graphDb, IndexProvider indexProvider) {
this.graphDb = graphDb;
this.indexProvider = indexProvider;
typeCache = new EntityTypeCache();
}
private Index<Relationship> getRelTypesIndex() {
return graphDb.createIndex(Relationship.class,INDEX_NAME, IndexType.SIMPLE);
}
@Override
public void postEntityCreation(Relationship state, Class<?> type) {
addToTypesIndex(state, type);
state.setProperty(TYPE_PROPERTY_NAME, type.getName());
}
private void addToTypesIndex(Relationship node, Class<?> entityClass) {
Class<?> type = entityClass;
while (type.getAnnotation(RelationshipEntity.class) != null) {
String value = entityClass.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(entityClass);
getRelTypesIndex().add(node, INDEX_KEY, value);
type = type.getSuperclass();
}
}
@Override
public <U> ClosableIterable<Relationship> findAll(Class<U> clazz) {
return findAllRelBacked(clazz);
}
private <Object> ClosableIterable<Relationship> findAllRelBacked(Class<Object> clazz) {
String value = clazz.getName();
if (indexProvider != null)
value = indexProvider.createIndexValueForType(clazz);
final IndexHits<Relationship> allEntitiesOfType = getRelTypesIndex().get(INDEX_KEY, value);
return new ClosableIndexHits<Relationship>(allEntitiesOfType);
}
@Override
public long count(Class<?> entityClass) {
long count = 0;
final IndexHits<Relationship> hits = getRelTypesIndex().get(INDEX_KEY, entityClass.getName());
while (hits.hasNext()) {
hits.next();
count++;
}
return count;
}
@Override
@SuppressWarnings("unchecked")
public Class<?> getJavaType(Relationship relationship) {
if (relationship == null) throw new IllegalArgumentException("Relationship is null");
String className = (String) relationship.getProperty(TYPE_PROPERTY_NAME);
return typeCache.getClassForName(className);
}
@Override
public void preEntityRemoval(Relationship state) {
getRelTypesIndex().remove(state);
super(graphDb, indexProvider, INDEX_NAME, Relationship.class, RelationshipEntity.class);
}
}

View File

@@ -268,4 +268,6 @@ public interface Neo4jOperations {
MappingPolicy getMappingPolicy(Class<?> targetType);
ResultConverter getDefaultConverter();
Class getStoredJavaType(Object entity);
}

View File

@@ -18,9 +18,11 @@ package org.springframework.data.neo4j.transaction;
import org.neo4j.helpers.Service;
import org.neo4j.kernel.impl.core.KernelPanicEventGenerator;
import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
import org.neo4j.kernel.impl.transaction.AbstractTransactionManager;
import org.neo4j.kernel.impl.transaction.TransactionManagerProvider;
import org.neo4j.kernel.impl.transaction.TxHook;
import org.neo4j.kernel.impl.util.StringLogger;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
@@ -33,7 +35,7 @@ public class SpringProvider extends TransactionManagerProvider
}
@Override
protected AbstractTransactionManager loadTransactionManager(String s, KernelPanicEventGenerator kernelPanicEventGenerator, TxHook txHook) {
protected AbstractTransactionManager loadTransactionManager(String s, KernelPanicEventGenerator kernelPanicEventGenerator, TxHook txHook, StringLogger stringLogger, FileSystemAbstraction fileSystemAbstraction) {
return new SpringServiceImpl();
}
}

View File

@@ -108,7 +108,7 @@ public class DataGraphNamespaceHandlerTest {
Neo4jTemplate template = config.neo4jTemplate;
Assert.assertNotNull("template", template);
AbstractGraphDatabase graphDatabaseService = (AbstractGraphDatabase) template.getGraphDatabaseService();
Assert.assertEquals("store-dir", "target/config-test", graphDatabaseService.getStoreDir());
Assert.assertTrue("store-dir", graphDatabaseService.getStoreDir().endsWith("target/config-test"));
Assert.assertNotNull("graphDatabaseService",config.graphDatabaseService);
Assert.assertNotNull("transactionManager",config.transactionManager);
config.graphDatabaseService.shutdown();

View File

@@ -17,7 +17,9 @@ package org.springframework.data.neo4j.mapping;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.model.PrimitiveIdEntity;
import org.springframework.data.neo4j.support.index.IndexType;
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.support.mapping.Neo4jPersistentEntityImpl;
@@ -54,4 +56,9 @@ public class Neo4jMappingContextTest {
assertEquals(IndexType.SIMPLE,nameProperty.getIndexInfo().getIndexType());
assertEquals(false,nameProperty.isRelationship());
}
@Test(expected = MappingException.class)
public void testPrimitiveGraphIdFails() {
mappingContext.getPersistentEntity(PrimitiveIdEntity.class);
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.data.neo4j.annotation.NodeEntity;
@NodeEntity
public abstract class Car {
@GraphId
long id;
Long id;
public Car() {
}

View File

@@ -32,7 +32,7 @@ import java.util.HashSet;
import java.util.Set;
@NodeEntity
public class Group {
public class Group implements IGroup {
public final static String OTHER_NAME_INDEX = "other_name";
public static final String SEARCH_GROUPS_INDEX = "search_groups";

View File

@@ -0,0 +1,23 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.model;
/**
* @author mh
* @since 23.12.11
*/
public interface IGroup {
}

View File

@@ -16,8 +16,7 @@
package org.springframework.data.neo4j.model;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.*;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.fieldaccess.DynamicProperties;
import org.springframework.data.neo4j.support.index.IndexType;
@@ -27,12 +26,14 @@ import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.Map;
import java.util.Set;
@NodeEntity
public class Person {
public static final String NAME_INDEX = "name_index";
public static final org.neo4j.graphdb.RelationshipType KNOWS = DynamicRelationshipType.withName("knows");
@GraphId
private Long graphId;
@@ -86,9 +87,26 @@ public class Person {
@Query("start person=node({self}) match (person)<-[:persons]-(team)-[:persons]->(member) return member")
private Iterable<Person> otherTeamMembers;
@Query("start person=node({self}) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name, member.age")
@Query("start person=node({self}) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name?, member.age?")
private Iterable<Map<String,Object>> otherTeamMemberData;
@RelatedTo(elementClass = Group.class, type = "interface_test", direction = Direction.OUTGOING)
private Set<IGroup> groups;
RootEntity root;
public RootEntity getRoot() {
return root;
}
public void setRoot(RootEntity root) {
this.root = root;
}
public Set<IGroup> getGroups() {
return groups;
}
public Person(Node n) {
this.graphId = n.getId();
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.model;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
/**
* @author mh
* @since 14.12.11
*/
@NodeEntity
public class PrimitiveIdEntity {
@GraphId long id;
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.model;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
/**
* @author mh
* @since 23.12.11
*/
@NodeEntity
public class RootEntity {
@GraphId Long id;
String rootName;
public Long getId() {
return id;
}
public String getRootName() {
return rootName;
}
public void setRootName(String rootName) {
this.rootName = rootName;
}
}

View File

@@ -20,6 +20,7 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.Node;
import org.neo4j.helpers.collection.IteratorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,6 +31,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.neo4j.model.Friendship;
import org.springframework.data.neo4j.model.Group;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.model.RootEntity;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.conversion.NoSuchColumnFoundException;
import org.springframework.data.neo4j.support.node.Neo4jHelper;
@@ -46,12 +48,14 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
import static org.neo4j.helpers.collection.IteratorUtil.addToCollection;
@@ -143,7 +147,7 @@ public class GraphRepositoryTest {
assertThat( asCollection( teamMembers ), hasItems( testTeam.michael, testTeam.david, testTeam.emil ) );
}
@Test @Transactional
@Test @Transactional
public void testFindIterableOfPersonWithQueryAnnotationSpatial() {
Iterable<Person> teamMembers = personRepository.findWithinBoundingBox("personLayer", 55, 15, 57, 17);
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david));
@@ -192,7 +196,7 @@ public class GraphRepositoryTest {
@Test @Transactional
public void testFindPaged() {
final PageRequest page = new PageRequest(0, 1, Sort.Direction.ASC, "member.name");
final PageRequest page = new PageRequest(0, 1, Sort.Direction.ASC, "member.name","member.age");
Page<Person> teamMemberPage1 = personRepository.findAllTeamMembersPaged(testTeam.sdg, page);
assertThat(teamMemberPage1, hasItem(testTeam.david));
}
@@ -220,9 +224,27 @@ public class GraphRepositoryTest {
assertEquals(asList(testTeam.michael, testTeam.emil, testTeam.david), asCollection(teamMembers));
}
@Test
@Transactional
public void testGetStoredJavaType() {
Person p = personRepository.save(new Person());
assertEquals(Person.class, personRepository.getStoredJavaType(p));
assertEquals(Person.class, personRepository.getStoredJavaType(neo4jTemplate.getPersistentState(p)));
assertEquals(null, personRepository.getStoredJavaType(new Person()));
}
@Test
@Transactional
public void testTemplateGetStoredJavaType() {
Person p = neo4jTemplate.save(new Person());
assertEquals(Person.class, neo4jTemplate.getStoredJavaType(p));
assertEquals(Person.class, neo4jTemplate.getStoredJavaType(neo4jTemplate.getPersistentState(p)));
assertEquals(null, neo4jTemplate.getStoredJavaType(new Person()));
}
@Test @Transactional
public void testFindSortedNull() {
Iterable<Person> teamMembers = personRepository.findAllTeamMembersSorted(testTeam.sdg, null);
Collection<Person> teamMembers = IteratorUtil.asCollection(personRepository.findAllTeamMembersSorted(testTeam.sdg, null));
assertThat(teamMembers, hasItems(testTeam.michael, testTeam.emil, testTeam.david));
}
@@ -264,4 +286,38 @@ public class GraphRepositoryTest {
assertEquals(2, IteratorUtil.count(loaded.getFriendships()));
assertThat(loaded.getFriendships(),hasItem(friendship));
}
@Test @Transactional
public void testCreateDuplicateRelationship() {
assertEquals(1, IteratorUtil.count(neo4jTemplate.<Node>getPersistentState(testTeam.michael).getRelationships(Person.KNOWS)));
personRepository.createDuplicateRelationshipBetween(testTeam.michael, testTeam.david, Friendship.class, "knows");
assertEquals(2, IteratorUtil.count(neo4jTemplate.<Node>getPersistentState(testTeam.michael).getRelationships(Person.KNOWS)));
}
@Test @Transactional
public void testUseInterfaceInRelationships() {
final Person p = neo4jTemplate.save(new Person());
final Group group = neo4jTemplate.save(new Group());
neo4jTemplate.createRelationshipBetween(neo4jTemplate.<Node>getPersistentState(p),neo4jTemplate.<Node>getPersistentState(group),"interface_test",null);
final Person p2 = neo4jTemplate.fetch(p);
assertEquals(group,IteratorUtil.firstOrNull(p2.getGroups()));
}
@Test @Transactional
public void testConnectToRootEntity() {
final Node referenceNode = neo4jTemplate.getReferenceNode();
neo4jTemplate.postEntityCreation(referenceNode,RootEntity.class);
final RootEntity root = neo4jTemplate.findOne(referenceNode.getId(), RootEntity.class);
root.setRootName("RootName");
neo4jTemplate.save(root);
assertEquals(referenceNode.getId(), (long) root.getId());
assertEquals("RootName", referenceNode.getProperty("rootName"));
assertEquals("RootName", root.getRootName());
final Person person = new Person();
person.setRoot(root);
neo4jTemplate.save(person);
final Person p2 = neo4jTemplate.findOne(person.getId(), Person.class);
assertEquals(root.getId(),p2.getRoot().getId());
}
}

View File

@@ -34,7 +34,7 @@ import java.util.Map;
* @author Oliver Gierke
* @since 29.03.11
*/
public interface PersonRepository extends GraphRepository<Person>, NamedIndexRepository<Person>, SpatialRepository<Person>, PersonRepositoryFriendship, CypherDslRepository<Person> {
public interface PersonRepository extends GraphRepository<Person>, NamedIndexRepository<Person>, SpatialRepository<Person>, PersonRepositoryFriendship, CypherDslRepository<Person>, RelationshipOperationsRepository<Person> {
@Query("start team=node({p_team}) match (team)-[:persons]->(member) return member")
Iterable<Person> findAllTeamMembers(@Param("p_team") Group team);

View File

@@ -17,11 +17,13 @@ package org.springframework.data.neo4j.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.internal.matchers.IsCollectionContaining;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.kernel.Traversal;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,9 +47,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.*;
import static org.junit.Assert.*;
import static org.neo4j.graphdb.Direction.OUTGOING;
@@ -306,7 +306,11 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
public void testCreateRelationshipBetweenNodes() throws Exception {
final Friendship friendship = neo4jOperations.createRelationshipBetween(testTeam.david, testTeam.emil, Friendship.class, "knows", false);
assertEquals(friendship.getId(),(Long)getNodeState(testTeam.david).getSingleRelationship(KNOWS, OUTGOING).getId());
final List<Friendship> friendships = IteratorUtil.addToCollection(friendshipRepository.findAll(), new ArrayList<Friendship>());
assertEquals(2,friendships.size());
assertThat(friendships, IsCollectionContaining.hasItems(testTeam.friendShip, friendship));
}
@Test @Transactional
public void testCreateDuplicateRelationshipBetweenNodes() throws Exception {
neo4jOperations.createRelationshipBetween(testTeam.michael, testTeam.david, Friendship.class, "knows", true);

View File

@@ -29,10 +29,6 @@
<firstname>Michael</firstname>
<surname>Hunger</surname>
</author>
<author>
<firstname>David</firstname>
<surname>Montag</surname>
</author>
</authorgroup>
<legalnotice>
@@ -134,6 +130,7 @@
<xi:include href="reference/setup.xml"/>
<xi:include href="reference/cross-store.xml"/>
<xi:include href="reference/samples.xml"/>
<xi:include href="reference/heroku.xml"/>
<xi:include href="reference/performance.xml" />
<xi:include href="reference/aspectj-details.xml" />
<xi:include href="reference/neo4j-server.xml" />

View File

@@ -59,6 +59,10 @@
advanced AspectJ usage which caused some headaches. He always quickly solved our issues and gave us excellent
answers.
</para>
<para>
Many thanks to our colleagues David Montag, Andreas Kollegger and Rickard Öberg who not only contributed to
Spring Data Neo4j but also provided content and feedback for this book.
</para>
<para>
We also appreciate very much the foresight of Rod Johnson and Emil Eifrem to initiate the project, and now
also providing great forewords. Their leadership inspired collaboration between the engineering teams at

Binary file not shown.

View File

@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="reference:heroku">
<title>Heroku: Seeding the Cloud</title>
<para>
Deploying your application into the cloud is a great way to scale from from "wouldn't it be cool if.." to
giving interviews to Forbes, Fast Company, and Jimmy Fallon. Heroku makes it super easy to provision everying
you need, including a Neo4j Add-on. With a few simple adjustments, your Spring Data Neo4j application is
ready to take that first step into the cloud.
</para>
<para>
To deploy your Spring Data Neo4j web application to Heroku, you'll need:
</para>
<itemizedlist mark="circle">
<listitem><para>account on <ulink url="http://heroku.com">Heroku</ulink></para></listitem>
<listitem><para>git command line</para></listitem>
<listitem><para>maven-based project</para></listitem>
<listitem><para>standard Spring MV Servlet application</para></listitem>
<listitem><para>well, and Spring Data Neo4j REST</para></listitem>
</itemizedlist>
<para>
For reference, the following sections detail the steps taken to make the Spring Data Neo4j Todos
example ready for deployment to Heroku.
</para>
<section>
<title>Create a Self-Hosted Web Application</title>
<para>
Usually, a Spring MVC application is bundled into a war and deployed to an application server
like Tomcat. But Heroku can host any kind of java application. It just needs to know what to
launch. So, we'll transform the war into a self-hosted servlet using an embedded Jetty server,
then add a startup script to launch it.
</para>
<para>
First, we'll add the dependencies for Jetty to the <code>pom.xml</code>:
</para>
<example>
<title>Jetty dependencies - pom.xml</title>
<programlisting language="xml"><![CDATA[
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>7.4.4.v20110707</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>2.1.v20100127</version>
</dependency>
]]></programlisting>
</example>
<para>
Then we'll change the scope of the servlet-api artifact from <code>provided</code> to <code>compile</code>.
This library is normally provided at runtime by the application container. Since we're self-hosting,
it needs to be included directly. Make sure the servlet-api dependency looks like this:
</para>
<example>
<title>servlet-api dependencies - pom.xml</title>
<programlisting language="xml"><![CDATA[
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>compile</scope>
</dependency>
]]></programlisting>
</example>
<para>
We could provide a complicated command-line to Heroku to launch the app. Instead, we'll simplify
the command-line by using the <code>appassembler-maven-plugin</code> to create a launch script. Add the plugin
to your pom's <code>build/plugins</code> section:
</para>
<example>
<title>appassembler-maven-plugin configuration pom.xml</title>
<programlisting language="xml"><![CDATA[
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>assemble</goal></goals>
<configuration>
<assembleDirectory>target</assembleDirectory>
<extraJvmArguments>-Xmx512m</extraJvmArguments>
<programs>
<program>
<mainClass>Main</mainClass>
<name>webapp</name>
</program>
</programs>
</configuration>
</execution>
</executions>
</plugin>
]]></programlisting>
</example>
<para>
Finally, switch the packaging from <code>war</code> to <code>jar</code>. That's it for the pom.
</para>
<para>
Now that the application is ready to be self-hosted, create a simple <code>Main</code> to
bootstrap Jetty and host the servlet.
</para>
<example>
<title>src/main/java/Main.java</title>
<programlisting language="java"><![CDATA[
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class Main {
public static void main(String[] args) throws Exception {
String webappDirLocation = "src/main/webapp/";
String webPort = System.getenv("PORT");
if(webPort == null || webPort.isEmpty()) {
webPort = "8080";
}
Server server = new Server(Integer.valueOf(webPort));
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setDescriptor(webappDirLocation+"/WEB-INF/web.xml");
root.setResourceBase(webappDirLocation);
root.setParentLoaderPriority(true);
server.setHandler(root);
server.start();
server.join();
}
}
]]></programlisting>
</example>
<para>
Notice the use of environment variable "PORT" for discovering which port to use. Heroku and the
Neo4j Add-on use a number of environment variable to configure the application. Next, we'll
modify the Spring application context to use the Neo4j variables for specifying the connection
to Neo4j itself.
</para>
<para>
In the SDN Todos example, <code>src/main/resources/META-INF/spring/ applicationContext-graph.xml</code>
was modified to look like this:
</para>
<example>
<title>Spring Data Neo4j REST configuration - applicationContext-graph.xml</title>
<programlisting language="xml"><![CDATA[
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService"
class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg index="0" value="${NEO4J_REST_URL}" />
<constructor-arg index="1" value="${NEO4J_LOGIN}" />
<constructor-arg index="2" value="${NEO4J_PASSWORD}" />
</bean>
]]></programlisting>
</example>
<para>
Before provisioning at Heroku, test the application locally. First make sure you've got
Neo4j server running locally, using default configuration. Then set the following
environment variables:
</para>
<example>
<title>environment variables</title>
<programlisting><![CDATA[
export NEO4J_REST_URL=http://localhost:7474/db/data
export NEO4J_LOGIN=""
export NEO4J_PASSWORD=""
]]></programlisting>
</example>
<para>
Now you can launch the app by running <code>sh target/bin/webapp</code>. If running the SDN Todos
example, you can test it by running <code>./bin/todos list</code>. That should return an empty
JSON array, since no todos have been created yet.
</para>
<para>
For details about the <code>todos</code> script, see the <code>readme</code> included with the example.
</para>
</section>
<section>
<title>Deploy to Heroku</title>
<para>
With a self-hosted application ready, deploying to Heroku needs a few more steps. First,
create a <code>Procfile</code> at the top-level of the project, which will contain a single
line identifying the command line which launches the application.
</para>
<para>
The contents of the <code>Procfile</code> should contain:
</para>
<example>
<title>Procfile</title>
<programlisting><![CDATA[
web: sh target/bin/webapp
]]></programlisting>
</example>
<para>
<example>
<title>deploy to heroku</title>
<programlisting><![CDATA[
# Initialize a local git repository, adding all the project files
git init
git add .
git commit -m "initial commit"
# Provision a Heroku stack, add the Neo4j Add-on and deploy the appication
heroku create --stack cedar
heroku addons:add neo4j
git push heroku master
]]></programlisting>
</example>
</para>
<note>
<para>
Note that the stack must be "cedar" to support running Java. Check that the process
is running by using <code>heroku ps</code>, which should show a "web.1" process
in the "up" state. Success!
</para>
</note>
<para>
For the SDN Todos application, you can try out the remote application using the <code>-r</code>
switch with the <code>bin/todo</code> script like this:
</para>
<example>
<title>Session with todo script</title>
<programlisting><![CDATA[
./bin/todo -r mk "tweet thanks for the good work @mesirii @akollegger"
./bin/todo -r list
]]></programlisting>
</example>
<para>
To see the Neo4j graph you just created through Heroku, use <code>heroku config</code> to reveal
the <code>NEO4J_URL</code> environment variable, which will take you to Neo4j's Webadmin.
</para>
</section>
</chapter>

View File

@@ -66,7 +66,7 @@ public void foo( @Context WorldRepository repo ) {
}
]]></programlisting>
</example>
The <code>SpringPluginInitializer</code> merges the GraphDatabaseService with the Spring configuration
The <code>SpringPluginInitializer</code> merges the server provided <code>GraphDatabaseService</code> with the Spring configuration
and registers the named beans as Jersey <code>Injectables</code>. It is still necessary to list the
initializer's fully qualified class name in a file named
<code>META-INF/services/org.neo4j.server.plugins.PluginLifecycle</code>. The Neo4j Server can then pick
@@ -76,21 +76,27 @@ public void foo( @Context WorldRepository repo ) {
<section>
<title>Using Spring Data Neo4j as a REST client</title>
<para>
Spring Data Neo4j can use a set of Java REST bindings which come as a drop in replacement for the
GraphDatabaseService API. By simply configuring the <code>graphDatabaseService</code> to be a
<code>RestGraphDatabase</code> pointing to a Neo4j Server instance.
To use REST-API the Neo4j Server exposes, one would either go with REST libraries on the lower level or choose one of the Neo4j related
rest drivers in various languages. For Java Neo4j provides the <ulink url="https://github.com/neo4j/java-rest-binding">Neo4j Java REST bindings</ulink>
which come as a drop in replacement for the <code>GraphDatabaseService</code> API. Spring Data Neo4j REST uses those bindings to provide seamless
access to a remote Neo4j Database.
</para>
<para>
By simply configuring the <code>graphDatabaseService</code> to be a <code>SpringRestGraphDatabase</code> pointing to a Neo4j Server instance and
referring to that from <code>&lt;neo4j:config&gt;</code> Spring Data Neo4j will use the server side database for both the simple mapping as
well as the advanced mapping.
</para>
<note>
<para>
The Neo4j Server REST API does not allow for transactions to span across requests, which means
that Spring Data Neo4j is not transactional when running with a <code>RestGraphDatabase</code>.
that Spring Data Neo4j is not transactional across multiple operations when running with a <code>SpringRestGraphDatabase</code>.
</para>
</note>
<para>
Please also keep in mind that performing graph operations via the REST-API is about one order of
magnitude slower than local operations. Try to use the Neo4j Cypher query language,
server-side traversals (<code>RestTraversal</code>) or Gremlin expressions whenever possible for retrieving large sets of data.
Future versions of Spring Data Neo4j will use the more performant batching as well as a binary protocol.
Future versions of Spring Data Neo4j will use the more performant batch API as well as a binary protocol.
</para>
<para>
To set up your project to use the REST bindings, add this dependency to your pom.xml:
@@ -100,7 +106,7 @@ public void foo( @Context WorldRepository repo ) {
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>2.0.0.RC1</version>
<version>2.0.0.RELEASE</version>
</dependency>
]]></programlisting>
</example>
@@ -111,18 +117,22 @@ public void foo( @Context WorldRepository repo ) {
<programlisting language="xml"><![CDATA[<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg value="http://localhost:7474/db/data/"/>
<constructor-arg value="http://localhost:7474/db/data/" index="0"/>
<!-- for running against a server requiring authentication
<constructor-arg value="username" index="1"/>
<constructor-arg value="password" index="2"/>
-->
</bean>
]]></programlisting>
</example>
Your project is now set up to work against a remote Neo4j Server.
</para>
<para>
The remote REST implementation works for both the Neo4jTemplate as well as the GraphEntities. For traversals
and Cypher graph queries it is sensible to forward those to the remote endpoint and execute them there instead of
walking the graph over the wire. RestGraphDatabase already supports that by providing methods that forward
For traversals and Cypher and Gremlin graph queries it is sensible to forward those to the remote endpoint and execute them there
instead of walking the graph over the wire. SpringRestGraphDatabase already supports that by providing methods that forward
to the remote instance. (e.g. <code>queryEngineFor(), index() and createTraversalDescription()</code>).
Please use those methods when interacting with a remote server for optimal performance.
Please use those methods when interacting with a remote server for optimal performance. Those methods are also
used by the Neo4jTemplate and the mapping infrastructure automatically.
</para>
<para>

View File

@@ -160,7 +160,8 @@ for (Node foundNode : nodeIndex.get("property","value")) {
<ulink url="http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html">"Cypher"</ulink> which draws from many
sources. It resembles SQL but with an iconic representation of patterns in the graph (concepts drawn from SPARQL).
The Cypher execution engine was written in Scala to leverage the high expressiveness for lazy sequence operations of the language and the
parser combinator library.
parser combinator library. A screencast explaining the possibilities in detail can be found on the
<ulink url="http://video.neo4j.org/ybMbf/screencast-introduction-to-cypher/">Neo4j video site</ulink>.
</para>
<para>
Cypher queries always begin with a <code>start</code> set of nodes. Those can be either expressed by their

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -155,7 +155,7 @@
</section>
<section>
<title>Cypher examples</title>
<para>There is a <ulink url="http://neo4j.vidcaster.com/U2Y/introduction-to-cypher">screencast</ulink> available showing many features of the query language.
<para>There is a <ulink url="http://video.neo4j.org/ybMbf/screencast-introduction-to-cypher">screencast</ulink> available showing many features of the query language.
The following examples are taken from the cineasts dataset of the tutorial section.
<variablelist>
<varlistentry>
@@ -190,7 +190,7 @@
name can be split into its semantic parts and converted into a cypher query.
<code>@Indexed</code> fields will be converted into index-lookups of the <code>start</code> clause,
navigation along relationships will be reflected in the <code>match</code> clause properties with operators will end up as expressions in the
<code>where</code> clause. Order and limiting of the query will by handled by provided <code>Pageable</code> or <Sort></Sort> parameters.
<code>where</code> clause. Order and limiting of the query will by handled by provided <code>Pageable</code> or <code>Sort</code> parameters.
The other parameters will be used in the order they appear in the method signature so that should align with the expressions stated in the method name.
</para>
<para>

View File

@@ -7,7 +7,7 @@
<para>
The <code>Neo4jTemplate</code> offers the convenient API of Spring templates for the Neo4j graph
database. The Spring Data Neo4j Object Graph mapping builds upon the core functionality of the
template to persist objects to the graph and load them in a variety of ways.
template to persist objects to the graph and load them in a variety of ways.
The template handles the active mapping mode (<xref linkend="reference:mapping"/>) transparently.
</para>
<para>
@@ -17,6 +17,24 @@
</para>
<xi:include href="../../snippets/SnippetNeo4jTemplateMethods.xml"/>
<section>
<title>Core-Operations</title>
<para>
<code>Neo4jTemplate</code> provides access to some of the methods of the Neo4j-Core-API directly. So accessing
nodes and relationships (<code>getReferenceNode, getNode, getRelationship, getRelationshipBetween</code>),
creating nodes and relationships (<code>createNode, createNodeAs, createRelationshipBetween</code>)
and deleting them (<code>delete, deleteRelationshipBetween</code>) are supported. It also provides access to
the underlying GraphDatabase via <code>getGraphDatabase</code>.
</para>
</section>
<section>
<title>Entity-Persistence</title>
<para>
<code>Neo4jTemplate</code> allows to <code>save</code>,<code>find(One/All)</code>,<code>count</code><code>delete</code>
and <code>projectTo</code> entities. It provides the stored type information via <code>getStoredJavaType</code>
and can <code>fetch</code> lazy-loaded entities or <code>load</code> them alltogether.
</para>
</section>
<section>
<title>Result</title>
<para>
@@ -26,7 +44,7 @@
<code>ResultConverter&lt;FROM,TO&gt;</code> which takes care of custom conversions. By default most
query methods can already handle conversions from and to: Paths, Nodes, Relationship and GraphEntities
as well as conversions backed by registered ConversionServices. A converted <code>Result&lt;FROM&gt;</code> is an
<code>Iterable&lt;TO&gt;</code>. Results can be limited to a single value using the
<code>Iterable&lt;TO&gt;</code>. Results can be limited to a single value using the
<code>result.single() or result.singleOrNull()</code>
methods. It also offers support for a pure callback function using a <code>Handler&lt;T&gt;</code>.
</para>

View File

@@ -21,11 +21,9 @@
</para>
<para>
Stores entity types in the integrated index. Each entity node gets indexed with its type and
any supertypes that are also<code>@NodeEntity</code>-annotated. The special index used for this
is called<code>__types__</code>. Additionally, in order to get the type of an entity node, each
node has a property
<code>__type__</code>
with the fully qualified type of that entity.
all supertypes that are also <code>@NodeEntity</code>-annotated. The special index used for this
is named <code>__types__</code>. Additionally, in order to retrieve the type of an entity node, each
node has a property <code>__type__</code> with the fully qualified type of that entity.
</para>
</listitem>
<listitem>
@@ -57,6 +55,12 @@
<para>
<code>IndexingRelationshipTypeRepresentationStrategy</code>
</para>
<para>
Stores relationship entity types in the integrated index. Each entity relationship gets indexed with its type and
all supertypes that are also <code>@RelationshipEntity</code>-annotated. The special index used for this
is named <code>__rel_types__</code>. Additionally, in order to retrieve the type of an entity relationship, each
relationship has a property <code>__type__</code> with the fully qualified type of that entity.
</para>
</listitem>
<listitem>
<para>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -11,7 +11,7 @@
<mediaobject>
<imageobject>
<!--
http://yuml.me/diagram/scruffy;dir:lr/class/%23%20Cineasts.net%20Domain,%20%5BActor%7Cname;%7CplayedIn()%5D*-ACTS_IN-%3E%5BRole%7Cname;%5D,%20%5BRole%5D-ACTS_IN-%3E*%5BMovie%7Ctitle;year%5D,%20%5BUser%7Clogin;name;password;%7Crate();befriend();%5D*-RATED-%3E%5BRating%7Cstars;comment;%5D,%20%5BRating%5D-RATED-%3E*%5BMovie%5D,%20%5BUser%5D*-FRIEND-%3E*%5BUser%5D.png
http://yuml.me/diagram/scruffy;dir:lr/class/%23%20Cineasts.net%20Domain,%20%5BActor%7Cname;%7CplayedIn()%5D-ACTS_IN-%3E%5BRole%7Cname;%5D,%20%5BRole%5D-ACTS_IN-%3E%5BMovie%7Ctitle;year%5D,%20%5BUser%7Clogin;name;password;%7Crate();befriend();%5D-RATED-%3E%5BRating%7Cstars;comment;%5D,%20%5BRating%5D-RATED-%3E%5BMovie%5D,%20%5BUser%5D-FRIEND-%3E%5BUser%5D.png
-->
<imagedata fileref="domain.png"/>
</imageobject>

Some files were not shown because too many files have changed in this diff Show More