Skip to content
This repository was archived by the owner on Sep 1, 2023. It is now read-only.

Commit 3a9155b

Browse files
GH-27 - Add example for Neo4j embedded in combination with this starter.
This closes #26. Co-authored-by: Gerrit Meier <meistermeier@gmail.com>
1 parent 1ec4a9b commit 3a9155b

File tree

13 files changed

+711
-2
lines changed

13 files changed

+711
-2
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
with:
2121
java-version: ${{ matrix.java }}
2222
- name: Run Maven build (JDK 8)
23-
run: ./mvnw -pl !org.neo4j.doc.driver:testing-with-neo4j-harness --no-transfer-progress clean verify -Drevision=4.0 -Dchangelist=-SNAPSHOT
23+
run: ./mvnw -pl !org.neo4j.doc.driver:testing-with-neo4j-harness,!org.neo4j.doc.driver:embedded-bolt-connection --no-transfer-progress clean verify -Drevision=4.0 -Dchangelist=-SNAPSHOT
2424
if: matrix.java == 8
2525
- name: Run Maven build (JDK 11+)
2626
run: ./mvnw --no-transfer-progress clean verify -Drevision=4.0 -Dchangelist=-SNAPSHOT

docs/examples/index.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ include::{manualIncludeDir}../examples/web/README.adoc[leveloffset=+1]
114114

115115
include::{manualIncludeDir}../examples/reactive-web/README.adoc[leveloffset=+1]
116116

117+
include::{manualIncludeDir}../examples/embedded-bolt-connection/README.adoc[leveloffset=+1]
118+
117119
include::{manualIncludeDir}../examples/testing-with-neo4j-harness/README.adoc[leveloffset=+1]
118120

119121
include::{manualIncludeDir}../examples/dedicated-routing-driver/README.adoc[leveloffset=+1]

docs/manual/intro.adoc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,26 @@ The 1.7.x line of the starter is for the 1.7.x line of the driver, the 4.0.x lin
4141

4242
== Does it work with the embedded database?
4343

44-
No.
44+
If you enable the Bolt connector on the embedded instance, it does.
45+
46+
IMPORTANT: However, it *does not and will not* startup an embedded instance for you.
47+
There are two main reasons for this decisions:
48+
+
49+
First, we think that booting up a database from the connection layer (which the driver belongs to)
50+
or - even worse - from an object mapping layer - is wrong and violates the solid responsibility principle,
51+
thus, leading to all kinds of architectural problems.
52+
+
53+
Furthermore, Neo4j embedded is a full database engine and comes with a lot of dependencies and also in several
54+
versions and editions.
55+
Catering for all combinations and configuration possible will lead up to hard to maintain
56+
dependency management issues and also, duplication of configuration APIs.
57+
58+
There are two examples in which this starter works with an embedded database:
59+
60+
* <<testing-with-neo4j-harness>> shows a couple of ways to add a `@Bean` of type `org.neo4j.harness.Neo4j` (Neo4j 4.0) or `org.neo4j.harness.ServerControls` (Neo4j 3.5 and prior)
61+
to the application context. Those types represent the Neo4j test harness and provide an embedded instance, accessible via Bolt.
62+
The Bolt connector is always enabled with the test harness.
63+
4564

