Testing Neo4j with Spring Boot and embedded driver - maven

Problem
I build an application using a Neo4j database. I like to test some custom Cypher queries using Spring Boot's #DataNeo4jTest annotation (see also Spring Boot Test - Neo4j), but I run in either one of the following problems:
The test tries to connect to a Neo4j instance using the BOLT driver.
The test fails to load the embedded driver.
Details
My dependencies are managed with Maven following the Spring Data Neo4j Reference Documentation. Section 10.3.1 of the SDN documentation explains:
By default, SDN will use the BOLT driver to connect to Neo4j and you don’t need to declare it as a separate dependency in your pom. If you want to use the embedded or HTTP drivers in your production application, you must add the following dependencies as well. (This dependency on the embedded driver is not required if you only want to use the embedded driver for testing. See the section on Testing below for more information).
Therefore, the relevant parts of my pom.xml are:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi=...>
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
...
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>3.3.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
...
</project>
My main/resources/application.yml is:
spring:
data:
neo4j:
uri: bolt://localhost
username: <username>
password: <password>
My test/resources/application.yml is:
spring.data.neo4j.uri: file:///neo4j.db
Without the test/resources/application.yml I get the following exception, which I assume is caused by using the BOLT driver:
org.springframework.transaction.CannotCreateTransactionException: Could not open Neo4j Session for transaction;
nested exception is org.neo4j.driver.v1.exceptions.AuthenticationException: The client is unauthorized due to authentication failure.
With the test/resources/application.yml I get the following exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'neo4jAuditionBeanFactoryPostProcessor': Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.class]: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.neo4j.ogm.session.SessionFactory]: Factory method 'sessionFactory' threw exception;
nested exception is org.neo4j.ogm.exception.core.ConfigurationException: Could not load driver class org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver
Questions
Are there any dependencies missing?
Is the configuration wrong?
Does anyone have a link to a working example using the Spring Boot annotation #DataNeo4jTest?
Any suggestion is welcome.

I have found a solution to my problem. It seems as if the BOLT driver is used as default for testing as well - which is confusing given the Spring Data Neo4j (SDN) documentation. Finally, the pom.xml of the GitHub project movies-java-spring-data-neo4j helped me. I added the following test dependency to my pom.xml:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-embedded-driver</artifactId>
<version>${neo4j-ogm.version}</version>
<scope>test</scope>
</dependency>
I kept the test/resources/application.yml but removed the line:
spring.data.neo4j.uri: file:///neo4j.db
Now, the test context starts with the embedded driver, and creates a temporary database file like file:/C:/Users/Me/AppData/Local/Temp/neo4j.db6943517458205762238/, which is awesome. I can get a clean database instance for every test method.
I hope this answer will help others, who have the same problem. I'm happy to provide more details if necessary.

#DataNeo4JTest works great with Spring Boot 2.x.
Example Test:
#RunWith(SpringRunner.class)
#DataNeo4jTest
public class WidgetRepositoryTest {
#Autowired
private WidgetRepository repository;
private Widget widget;
#Before
public void setUp() {
widget = WidgetTestData.builder().build();
}
#Test
public void itShouldSaveAndRetrieve() {
final Widget saved = repository.save(widget);
assertThat(saved.getId()).isNotNull();
assertThat(saved.getName()).isEqualTo(widget.getName());
final Optional<Widget> found = repository.findById(saved.getId());
assertThat(found).hasValueSatisfying(w-> {
assertThat(w.getId()).isEqualTo(saved.getId());
assertThat(w.getName()).isEqualTo(saved.getName());
});
}
}
The Neo4J-related dependencies in my Maven POM:
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-embedded-driver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

Related

Are spring-boot-starter-data-jpa and Liquibase incompatible?