4665
== What's with the long name?
4766

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
[[embedded-bolt-connection]]
2+
= Using an embedded database with the Driver starter
3+
4+
This example demonstrates a couple things:
5+
6+
1. How to provide an instance of the Neo4j `DatabaseManagementService` as a managed bean into a Spring context.
7+
1. How to keep the configuration for the driver and the embedded instance in sync.
8+
1. This starter can connect against an embedded instance with the Bolt connector enabled.
9+
10+
While the <<testing-with-neo4j-harness,test harness example>> focuses on having an embedded instance through the harness in
11+
your context, this example focuses on having the embedded API for whatever reasons in your context.
12+
13+
One of those reasons might be that you want to be able to use the `GraphDatabaseService`
14+
to execute Cypher without Bolt involved or traverse nodes manually.
15+
Here is one made up example that uses the `GraphDatabaseService` to find all nodes with a label `Movie`:
16+
17+
[[GraphDatabaseService-usecase]]
18+
[source,java,indent=0]
19+
.Accessing the `GraphDatabaseService` from a service
20+
----
21+
try(var tx = databaseManagementService.database("neo4j").beginTx()) {
22+
return tx.findNodes(Label.label("Movie"))
23+
.stream()
24+
.map(n -> n.getProperty("title"))
25+
.map(String.class::cast)
26+
.collect(Collectors.toList());
27+
}
28+
----
29+
30+
The `MovieService` in the sources of this example however is exactly the same as the one in the other examples.
31+
It is not aware against what kind of server it is connected and just gets a Driver `Session` with `var session = driver.session()`.
32+
33+
== Provide a managed, embedded `DatabaseManagementService` and keep it in sync with the driver.
34+
35+
First, provide the necessary dependencies:
36+
37+
[source,xml,subs="verbatim,attributes"]
38+
----
39+
<dependencies>
40+
<!-- Embbedded instance -->
41+
<dependency>
42+
<groupId>org.neo4j</groupId>
43+
<artifactId>neo4j</artifactId>
44+
<version>{neo4j_version}</version>
45+
</dependency>
46+
<!-- Necessary bolt server -->
47+
<dependency>
48+
<groupId>org.neo4j</groupId>
49+
<artifactId>neo4j-bolt</artifactId>
50+
<version>{neo4j_version}</version>
51+
</dependency>
52+
</dependencies>
53+
----
54+
55+
Depending on your setup, you might have to exclude the following artifact from both dependencies above: `org.slf4j:slf4j-nop`,
56+
as it clashes with Spring Boots default logger.
57+
58+
The enterprise edition lives under different coordinates
59+
(`com.neo4j:neo4j-enterprise`, which are not available in Maven central but only to customers).
60+
61+
Configuration is done with the following components:
62+
63+
[[EmbeddedConfig-Properties]]
64+
[source,java,indent=0]
65+
.DatabaseManagementServiceProperties.java
66+
----
67+
include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/DatabaseManagementServiceProperties.java[tags=EmbeddedConfig-Properties]
68+
----
69+
<.> Make this class a component containing configurational properties under the prefix `org.neo4j.database-management-service`.
70+
This prefix is the canonical form. Camel- and Snakecases (`databaseManagementService` respectivly `DATABASE_MANAGEMENT_SERVICE`
71+
work as well.
72+
These properties can come from `application.properties` or `application.yml` as well as from the environment and of course,
73+
config servers.
74+
<.> This configuration is needed for the embedded instance. The embedded instance needs to store its data somewhere.
75+
76+
With a properties class like this, you can have all the relevant configuration properties in one place, instead of
77+
manually juggling with `@Value` and related.
78+
79+
80+
[[EmbeddedConfig]]
81+
[source,java,indent=0]
82+
.Configuring the embedded instance with the driver in mind
83+
----
84+
include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/Neo4jConfig.java[tags=EmbeddedConfig]
85+
----
86+
<.> Mark this class as a configurational bean.
87+
We can make the startup of the Spring context a bit fast, as we don't need to proxy the methods.
88+
<.> Define the class that represents the configuration properties.
89+
<.> This marks the returned value as Spring bean for the context
90+
<.> We only fire up the embedded instance in some conditions.
91+
This is optional, but makes perfect sense in a setup where both the embedded instance and the driver should be available.
92+
<.> We check if someone had configured the home directory for the instance.
93+
If so, we use that, otherwise we default to something random.
94+
<.> Here, we use the same port that has been given to the driver for the port the embedded bolt listens to.
95+
Choose any other port you want (or a random free one), but keep in mind,
96+
that you have to reconfigure the driver as well then (through `org.neo4j.driver.uri`).
97+
<.> This is a `Condition` to be used with `@Conditional`.
98+
It makes sure in 4. that we bring up an embedded instance only when the driver actually targets something on localhost via the bolt protocol.
99+
This later step is completely optional, but keeps you from funny surprise.
100+
101+
== Using the driver against the embedded instance
102+
103+
To the outside world, nothing indicates that the service runs against embedded:
104+
105+
[[MovieService]]
106+
[source,java,indent=0]
107+
.Accessing the `GraphDatabaseService` from a service
108+
----
109+
include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/MoviesService.java[tags=usage]
110+
----
111+
112+
You can of course inject the `DatabaseManagementService` or - if you provide those as beans as well - dedicated
113+
`GraphDatabaseService`-instances for the databases you want to work with.
114+
115+
Let's walk through a test setup.
116+
The tests prepares the data directly against the embedded instance and than tests the service:
117+
118+
[source,java,indent=0]
119+
.MoviesServiceTest.java
120+
----
121+
include::src/test/java/org/neo4j/doc/driver/springframework/boot/embedded/MoviesServiceTest.java[tags=test]
122+
----
123+
<.> Make it a standard, full blown `@SpringBootTest`, applying the same configuration found from walking the packages
124+
starting at the `@SpringBootApplication`.
125+
<.> This brings in dynamic test properties in the `registry`.
126+
It is available since Spring Boot 2.2.6.
127+
<.> We use `SocketUtils` to find a random free port.
128+
We give that random free port to the driver URI which is in turn used by <<EmbeddedConfig, the embedded config>> to configure the published Bolt port.
129+
This is necessary to not clash with any running databases on the test machine.
130+
<.> Uncommenting the next line gives you the opportunity to point the embedded instance to a directory containing pre-seeded
131+
data during test for example.
132+
<.> Springs JUnit 5 integration allows beans to be injected in all phases of a JUnit 5 test.
133+
Here we use the `@BeforeAll` phase to access the embedded database to create test data.
134+
<.> We want to test the service shown in <<MovieService,MovieService.java>>, which works with the driver.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<artifactId>spring-boot-starter-parent</artifactId>
6+
<groupId>org.springframework.boot</groupId>
7+
<version>2.2.6.RELEASE</version>
8+
<relativePath></relativePath>
9+
</parent>
10+
11+
<groupId>org.neo4j.doc.driver</groupId>
12+
<artifactId>embedded-bolt-connection</artifactId>
13+
<version>999-SNAPSHOT</version>
14+
15+
<name>Embedded bolt connection</name>
16+
<description>Using an embedded bolt connection</description>
17+
18+
<properties>
19+
<java.version>11</java.version>
20+
<neo4j-java-driver-spring-boot-starter.version>${revision}${sha1}${changelist}</neo4j-java-driver-spring-boot-starter.version>
21+
<neo4j.version>4.0.3</neo4j.version>
22+
</properties>
23+
24+
<dependencyManagement>
25+
<dependencies>
26+
<dependency>
27+
<groupId>org.neo4j</groupId>
28+
<artifactId>neo4j</artifactId>
29+
<version>${neo4j.version}</version>
30+
<exclusions>
31+
<exclusion>
32+
<artifactId>slf4j-nop</artifactId>
33+
<groupId>org.slf4j</groupId>
34+
</exclusion>
35+
</exclusions>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.neo4j</groupId>
39+
<artifactId>neo4j-bolt</artifactId>
40+
<version>${neo4j.version}</version>
41+
<exclusions>
42+
<exclusion>
43+
<artifactId>slf4j-nop</artifactId>
44+
<groupId>org.slf4j</groupId>
45+
</exclusion>
46+
</exclusions>
47+
</dependency>
48+
</dependencies>
49+
</dependencyManagement>
50+
51+
<dependencies>
52+
<!-- Embbedded instance -->
53+
<dependency>
54+
<groupId>org.neo4j</groupId>
55+
<artifactId>neo4j</artifactId>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.neo4j</groupId>
59+
<artifactId>neo4j-bolt</artifactId>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>org.neo4j.driver</groupId>
64+
<artifactId>neo4j-java-driver-spring-boot-starter</artifactId>
65+
<version>${neo4j-java-driver-spring-boot-starter.version}</version>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.springframework.boot</groupId>
69+
<artifactId>spring-boot-configuration-processor</artifactId>
70+
<optional>true</optional>
71+
</dependency>
72+
<dependency>
73+
<groupId>org.springframework.boot</groupId>
74+
<artifactId>spring-boot-starter</artifactId>
75+
</dependency>
76+
77+
<dependency>
78+
<groupId>org.springframework.boot</groupId>
79+
<artifactId>spring-boot-starter-test</artifactId>
80+
<scope>test</scope>
81+
</dependency>
82+
83+
</dependencies>
84+
85+
<build>
86+
<plugins>
87+
<plugin>
88+
<groupId>com.github.ekryd.sortpom</groupId>
89+
<artifactId>sortpom-maven-plugin</artifactId>
90+
<version>2.8.0</version>
91+
<executions>
92+
<execution>
93+
<phase>verify</phase>
94+
<goals>
95+
<goal>sort</goal>
96+
</goals>
97+
</execution>
98+
</executions>
99+
<configuration>
100+
<encoding>${project.build.sourceEncoding}</encoding>
101+
<keepBlankLines>true</keepBlankLines>
102+
<nrOfIndentSpace>-1</nrOfIndentSpace>
103+
<sortProperties>true</sortProperties>
104+
<sortDependencies>groupId,artifactId</sortDependencies>
105+
<createBackupFile>false</createBackupFile>
106+
</configuration>
107+
</plugin>
108+
<plugin>
109+
<groupId>org.springframework.boot</groupId>
110+
<artifactId>spring-boot-maven-plugin</artifactId>
111+
</plugin>
112+
<plugin>
113+
<groupId>org.apache.maven.plugins</groupId>
114+
<artifactId>maven-install-plugin</artifactId>
115+
<configuration>
116+
<skip>true</skip>
117+
</configuration>
118+
</plugin>
119+
<plugin>
120+
<groupId>org.apache.maven.plugins</groupId>
121+
<artifactId>maven-deploy-plugin</artifactId>
122+
<configuration>
123+
<skip>true</skip>
124+
</configuration>
125+
</plugin>
126+
</plugins>
127+
</build>
128+
129+
<!-- Those profiles are not relevant to the examples and are only needed for SDN-RX release chain. -->
130+
<profiles>
131+
<profile>
132+
<id>revisionMissing</id>
133+
<activation>
134+
<property>
135+
<name>!revision</name>
136+
</property>
137+
</activation>
138+
<properties>
139+
<revision>4.0.1.1</revision>
140+
</properties>
141+
</profile>
142+
<profile>
143+
<id>sha1Missing</id>
144+
<activation>
145+
<property>
146+
<name>!sha</name>
147+
</property>
148+
</activation>
149+
<properties>
150+
<sha1></sha1>
151+
</properties>
152+
</profile>
153+
<profile>
154+
<id>changelistMissing</id>
155+
<activation>
156+
<property>
157+
<name>!changelist</name>
158+
</property>
159+
</activation>
160+
<properties>
161+
<changelist></changelist>
162+
</properties>
163+
</profile>
164+
</profiles>
165+
166+
</project>

0 commit comments

Comments
 (0)