I tried to setup a new Spring Boot 2.4.3 project and I want to include Liquibase as well as Spring Data JPA.
To achieve this I use the following dependencies in my POM:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>${liquibase.version}</version>
</dependency>
or...
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hibernate5</artifactId>
<version>${liquibase.version}</version>
</dependency>
...respectively and
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
with
<kotlin.version>1.4.31</kotlin.version>
<liquibase.version>4.3.1</liquibase.version>
<hibernate.version>5.4.29.Final</hibernate.version>
Yet, in the beginning, I don't have any DB setup so far and in application.yaml you can find:
server:
port: 8080
spring:
liquibase:
change-log: classpath:db/changelog/db.changelog.yml
wherbey db.changelog.yml is empty.
A Maven install using the spring-boot-starter-data-jpa dependency results in:
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is
liquibase.exception.ChangeLogParseException: Empty file classpath:db/changelog/db.changelog.yml
Caused by: liquibase.exception.ChangeLogParseException: Empty file classpath:db/changelog/db.changelog.yml
while when commenting out this dependency the build succeeds.
I found out that the empty db.changelog.yml file must contain at least:
databaseChangeLog:
- changeset:
although on https://docs.liquibase.com/concepts/basic/changelog.html an example is given that an empty changelog.yaml file needs to look like:
databaseChangeLog:
, but this results in a BeanCreationException:
Caused by: liquibase.exception.ChangeLogParseException: Could not find databaseChangeLog node

Spring Boot Elastic Search connection issue

Boot with Elastic Search using Spring Data JPA. To implement this, I created a sample Amazon account and launched sample Elasticsearch instance in the AWS console and got the details.
Now the problem is, the Spring BOOT Application is failing to due to connection issues to the AWS ElasticSearch. Not sure, I am adding the right dependencies.
AmazonElasticSearch Details :
https://search-elasticsearch-3wqyu7ekb7nymtxwosu7nxkbkq.us-east-2.es.amazonaws.com/
Pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
ElasticSearchConfiuration:
enter image description here
application.properties
elasticsearch.clustername = 008026978848:elasticsearch
elasticsearch.host = https://search-elasticsearch-3wqyu7ekb7nymtxwosu7nxkbkq.us-east-2.es.amazonaws.com/
elasticsearch.port = 9300
Exception Details
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client' defined in class path resource [com/aws/elasticsearch/config/ElasticSearchConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.Client]: Factory method 'client' threw exception; nested exception is java.net.UnknownHostException: https://search-elasticsearch-3wqyu7ekb7nymtxwosu7nxkbkq.us-east-2.es.amazonaws.com/

Do Spring LDAP and Spring Cloud Consul work together?

I had a project with Spring LDAP in place. I am trying to put in Spring Consul into this project and have been facing issues with respect to it.
Here is the pom.xml :
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
Here is application properties :
#consul properties
spring.cloud.consul.config.format=PROPERTIES
spring.cloud.consul.discovery.healthCheckPath=/<root>/health
spring.cloud.consul.discovery.healthCheckInterval=15s
spring.application.name=<App_Name>
spring.profiles.active=dev
And I enabled discovery client using #EnableDiscoveryClient on SpringBootApplication class.
But, I am ending up with this error and the app never starts :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field ldapTemplate in com.<package>.config.LdapClient required a bean of type 'org.springframework.ldap.core.LdapTemplate' that could not be found.
- Bean method 'ldapTemplate' not loaded because #ConditionalOnClass did not find required class 'org.springframework.data.ldap.repository.LdapRepository'
Action:
Consider revisiting the conditions above or defining a bean of type 'org.springframework.ldap.core.LdapTemplate' in your configuration.
Disconnected from the target VM, address: '127.0.0.1:62703', transport: 'socket'
I figure it has something to do with autodiscovery of Ldap and introducing Consul is causing this issue, but, I am not able to pin-point the problem.
Can someone please help me resolve this issue?

Cannot get spring-data-neo4j to connect to remote database

I am building a small proof of concept Spring Boot app which is supposed to connect to a Neo4j instance and perform some basic operations on a couple of different Nodes. If I have the main application class wired to create an embedded Neo4j service using the following code, everything works fine. (this is based on the working example https://spring.io/guides/gs/accessing-neo4j-data-rest/)
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("target/hello.db");
}
This is the only code sample I can find though for connecting to a Neo4j server from spring boot. If I try connecting to a remote server, the code fails to start with the exception at the end of this question. Our plan is to run a centralized Neo4j instance which is obviously a common production requirement.
How can or should I configure my bean to connect to a remote Neo4j database or is anyone aware of a solid working example of this kind of setup?
Thanks!
My pom.xml includes the following:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>3.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
I have seen several references to using SpringCypherRestGraphDatabase so I have this being handled in my Main application class is as follows:
import org.neo4j.graphdb.GraphDatabaseService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
#SpringBootApplication
#EnableNeo4jRepositories
public class ProfileServiceApplication extends Neo4jConfiguration {
public ProfileServiceApplication() {
setBasePackage("profile");
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return new SpringCypherRestGraphDatabase("http://localhost:7474/db/data/","neo4j","password");
}
public static void main(String[] args) {
SpringApplication.run(ProfileServiceApplication.class, args);
}
}
When I try to run with this configuration, I get the following error:
nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.neo4j.graphdb.GraphDatabaseService]: Circular reference involving containing bean 'profileServiceApplication' - consider declaring the factory method as static for independence from its containing instance. Factory method 'graphDatabaseService' threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/data/neo4j/core/UpdateableState
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210)
Please share your application as github-project for testing. Perhaps it is also a dependency issue of the spring-boot-starter? Which boot version are you using??
Perhaps you can check mvn dependency:tree if there is any older version of SDN pulled in and if so, update the neo4-version-property for the spring-boot-starter.
Example application is here:
http://neo4j.com/developer/java/#_using_spring_data_neo4j
and it is also in the docs as you correctly saw:
http://docs.spring.io/spring-data/data-neo4j/docs/3.3.0.RELEASE/reference/html/#using_spring_data_neo4j_as_a_neo4j_server_client
The reference which you are giving doesn't contain Neo4j URL.
Hence Here is Application.properties file to connect to your Neo4j.
spring.data.neo4j.uri=bolt://localhost
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=Your_Password
Start your Neo4j Database as a console application which will run on localhost:7474 port most probably.
command to start:
neo4j console
Also check the dependency. As the latest version work with only
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

PhaseInterceptorChain and HttpServletRequest lead to AbstractEndpointFactory ClassNotFoundException

I need the ability to reliably get client IP address in CXF JAX-WS and so I added the following code to my web service:
Message message = PhaseInterceptorChain.getCurrentMessage();
HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
request.getRemoteAddr();
In order for the build to complete successfully, I had to add the following imports:
import javax.servlet.http.HttpServletRequest;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.PhaseInterceptorChain;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
And add the following dependencies to my pom.xml:
<dependency> <!-- PhaseInterceptorChain & Message -->
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-api</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.2.3</version>
</dependency>
<dependency> <!-- HttpServletRequest -->
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<!-- scope>provided</scope -->
</dependency>
But when I try to deploy the resulting successful(!) build, Tomcat (7.0.42) throws an exception and refuses to load the war file:
org.springframework.beans.factory.BeanDefinitionStoreException:
Unexpected exception parsing XML document from class path resource [beans.xml];
nested exception is org.springframework.beans.FatalBeanException:
Invalid NamespaceHandler class [org.apache.cxf.jaxws.spring.NamespaceHandler] for namespace [http://cxf.apache.org/jaxws]:
problem with handler class file or dependent class;
nested exception is java.lang.NoClassDefFoundError: org/apache/cxf/endpoint/AbstractEndpointFactory
...
Caused by: org.springframework.beans.FatalBeanException:
Invalid NamespaceHandler class [org.apache.cxf.jaxws.spring.NamespaceHandler]
for namespace [http://cxf.apache.org/jaxws]:
problem with handler class file or dependent class;
nested exception is java.lang.NoClassDefFoundError: org/apache/cxf/endpoint/AbstractEndpointFactory
...
Caused by: java.lang.ClassNotFoundException: org.apache.cxf.endpoint.AbstractEndpointFactory
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
Apparently, something is missing from my pom.xml that provides the necessary module at runtime. What am I missing?
Mystery solved. For the benefit of all I am providing the root cause of the problem and the solution:
The problem was in this line:
<version>2.4.0</version>
That is, cxf-api artifact's version did not match the CXF version throughout the project (and the pom.xml).
All I had to do to solve the problem was to change that line to:
<version>${cxf.version}</version>
Where cxf.version was defined earlier as 2.7.1.
Conclusion: CXF dependencies/packages/plugins versions must match throughout the pom.xml (or else you will get some "red herrings").

Resources