diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index d4c7b547e5..cf323298cb 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -166,6 +166,11 @@
spring-data-mongodb
true
+
+ org.springframework.data
+ spring-data-neo4j
+ true
+
org.springframework.data
spring-data-redis
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
index 2eafa5a520..81f8bb7377 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
@@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDa
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.util.ApplicationContextTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
@@ -63,6 +64,7 @@ public class SpringApplicationHierarchyTests {
@EnableAutoConfiguration(exclude = { ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
+ Neo4jAutoConfiguration.class,
RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class }, excludeName = {
"org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration" })
@@ -75,6 +77,7 @@ public class SpringApplicationHierarchyTests {
ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
+ Neo4jAutoConfiguration.class,
RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class }, excludeName = {
"org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration" })
diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index 9951e96cb1..b6de2d4b7b 100755
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -379,6 +379,17 @@
+
+ org.springframework.data
+ spring-data-neo4j
+ true
+
+
+ jcl-over-slf4j
+ org.slf4j
+
+
+
org.springframework.data
spring-data-redis
@@ -635,5 +646,6 @@
tomcat-embed-jasper
test
+
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java
new file mode 100644
index 0000000000..8eb7e4751b
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j;
+
+import org.neo4j.ogm.session.Neo4jSession;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.neo4j.repository.GraphRepository;
+import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
+import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension;
+import org.springframework.data.neo4j.repository.support.GraphRepositoryFactoryBean;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Neo4j
+ * Repositories.
+ *
+ * Activates when there is no bean of type
+ * {@link org.springframework.data.neo4j.repository.support.GraphRepositoryFactoryBean}
+ * configured in the context, the Spring Data Neo4j
+ * {@link org.springframework.data.neo4j.repository.GraphRepository} type is on the
+ * classpath, the Neo4j client driver API is on the classpath, and there is no other
+ * configured {@link org.springframework.data.neo4j.repository.GraphRepository}.
+ *
+ * Once in effect, the auto-configuration is the equivalent of enabling Neo4j repositories
+ * using the
+ * {@link org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories}
+ * annotation.
+ *
+ * @author Dave Syer
+ * @author Oliver Gierke
+ * @author Josh Long
+ * @since 1.4.0
+ * @see EnableNeo4jRepositories
+ */
+@Configuration
+@ConditionalOnClass({ Neo4jSession.class, GraphRepository.class })
+@ConditionalOnMissingBean({ GraphRepositoryFactoryBean.class, Neo4jRepositoryConfigurationExtension.class })
+@ConditionalOnProperty(prefix = "spring.data.neo4j.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
+@Import(Neo4jRepositoriesAutoConfigureRegistrar.class)
+@AutoConfigureAfter(Neo4jAutoConfiguration.class)
+public class Neo4jRepositoriesAutoConfiguration {
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java
new file mode 100644
index 0000000000..5793b10072
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j;
+
+import java.lang.annotation.Annotation;
+
+import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
+import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension;
+import org.springframework.data.repository.config.RepositoryConfigurationExtension;
+
+/**
+ * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Neo4j
+ * Repositories.
+ *
+ * @author Michael Hunger
+ */
+class Neo4jRepositoriesAutoConfigureRegistrar extends
+ AbstractRepositoryConfigurationSourceSupport {
+
+ @Override
+ protected Class extends Annotation> getAnnotation() {
+ return EnableNeo4jRepositories.class;
+ }
+
+ @Override
+ protected Class> getConfiguration() {
+ return EnableNeo4jRepositoriesConfiguration.class;
+ }
+
+ @Override
+ protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
+ return new Neo4jRepositoryConfigurationExtension();
+ }
+
+ @EnableNeo4jRepositories
+ private static class EnableNeo4jRepositoriesConfiguration {
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java
new file mode 100644
index 0000000000..a681b2a7d5
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2012-2015 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.
+ */
+
+/**
+ * Auto-configuration for Spring Data Neo4j.
+ */
+package org.springframework.boot.autoconfigure.data.neo4j;
+
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java
new file mode 100644
index 0000000000..e8d2a814f3
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.neo4j;
+
+import java.util.List;
+
+import org.neo4j.ogm.session.Neo4jSession;
+import org.neo4j.ogm.session.Session;
+import org.neo4j.ogm.session.SessionFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.neo4j.SessionFactoryProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.data.neo4j.config.Neo4jConfiguration;
+import org.springframework.data.neo4j.template.Neo4jOperations;
+import org.springframework.data.neo4j.template.Neo4jTemplate;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Neo4j support.
+ *
+ * Registers a {@link Neo4jTemplate} bean if no other bean of
+ * the same type is configured.
+ *
+ * @author Michael Hunger
+ * @author Josh Long
+ * @author Vince Bickers
+ * @author Stephane Nicoll
+ * @since 1.4.0
+ */
+@Configuration
+@ConditionalOnClass({Neo4jSession.class, Neo4jOperations.class})
+@ConditionalOnMissingBean(Neo4jOperations.class)
+@EnableConfigurationProperties(Neo4jProperties.class)
+public class Neo4jAutoConfiguration {
+
+ @Configuration
+ @Import(SessionFactoryProviderConfiguration.class)
+ public static class SpringBootNeo4jConfiguration extends Neo4jConfiguration {
+
+ private final ObjectProvider sessionFactoryProvider;
+
+ public SpringBootNeo4jConfiguration(ObjectProvider sessionFactoryProvider) {
+ this.sessionFactoryProvider = sessionFactoryProvider;
+ }
+
+ @Override
+ public SessionFactory getSessionFactory() {
+ SessionFactoryProvider provider = this.sessionFactoryProvider.getObject();
+ return provider.getSessionFactory();
+ }
+
+ @Bean
+ @Scope(scopeName = "${spring.data.neo4j.session.scope:singleton}",
+ proxyMode = ScopedProxyMode.TARGET_CLASS)
+ @Override
+ public Session getSession() throws Exception {
+ return getSessionFactory().openSession();
+ }
+
+ }
+
+ @Configuration
+ @Import(Neo4jConfigurationConfiguration.class)
+ static class SessionFactoryProviderConfiguration implements BeanFactoryAware {
+
+ private final org.neo4j.ogm.config.Configuration configuration;
+
+ private ConfigurableListableBeanFactory beanFactory;
+
+ SessionFactoryProviderConfiguration(org.neo4j.ogm.config.Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SessionFactoryProvider sessionFactoryProvider() {
+ SessionFactoryProvider provider = new SessionFactoryProvider();
+ provider.setConfiguration(this.configuration);
+ provider.setPackagesToScan(getPackagesToScan());
+ return provider;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
+ }
+
+ protected String[] getPackagesToScan() {
+ if (AutoConfigurationPackages.has(this.beanFactory)) {
+ List basePackages = AutoConfigurationPackages.get(this.beanFactory);
+ return basePackages.toArray(new String[basePackages.size()]);
+ }
+ return new String[0];
+ }
+
+ }
+
+ @Configuration
+ static class Neo4jConfigurationConfiguration {
+
+ private final Neo4jProperties properties;
+
+ Neo4jConfigurationConfiguration(Neo4jProperties properties) {
+ this.properties = properties;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public org.neo4j.ogm.config.Configuration configuration() {
+ return this.properties.createConfiguration();
+ }
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jProperties.java
new file mode 100644
index 0000000000..1331310ac4
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jProperties.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.neo4j;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.neo4j.ogm.config.Configuration;
+
+import org.springframework.beans.BeansException;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Configuration properties for Neo4j.
+ *
+ * @author Stephane Nicoll
+ * @author Michael Hunger
+ * @author Vince Bickers
+ * @since 1.4.0
+ */
+@ConfigurationProperties(prefix = "spring.data.neo4j")
+public class Neo4jProperties implements ApplicationContextAware {
+
+ static final String EMBEDDED_DRIVER = "org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver";
+
+ static final String HTTP_DRIVER = "org.neo4j.ogm.drivers.http.driver.HttpDriver";
+
+ static final String DEFAULT_HTTP_URI = "http://localhost:7474";
+
+ /**
+ * URI used by the driver. Auto-detected by default.
+ */
+ private String uri;
+
+ /**
+ * Login user of the server.
+ */
+ private String username;
+
+ /**
+ * Login password of the server.
+ */
+ private String password;
+
+ /**
+ * Compiler to use.
+ */
+ private String compiler;
+
+ private final Embedded embedded = new Embedded();
+
+ private ClassLoader classLoader = Neo4jProperties.class.getClassLoader();
+
+ public String getUri() {
+ return this.uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public String getUsername() {
+ return this.username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return this.password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getCompiler() {
+ return this.compiler;
+ }
+
+ public void setCompiler(String compiler) {
+ this.compiler = compiler;
+ }
+
+ public Embedded getEmbedded() {
+ return this.embedded;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext ctx) throws BeansException {
+ this.classLoader = ctx.getClassLoader();
+ }
+
+ /**
+ * Create a {@link Configuration} based on the state of this instance.
+ * @return a configuration
+ */
+ public Configuration createConfiguration() {
+ Configuration configuration = new Configuration();
+ if (this.uri == null) {
+ if (getEmbedded().isEnabled()
+ && ClassUtils.isPresent(EMBEDDED_DRIVER, this.classLoader)) {
+ configuration.driverConfiguration().setDriverClassName(EMBEDDED_DRIVER);
+ }
+ else {
+ configuration.driverConfiguration().setDriverClassName(HTTP_DRIVER);
+ configuration.driverConfiguration().setURI(DEFAULT_HTTP_URI);
+ }
+ }
+ else {
+ configuration.driverConfiguration().setDriverClassName(deduceDriverFromUri());
+ configuration.driverConfiguration().setURI(this.uri);
+ }
+
+ if (this.username != null && this.password != null) {
+ configuration.driverConfiguration().setCredentials(this.username, this.password);
+ }
+ if (this.compiler != null) {
+ configuration.compilerConfiguration().setCompilerClassName(this.compiler);
+ }
+ return configuration;
+ }
+
+ private String deduceDriverFromUri() {
+ try {
+ URI uri = new URI(this.uri);
+ String scheme = uri.getScheme();
+ if (scheme == null || scheme.equals("file")) {
+ return EMBEDDED_DRIVER;
+ }
+ else if ("http".equals(scheme)) {
+ return HTTP_DRIVER;
+ }
+ else {
+ throw new IllegalArgumentException("Could not deduce driver to use based on URI '" + uri + "'");
+ }
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalArgumentException("Invalid URI for spring.data.neo4j.uri '" + this.uri + "'", ex);
+ }
+ }
+
+
+ public static class Embedded {
+
+ /**
+ * Enable embedded mode if the embedded driver is available.
+ */
+ private boolean enabled = true;
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java
new file mode 100644
index 0000000000..e53e6c5214
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012-2016 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.
+ */
+
+/**
+ * Auto-configuration for Neo4j.
+ */
+package org.springframework.boot.autoconfigure.neo4j;
diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index fba5f4e02c..e16833216b 100644
--- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -90,6 +90,18 @@
"description": "Enable Mongo repositories.",
"defaultValue": true
},
+ {
+ "name": "spring.data.neo4j.repositories.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable Neo4j repositories.",
+ "defaultValue": true
+ },
+ {
+ "name": "spring.data.neo4j.session.scope",
+ "type": "java.lang.String",
+ "description": "Scope (lifetime) of the session.",
+ "defaultValue": "singleton"
+ },
{
"name": "spring.data.redis.repositories.enabled",
"type": "java.lang.Boolean",
@@ -330,6 +342,36 @@
}
]
},
+ {
+ "name": "spring.data.neo4j.compiler",
+ "providers": [
+ {
+ "name": "class-reference",
+ "parameters": {
+ "target": "org.neo4j.ogm.compiler.Compiler"
+ }
+ }
+ ]
+ },
+ {
+ "name": "spring.data.neo4j.session.scope",
+ "values": [
+ {
+ "value": "singleton"
+ },
+ {
+ "value": "session"
+ },
+ {
+ "value": "request"
+ }
+ ],
+ "providers": [
+ {
+ "name": "any"
+ }
+ ]
+ },
{
"name": "spring.datasource.driver-class-name",
"providers": [
diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
index 811f7d78f3..5afd06d3ff 100644
--- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -31,6 +31,7 @@ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositor
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
+org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
@@ -68,6 +69,7 @@ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
+org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java
new file mode 100644
index 0000000000..30fc7667ca
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.alt.neo4j;
+
+import org.springframework.boot.autoconfigure.data.neo4j.city.City;
+import org.springframework.data.neo4j.repository.GraphRepository;
+
+public interface CityNeo4jRepository extends GraphRepository {
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java
new file mode 100644
index 0000000000..7f774d1b4f
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
+import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.jpa.city.City;
+import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository;
+import org.springframework.boot.autoconfigure.data.neo4j.country.Country;
+import org.springframework.boot.autoconfigure.data.neo4j.country.CountryRepository;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfigurationTests;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.boot.orm.jpa.EntityScan;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportSelector;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link Neo4jRepositoriesAutoConfiguration}.
+ *
+ * @author Dave Syer
+ * @author Oliver Gierke
+ * @author Michael Hunger
+ * @author Vince Bickers
+ * @author Stephane Nicoll
+ */
+public class MixedNeo4jRepositoriesAutoConfigurationTests {
+
+ private AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext();
+
+ @After
+ public void close() {
+ this.context.close();
+ }
+
+ @Test
+ public void testDefaultRepositoryConfiguration() throws Exception {
+ EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false");
+ this.context.register(TestConfiguration.class, BaseConfiguration.class);
+ this.context.refresh();
+ assertThat(this.context.getBean(CountryRepository.class)).isNotNull();
+ }
+
+ @Test
+ public void testMixedRepositoryConfiguration() throws Exception {
+ EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false");
+ this.context.register(MixedConfiguration.class, BaseConfiguration.class);
+ this.context.refresh();
+ assertThat(this.context.getBean(CountryRepository.class)).isNotNull();
+ assertThat(this.context.getBean(CityRepository.class)).isNotNull();
+ }
+
+ @Test
+ public void testJpaRepositoryConfigurationWithNeo4jTemplate() throws Exception {
+ EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false");
+ this.context.register(JpaConfiguration.class, BaseConfiguration.class);
+ this.context.refresh();
+ assertThat(this.context.getBean(CityRepository.class)).isNotNull();
+ }
+
+ @Test
+ @Ignore
+ public void testJpaRepositoryConfigurationWithNeo4jOverlap() throws Exception {
+ EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false");
+ this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
+ this.context.refresh();
+ assertThat(this.context.getBean(CityRepository.class)).isNotNull();
+ }
+
+ @Test
+ public void testJpaRepositoryConfigurationWithNeo4jOverlapDisabled() throws Exception {
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.datasource.initialize:false",
+ "spring.data.neo4j.repositories.enabled:false");
+ this.context.register(OverlapConfiguration.class, BaseConfiguration.class);
+ this.context.refresh();
+ assertThat(this.context.getBean(CityRepository.class)).isNotNull();
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class)
+ // Not this package or its parent
+ @EnableNeo4jRepositories(basePackageClasses = Country.class)
+ protected static class TestConfiguration {
+
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class)
+ @EnableNeo4jRepositories(basePackageClasses = Country.class)
+ @EntityScan(basePackageClasses = City.class)
+ @EnableJpaRepositories(basePackageClasses = CityRepository.class)
+ protected static class MixedConfiguration {
+
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class)
+ @EntityScan(basePackageClasses = City.class)
+ @EnableJpaRepositories(basePackageClasses = CityRepository.class)
+ protected static class JpaConfiguration {
+
+ }
+
+ // In this one the Jpa repositories and the auto-configuration packages overlap, so
+ // Neo4j will try and configure the same repositories
+ @Configuration
+ @TestAutoConfigurationPackage(CityRepository.class)
+ @EnableJpaRepositories(basePackageClasses = CityRepository.class)
+ protected static class OverlapConfiguration {
+
+ }
+
+ @Configuration
+ @Import(Registrar.class)
+ protected static class BaseConfiguration {
+
+ }
+
+ protected static class Registrar implements ImportSelector {
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ List names = new ArrayList();
+ for (Class> type : new Class>[] {DataSourceAutoConfiguration.class,
+ HibernateJpaAutoConfiguration.class,
+ JpaRepositoriesAutoConfiguration.class,
+ Neo4jAutoConfiguration.class,
+ Neo4jRepositoriesAutoConfiguration.class}) {
+ names.add(type.getName());
+ }
+ return names.toArray(new String[names.size()]);
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java
new file mode 100644
index 0000000000..7b8fa27709
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j;
+
+import org.assertj.core.api.Assertions;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
+import org.springframework.boot.autoconfigure.data.neo4j.city.City;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
+
+/**
+ * Tests for {@link Neo4jAutoConfiguration}.
+ *
+ * @author Josh Long
+ * @author Oliver Gierke
+ * @author Vince Bickers
+ */
+public class Neo4jDataAutoConfigurationTests {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+
+ @After
+ public void close() {
+ this.context.close();
+ }
+
+ @Test
+ public void usesAutoConfigurationPackageToPickUpDomainTypes() {
+ String cityPackage = City.class.getPackage().getName();
+ AutoConfigurationPackages.register(this.context, cityPackage);
+ this.context.register(Neo4jAutoConfiguration.class);
+ this.context.refresh();
+ assertDomainTypesDiscovered(this.context.getBean(Neo4jMappingContext.class),
+ City.class);
+ }
+
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static void assertDomainTypesDiscovered(Neo4jMappingContext mappingContext,
+ Class>... types) {
+ for (Class> type : types) {
+ Assertions.assertThat(mappingContext.getPersistentEntity(type)).isNotNull();
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java
new file mode 100644
index 0000000000..a1a514f831
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j;
+
+import org.junit.After;
+import org.junit.Test;
+import org.neo4j.ogm.session.SessionFactory;
+
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
+import org.springframework.boot.autoconfigure.data.alt.neo4j.CityNeo4jRepository;
+import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage;
+import org.springframework.boot.autoconfigure.data.neo4j.city.City;
+import org.springframework.boot.autoconfigure.data.neo4j.city.CityRepository;
+import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
+import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link Neo4jRepositoriesAutoConfiguration}.
+ *
+ * @author Dave Syer
+ * @author Oliver Gierke
+ * @author Michael Hunger
+ * @author Vince Bickers
+ * @author Stephane Nicoll
+ */
+public class Neo4jRepositoriesAutoConfigurationTests {
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void close() {
+ this.context.close();
+ }
+
+ @Test
+ public void testDefaultRepositoryConfiguration() throws Exception {
+ prepareApplicationContext(TestConfiguration.class);
+
+ assertThat(this.context.getBean(CityRepository.class)).isNotNull();
+ Neo4jMappingContext mappingContext = this.context.getBean(Neo4jMappingContext.class);
+ assertThat(mappingContext.getPersistentEntity(City.class)).isNotNull();
+
+ }
+
+ @Test
+ public void testNoRepositoryConfiguration() throws Exception {
+ prepareApplicationContext(EmptyConfiguration.class);
+
+ assertThat(this.context.getBean(SessionFactory.class)).isNotNull();
+ }
+
+ @Test
+ public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() {
+ prepareApplicationContext(CustomizedConfiguration.class);
+
+ assertThat(this.context.getBean(CityNeo4jRepository.class)).isNotNull();
+ }
+
+ @Test(expected = NoSuchBeanDefinitionException.class)
+ public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() {
+ prepareApplicationContext(SortOfInvalidCustomConfiguration.class);
+
+ this.context.getBean(CityRepository.class);
+ }
+
+ private void prepareApplicationContext(Class>... configurationClasses) {
+ this.context = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.data.neo4j.uri=http://localhost:9797");
+ this.context.register(configurationClasses);
+ this.context.register(Neo4jAutoConfiguration.class,
+ Neo4jRepositoriesAutoConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class);
+ this.context.refresh();
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(City.class)
+ protected static class TestConfiguration {
+
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(EmptyDataPackage.class)
+ protected static class EmptyConfiguration {
+
+ }
+
+ @Configuration
+ @TestAutoConfigurationPackage(Neo4jRepositoriesAutoConfigurationTests.class)
+ @EnableNeo4jRepositories(basePackageClasses = CityNeo4jRepository.class)
+ protected static class CustomizedConfiguration {
+
+ }
+
+ @Configuration
+ // To not find any repositories
+ @EnableNeo4jRepositories("foo.bar")
+ @TestAutoConfigurationPackage(Neo4jRepositoriesAutoConfigurationTests.class)
+ protected static class SortOfInvalidCustomConfiguration {
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java
new file mode 100644
index 0000000000..98e225c0c4
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j.city;
+
+import java.io.Serializable;
+
+import org.neo4j.ogm.annotation.GraphId;
+import org.neo4j.ogm.annotation.NodeEntity;
+
+import org.springframework.boot.autoconfigure.data.neo4j.country.Country;
+
+@NodeEntity
+public class City implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @GraphId
+ private Long id;
+
+ private String name;
+
+ private String state;
+
+ private Country country;
+
+ private String map;
+
+ public City() {
+ }
+
+ public City(String name, Country country) {
+ this.name = name;
+ this.country = country;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getState() {
+ return this.state;
+ }
+
+ public Country getCountry() {
+ return this.country;
+ }
+
+ public String getMap() {
+ return this.map;
+ }
+
+ @Override
+ public String toString() {
+ return getName() + "," + getState() + "," + getCountry();
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java
new file mode 100644
index 0000000000..67b7f4b860
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j.city;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.neo4j.repository.GraphRepository;
+
+public interface CityRepository extends GraphRepository {
+
+ Page findAll(Pageable pageable);
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java
new file mode 100644
index 0000000000..b9baccf1a4
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j.country;
+
+import java.io.Serializable;
+
+import org.neo4j.ogm.annotation.GraphId;
+import org.neo4j.ogm.annotation.NodeEntity;
+
+@NodeEntity
+public class Country implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @GraphId
+ private Long id;
+
+ private String name;
+
+ public Country() {
+ }
+
+ public Country(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java
new file mode 100644
index 0000000000..a3180af6d8
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.data.neo4j.country;
+
+import org.springframework.data.neo4j.repository.GraphRepository;
+
+public interface CountryRepository extends GraphRepository {
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java
new file mode 100644
index 0000000000..a00bbf59b9
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.neo4j;
+
+import org.junit.After;
+import org.junit.Test;
+import org.neo4j.ogm.drivers.http.driver.HttpDriver;
+import org.neo4j.ogm.session.Session;
+import org.neo4j.ogm.session.SessionFactory;
+
+import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.neo4j.template.Neo4jOperations;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link Neo4jAutoConfiguration}. Tests can't use the embedded driver
+ * as we use lucene 4 and Neo4j still requires 3.
+ *
+ * @author Stephane Nicoll
+ * @author Michael Hunger
+ * @author Vince Bickers
+ */
+public class Neo4jAutoConfigurationTests {
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void close() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void defaultConfiguration() {
+ load(null, "spring.data.neo4j.uri=http://localhost:8989");
+ assertThat(this.context.getBeansOfType(Neo4jOperations.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(org.neo4j.ogm.config.Configuration.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(SessionFactory.class)).hasSize(1);
+ assertThat(this.context.getBeanDefinition("scopedTarget.getSession").getScope()).isEqualTo("singleton");
+ }
+
+ @Test
+ public void customScope() {
+ load(null, "spring.data.neo4j.uri=http://localhost:8989",
+ "spring.data.neo4j.session.scope=prototype");
+ assertThat(this.context.getBeanDefinition("scopedTarget.getSession").getScope()).isEqualTo("prototype");
+ }
+
+ @Test
+ public void customNeo4jOperations() {
+ load(CustomNeo4jOperations.class);
+ assertThat(this.context.getBean(Neo4jOperations.class))
+ .isSameAs(this.context.getBean("myNeo4jOperations"));
+ assertThat(this.context.getBeansOfType(org.neo4j.ogm.config.Configuration.class)).hasSize(0);
+ assertThat(this.context.getBeansOfType(SessionFactory.class)).hasSize(0);
+ assertThat(this.context.getBeansOfType(Session.class)).hasSize(0);
+ }
+
+ @Test
+ public void customConfiguration() {
+ load(CustomConfiguration.class);
+ assertThat(this.context.getBean(org.neo4j.ogm.config.Configuration.class))
+ .isSameAs(this.context.getBean("myConfiguration"));
+ assertThat(this.context.getBeansOfType(Neo4jOperations.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(org.neo4j.ogm.config.Configuration.class)).hasSize(1);
+ assertThat(this.context.getBeansOfType(SessionFactory.class)).hasSize(1);
+ }
+
+ public void load(Class> config, String... environment) {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(ctx, environment);
+ if (config != null) {
+ ctx.register(config);
+ }
+ ctx.register(PropertyPlaceholderAutoConfiguration.class,
+ Neo4jAutoConfiguration.class);
+ ctx.refresh();
+ this.context = ctx;
+ }
+
+ @Configuration
+ static class CustomNeo4jOperations {
+
+ @Bean
+ public Neo4jOperations myNeo4jOperations() {
+ return mock(Neo4jOperations.class);
+ }
+
+ }
+
+
+ @Configuration
+ static class CustomConfiguration {
+
+ @Bean
+ public org.neo4j.ogm.config.Configuration myConfiguration() {
+ org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration();
+ configuration.driverConfiguration().setDriverClassName(HttpDriver.class.getName());
+ return configuration;
+ }
+
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java
new file mode 100644
index 0000000000..be38fab978
--- /dev/null
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012-2016 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.boot.autoconfigure.neo4j;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import com.hazelcast.util.Base64;
+import org.junit.After;
+import org.junit.Test;
+import org.neo4j.ogm.authentication.Credentials;
+import org.neo4j.ogm.config.Configuration;
+import org.neo4j.ogm.config.DriverConfiguration;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.util.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link Neo4jProperties}.
+ *
+ * @author Stephane Nicoll
+ */
+public class Neo4jPropertiesTests {
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void close() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void defaultUseEmbeddedInMemoryIfAvailable() {
+ Neo4jProperties properties = load(true);
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.EMBEDDED_DRIVER, null);
+ }
+
+ @Test
+ public void defaultUseHttpDriverIfEmbeddedDriverIsNotAvailable() {
+ Neo4jProperties properties = load(false);
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.HTTP_DRIVER,
+ Neo4jProperties.DEFAULT_HTTP_URI);
+ }
+
+ @Test
+ public void httpUriUseHttpServer() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.uri=http://localhost:7474");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.HTTP_DRIVER,
+ "http://localhost:7474");
+ }
+
+ @Test
+ public void fileUriUseEmbeddedServer() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.uri=file://var/tmp/graph.db");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.EMBEDDED_DRIVER,
+ "file://var/tmp/graph.db");
+ }
+
+ @Test
+ public void credentialsAreSet() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.uri=http://localhost:7474",
+ "spring.data.neo4j.username=user",
+ "spring.data.neo4j.password=secret");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.HTTP_DRIVER,
+ "http://localhost:7474");
+ assertCredentials(configuration, "user", "secret");
+ }
+
+ @Test
+ public void credentialsAreSetFromUri() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.uri=http://user:secret@my-server:7474");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.HTTP_DRIVER,
+ "http://user:secret@my-server:7474");
+ assertCredentials(configuration, "user", "secret");
+ }
+
+ @Test
+ public void embeddedModeDisabledUseHttpUri() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.embedded.enabled=false");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.HTTP_DRIVER,
+ Neo4jProperties.DEFAULT_HTTP_URI);
+ }
+
+ @Test
+ public void embeddedModeWithRelativeLocation() {
+ Neo4jProperties properties = load(true,
+ "spring.data.neo4j.uri=target/neo4j/my.db");
+ Configuration configuration = properties.createConfiguration();
+ assertDriver(configuration, Neo4jProperties.EMBEDDED_DRIVER,
+ "target/neo4j/my.db");
+ }
+
+ private static void assertDriver(Configuration actual, String driver,
+ String uri) {
+ assertThat(actual).isNotNull();
+ DriverConfiguration driverConfig = actual.driverConfiguration();
+ assertThat(driverConfig.getDriverClassName()).isEqualTo(driver);
+ assertThat(driverConfig.getURI()).isEqualTo(uri);
+ }
+
+ private static void assertCredentials(Configuration actual, String username, String password) {
+ Credentials credentials = actual.driverConfiguration().getCredentials();
+ if (username == null & password == null) {
+ assertThat(credentials).isNull();
+ }
+ else {
+ assertThat(credentials).isNotNull();
+ Object content = credentials.credentials();
+ assertThat(content).isInstanceOf(String.class);
+ String[] auth = new String(Base64.decode(((String) content)
+ .getBytes())).split(":");
+ assertThat(auth[0]).isEqualTo(username);
+ assertThat(auth[1]).isEqualTo(password);
+ assertThat(auth).hasSize(2);
+ }
+ }
+
+ public Neo4jProperties load(final boolean embeddedAvailable, String... environment) {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.setClassLoader(
+ new URLClassLoader(new URL[0], getClass().getClassLoader()) {
+
+ @Override
+ protected Class> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ if (name.equals(Neo4jProperties.EMBEDDED_DRIVER)) {
+ if (embeddedAvailable) {
+ return TestEmbeddedDriver.class;
+ }
+ else {
+ throw new ClassNotFoundException();
+ }
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ });
+ EnvironmentTestUtils.addEnvironment(ctx, environment);
+ ctx.register(TestConfiguration.class);
+ ctx.refresh();
+ this.context = ctx;
+ return this.context.getBean(Neo4jProperties.class);
+ }
+
+ @org.springframework.context.annotation.Configuration
+ @EnableConfigurationProperties(Neo4jProperties.class)
+ static class TestConfiguration {
+
+ }
+
+ private static class TestEmbeddedDriver {
+
+ }
+
+}
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index d6570f51fb..7c7a99d7a4 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -55,6 +55,7 @@
2.1.9
1.9.2
3.2.2
+ 1.10
1.4
2.1.1
2.1
@@ -122,6 +123,7 @@
2.14.1
5.1.38
1.9.22
+ 2.0.0-M04
9.4.1208.jre7
2.0.7.RELEASE
2.0.7.RELEASE
@@ -325,6 +327,11 @@
spring-boot-starter-data-redis
1.4.0.BUILD-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-starter-data-neo4j
+ 1.4.0.BUILD-SNAPSHOT
+
org.springframework.boot
spring-boot-starter-data-rest
@@ -768,6 +775,11 @@
commons-collections
${commons-collections.version}
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
commons-dbcp
commons-dbcp
@@ -1760,6 +1772,26 @@
mongo-java-driver
${mongodb.version}
+
+ org.neo4j
+ neo4j-ogm-api
+ ${neo4j-ogm.version}
+
+
+ org.neo4j
+ neo4j-ogm-compiler
+ ${neo4j-ogm.version}
+
+
+ org.neo4j
+ neo4j-ogm-core
+ ${neo4j-ogm.version}
+
+
+ org.neo4j
+ neo4j-ogm-http-driver
+ ${neo4j-ogm.version}
+
org.postgresql
postgresql
diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml
index 3500b5bd58..b36c2a3d56 100644
--- a/spring-boot-docs/pom.xml
+++ b/spring-boot-docs/pom.xml
@@ -488,6 +488,11 @@
spring-data-mongodb
true
+
+ org.springframework.data
+ spring-data-neo4j
+ true
+
org.springframework.data
spring-data-redis
diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
index 8f22283ea2..fa31ea2ee9 100644
--- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
+++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
@@ -536,6 +536,15 @@ content into your application; rather pick only the properties that you need.
# DATA REDIS
spring.data.redis.repositories.enabled=true # Enable Redis repositories.
+ # NEO4J ({sc-spring-boot-autoconfigure}/neo4j/Neo4jProperties.{sc-ext}[Neo4jProperties])
+ spring.data.neo4j.compiler= # Compiler to use.
+ spring.data.neo4j.embedded.enabled=true # Enable embedded mode if the embedded driver is available.
+ spring.data.neo4j.password= # Login password of the server.
+ spring.data.neo4j.repositories.enabled=true # Enable Neo4j repositories.
+ spring.data.neo4j.session.scope=singleton # Scope (lifetime) of the session.
+ spring.data.neo4j.uri= # URI used by the driver. Auto-detected by default.
+ spring.data.neo4j.username= # Login user of the server.
+
# DATA REST ({sc-spring-boot-autoconfigure}/data/rest/RepositoryRestProperties.{sc-ext}[RepositoryRestProperties])
spring.data.rest.base-path= # Base path to be used by Spring Data REST to expose repository resources.
spring.data.rest.default-page-size= # Default size of pages.
diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
index 7c88a8e2e1..9871bbba53 100644
--- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
@@ -2805,9 +2805,9 @@ http://projects.spring.io/spring-data-redis/[Redis],
http://projects.spring.io/spring-data-gemfire/[Gemfire],
http://projects.spring.io/spring-data-couchbase/[Couchbase] and
http://projects.spring.io/spring-data-cassandra/[Cassandra].
-Spring Boot provides auto-configuration for Redis, MongoDB, Elasticsearch, Solr and
-Cassandra; you can make use of the other projects, but you will need to configure them
-yourself. Refer to the appropriate reference documentation at
+Spring Boot provides auto-configuration for Redis, MongoDB, Neo4j, Elasticsearch, Solr
+and Cassandra; you can make use of the other projects, but you will need to configure
+them yourself. Refer to the appropriate reference documentation at
http://projects.spring.io/spring-data[projects.spring.io/spring-data].
@@ -3009,6 +3009,122 @@ Mongo instance's configuration and logging routing.
+[[boot-features-neo4j]]
+=== Neo4j
+http://neo4j.com/[Neo4j] is an open-source NoSQL graph database that uses a rich data
+model of nodes related by first class relationships which is better suited for connected
+big data than traditional rdbms approaches. Spring Boot offers several conveniences for
+working with Neo4j, including the `spring-boot-starter-data-neo4j` '`Starter POM`'.
+
+
+
+[[boot-features-connecting-to-neo4j]]
+==== Connecting to a Neo4j database
+You can inject an auto-configured `Neo4jSession`, `Session` or `Neo4jOperations` instance
+as you would any other Spring Bean. By default the instance will attempt to connect to a
+Neo4j server using `localhost:7474`:
+
+[source,java,indent=0]
+----
+ @Component
+ public class MyBean {
+
+ private final Neo4jTemplate neo4jTemplate;
+
+ @Autowired
+ public MyBean(Neo4jTemplate neo4jTemplate) {
+ this.neo4jTemplate = neo4jTemplate;
+ }
+
+ // ...
+
+ }
+----
+
+You can take full control of the configuration by adding a
+`org.neo4j.ogm.config.Configuration` `@Bean` of your own. Also, adding a `@Bean` of type
+`Neo4jOperations` disables the auto-configuration.
+
+You can configure the user and credentials to use via the `spring.data.couchbase.*`
+properties:
+
+[source,properties,indent=0]
+----
+ spring.data.neo4j.uri=http://my-server:7474
+ spring.data.neo4j.username=neo4j
+ spring.data.neo4j.password=secret
+----
+
+[[boot-features-connecting-to-neo4j-embedded]]
+==== Using the embedded mode
+
+NOTE: Neo4j's embedded mode is subject to a different licensing, make sure to review it
+before integrating the dependency in your application.
+
+If you add `org.neo4j:neo4j-ogm-embedded-driver` to the dependencies of your application,
+Spring Boot will automatically configure an in-process embedded instance of Neo4j that
+will not persist any data when your application shuts down. You can explicitly disable
+that mode using `spring.data.neo4j.embedded.enabled=false`. You can also enable
+persistence for the embedded mode:
+
+----
+ spring.data.neo4j.uri=file://var/tmp/graph.db
+----
+
+[[boot-features-neo4j-ogm-session]]
+==== Neo4jSession
+
+By default, the lifetime of the session is scope to the application. If you are running a
+web application you can change it to scope or request easily:
+
+----
+ spring.data.neo4j.session.scope=session
+----
+
+
+
+[[boot-features-spring-data-neo4j-repositories]]
+==== Spring Data Neo4j repositories
+Spring Data includes repository support for Neo4j.
+
+In fact, both Spring Data JPA and Spring Data Neo4j share the same common
+infrastructure; so you could take the JPA example from earlier and, assuming that `City`
+is now a Neo4j OGM `@NodeEntity` rather than a JPA `@Entity`, it will work in the same way.
+
+TIP: You can customize entity scanning locations using the `@NodeEntityScan` annotation.
+
+To enable repository support (and optionally support for `@Transactional`), add the following
+two annotations to your Spring configuration:
+
+[source,java,indent=0]
+----
+ @EnableNeo4jRepositories(basePackages = "com.example.myapp.repository")
+ @EnableTransactionManagement
+----
+
+==== Repository example
+[source,java,indent=0]
+----
+ package com.example.myapp.domain;
+
+ import org.springframework.data.domain.*;
+ import org.springframework.data.repository.*;
+
+ public interface CityRepository extends GraphRepository {
+
+ Page findAll(Pageable pageable);
+
+ City findByNameAndCountry(String name, String country);
+
+ }
+----
+
+TIP: For complete details of Spring Data Neo4j, including its rich object mapping
+technologies, refer to their http://projects.spring.io/spring-data-neo4j/[reference
+documentation].
+
+
+
[[boot-features-gemfire]]
=== Gemfire
https://github.com/spring-projects/spring-data-gemfire[Spring Data Gemfire] provides
diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
index 01db16519e..d040454f67 100644
--- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
+++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
@@ -420,6 +420,9 @@ and Hibernate.
|`spring-boot-starter-data-redis`
|Support for the REDIS key-value data store, including `spring-data-redis`.
+|`spring-boot-starter-data-neo4j`
+|Support for the Neo4j Graph Database, including `spring-data-neo4j`.
+
|`spring-boot-starter-data-rest`
|Support for exposing Spring Data repositories over REST via `spring-data-rest-webmvc`.
diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml
index 4ad586d157..3fc8a3cfe3 100644
--- a/spring-boot-samples/pom.xml
+++ b/spring-boot-samples/pom.xml
@@ -38,6 +38,7 @@
spring-boot-sample-data-gemfire
spring-boot-sample-data-jpa
spring-boot-sample-data-mongodb
+ spring-boot-sample-data-neo4j
spring-boot-sample-data-redis
spring-boot-sample-data-rest
spring-boot-sample-data-solr
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/README.adoc b/spring-boot-samples/spring-boot-sample-data-neo4j/README.adoc
new file mode 100644
index 0000000000..96c1922e71
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/README.adoc
@@ -0,0 +1,23 @@
+= Spring Boot Neo4j Sample
+
+This sample demonstrates the integration of Neo4j with a simple entity. It
+expects a Neo4j instance running on `localhost`. If your neo4j instance
+requires authentication, update `application.properties` with your credentials:
+
+```
+spring.data.neo4j.username=neo4j
+spring.data.neo4j.password=secret
+```
+
+You can also locally add the embedded driver to embed Neo4j instead. Note
+that Spring Boot does not provide dependency management for that GPL-licensed
+library:
+
+```
+
+ org.neo4j
+ neo4j-ogm-embedded-driver
+ ${neo4j-ogm.version}
+
+```
+
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml b/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml
new file mode 100644
index 0000000000..f6a28a52f3
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-samples
+ 1.4.0.BUILD-SNAPSHOT
+
+ spring-boot-sample-data-neo4j
+ Spring Boot Data Neo4j Sample
+ Spring Boot Data Neo4j Sample
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-neo4j
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java
new file mode 100644
index 0000000000..4267e34b46
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012-2013 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 sample.data.neo4j;
+
+import org.neo4j.ogm.annotation.NodeEntity;
+import org.neo4j.ogm.annotation.GraphId;
+
+@NodeEntity
+public class Customer {
+
+ @GraphId
+ private Long id;
+
+ private String firstName;
+ private String lastName;
+
+ public Customer() {
+ }
+
+ public Customer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Customer[id=%s, firstName='%s', lastName='%s']", id,
+ firstName, lastName);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java
new file mode 100644
index 0000000000..6c809b901f
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012-2013 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 sample.data.neo4j;
+
+import java.util.List;
+
+import org.springframework.data.neo4j.repository.GraphRepository;
+
+public interface CustomerRepository extends GraphRepository {
+
+ public Customer findByFirstName(String firstName);
+
+ public List findByLastName(String lastName);
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java
new file mode 100644
index 0000000000..986f9b2902
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012-2015 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 sample.data.neo4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleNeo4jApplication implements CommandLineRunner {
+
+ @Autowired
+ private CustomerRepository repository;
+
+ @Override
+ public void run(String... args) throws Exception {
+ this.repository.deleteAll();
+
+ // save a couple of customers
+ this.repository.save(new Customer("Alice", "Smith"));
+ this.repository.save(new Customer("Bob", "Smith"));
+
+ // fetch all customers
+ System.out.println("Customers found with findAll():");
+ System.out.println("-------------------------------");
+ for (Customer customer : this.repository.findAll()) {
+ System.out.println(customer);
+ }
+ System.out.println();
+
+ // fetch an individual customer
+ System.out.println("Customer found with findByFirstName('Alice'):");
+ System.out.println("--------------------------------");
+ System.out.println(this.repository.findByFirstName("Alice"));
+
+ System.out.println("Customers found with findByLastName('Smith'):");
+ System.out.println("--------------------------------");
+ for (Customer customer : this.repository.findByLastName("Smith")) {
+ System.out.println(customer);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(SampleNeo4jApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/resources/application.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java
new file mode 100644
index 0000000000..15f808b403
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2015 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 sample.data.neo4j;
+
+import java.net.ConnectException;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.springframework.boot.test.rule.OutputCapture;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link SampleNeo4jApplication}.
+ *
+ * @author Stephane Nicoll
+ */
+public class SampleNeo4jApplicationTests {
+
+ @Rule
+ public OutputCapture outputCapture = new OutputCapture();
+
+ @Test
+ public void testDefaultSettings() throws Exception {
+ try {
+ SampleNeo4jApplication.main(new String[0]);
+ }
+ catch (Exception ex) {
+ if (!neo4jServerRunning(ex)) {
+ return;
+ }
+ }
+ String output = this.outputCapture.toString();
+ assertTrue("Wrong output: " + output,
+ output.contains("firstName='Alice', lastName='Smith'"));
+ }
+
+ private boolean neo4jServerRunning(Throwable ex) {
+ System.out.println(ex.getMessage());
+ if (ex instanceof ConnectException) {
+ return false;
+ }
+ return (ex.getCause() == null || neo4jServerRunning(ex.getCause()));
+ }
+
+}
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index e5667e3c3a..57c9258cdc 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -33,6 +33,7 @@
spring-boot-starter-data-gemfire
spring-boot-starter-data-jpa
spring-boot-starter-data-mongodb
+ spring-boot-starter-data-neo4j
spring-boot-starter-data-redis
spring-boot-starter-data-rest
spring-boot-starter-data-solr
@@ -69,6 +70,7 @@
spring-boot-starter-websocket
spring-boot-starter-ws
+
diff --git a/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml b/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml
new file mode 100644
index 0000000000..3a753b0c3e
--- /dev/null
+++ b/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml
@@ -0,0 +1,30 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starters
+ 1.4.0.BUILD-SNAPSHOT
+
+ spring-boot-starter-data-neo4j
+ Spring Boot Data Neo4j Starter
+ Spring Boot Data Neo4j Starter
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.data
+ spring-data-neo4j
+
+
+
diff --git a/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides
new file mode 100644
index 0000000000..38cc6363c5
--- /dev/null
+++ b/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides
@@ -0,0 +1 @@
+provides: spring-data-neo4j
\ No newline at end of file
diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml
index 291535ccf7..c853b19759 100644
--- a/spring-boot/pom.xml
+++ b/spring-boot/pom.xml
@@ -189,6 +189,11 @@
liquibase-core
true
+
+ org.neo4j
+ neo4j-ogm-core
+ true
+
org.slf4j
slf4j-api
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanBeanPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanBeanPostProcessor.java
new file mode 100644
index 0000000000..06885ef909
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanBeanPostProcessor.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2016 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.boot.context.scan;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.Ordered;
+
+/**
+ * A base {@link BeanPostProcessor} implementation that holds the packages to
+ * use for a given component. An implementation must implement
+ * {@link #postProcessBeforeInitialization(Object, String)} and update the
+ * component responsible to manage the packages to scan.
+ *
+ * @author Stephane Nicoll
+ * @since 1.4.0
+ */
+public abstract class AbstractEntityScanBeanPostProcessor implements BeanPostProcessor, Ordered {
+
+ private final String[] packagesToScan;
+
+ protected AbstractEntityScanBeanPostProcessor(String[] packagesToScan) {
+ this.packagesToScan = packagesToScan;
+ }
+
+ /**
+ * Return the packages to use.
+ * @return the packages to use.
+ */
+ protected String[] getPackagesToScan() {
+ return this.packagesToScan;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java b/spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanRegistrar.java
similarity index 57%
rename from spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java
rename to spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanRegistrar.java
index b633de5478..5d9cf355f4 100644
--- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScanRegistrar.java
+++ b/spring-boot/src/main/java/org/springframework/boot/context/scan/AbstractEntityScanRegistrar.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors.
+ * Copyright 2012-2016 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.
@@ -14,44 +14,69 @@
* limitations under the License.
*/
-package org.springframework.boot.orm.jpa;
+package org.springframework.boot.context.scan;
+import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.BeanPostProcessor;
-import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
-import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
-import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
- * {@link ImportBeanDefinitionRegistrar} used by {@link EntityScan}.
+ * A baseĀ {@link ImportBeanDefinitionRegistrar} used to collect the packages to
+ * scan for a given component.
+ *
+ * Expect to process an annotation type that defines a {@code basePackage} and
+ * {@code basePackageClasses} attributes as well as a {@code value} alias of
+ * {@code basePackage}.
+ *
+ * The {@link ImportBeanDefinitionRegistrar} registers a single
+ * {@link AbstractEntityScanBeanPostProcessor} implementation with the packages
+ * to use.
*
- * @author Phillip Webb
- * @author Oliver Gierke
+ * @author Stephane Nicoll
+ * @since 1.4.0
+ * @see AbstractEntityScanBeanPostProcessor
*/
-class EntityScanRegistrar implements ImportBeanDefinitionRegistrar {
+public abstract class AbstractEntityScanRegistrar implements ImportBeanDefinitionRegistrar {
+
+ private final Class extends Annotation> annotationType;
+
+ private final String beanPostProcessorName;
+
+ private final Class extends AbstractEntityScanBeanPostProcessor> beanPostProcessorType;
+
+ /**
+ * Create an instance.
+ * @param annotationType the annotation to inspect
+ * @param beanPostProcessorName the name of the bean post processor
+ * @param beanPostProcessorType the type of the bean post processor implementation
+ */
+ protected AbstractEntityScanRegistrar(Class extends Annotation> annotationType,
+ String beanPostProcessorName,
+ Class extends AbstractEntityScanBeanPostProcessor> beanPostProcessorType) {
+ this.beanPostProcessorName = beanPostProcessorName;
+ this.annotationType = annotationType;
+ this.beanPostProcessorType = beanPostProcessorType;
+ }
- private static final String BEAN_NAME = "entityScanBeanPostProcessor";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
Set packagesToScan = getPackagesToScan(importingClassMetadata);
- if (!registry.containsBeanDefinition(BEAN_NAME)) {
+ if (!registry.containsBeanDefinition(this.beanPostProcessorName)) {
addEntityScanBeanPostProcessor(registry, packagesToScan);
}
else {
@@ -59,15 +84,16 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar {
}
}
- private Set getPackagesToScan(AnnotationMetadata metadata) {
+ protected Set getPackagesToScan(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
- .fromMap(metadata.getAnnotationAttributes(EntityScan.class.getName()));
+ .fromMap(metadata.getAnnotationAttributes(this.annotationType.getName()));
String[] value = attributes.getStringArray("value");
String[] basePackages = attributes.getStringArray("basePackages");
Class>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
if (!ObjectUtils.isEmpty(value)) {
- Assert.state(ObjectUtils.isEmpty(basePackages),
- "@EntityScan basePackages and value attributes are mutually exclusive");
+ Assert.state(ObjectUtils.isEmpty(basePackages), String.format(
+ "@%s basePackages and value attributes are mutually exclusive",
+ this.annotationType.getSimpleName()));
}
Set packagesToScan = new LinkedHashSet();
packagesToScan.addAll(Arrays.asList(value));
@@ -85,20 +111,20 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar {
private void addEntityScanBeanPostProcessor(BeanDefinitionRegistry registry,
Set packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
- beanDefinition.setBeanClass(EntityScanBeanPostProcessor.class);
+ beanDefinition.setBeanClass(this.beanPostProcessorType);
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(toArray(packagesToScan));
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// We don't need this one to be post processed otherwise it can cause a
// cascade of bean instantiation that we would rather avoid.
beanDefinition.setSynthetic(true);
- registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
+ registry.registerBeanDefinition(this.beanPostProcessorName, beanDefinition);
}
private void updateEntityScanBeanPostProcessor(BeanDefinitionRegistry registry,
Set packagesToScan) {
- BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME);
- ValueHolder constructorArguments = definition.getConstructorArgumentValues()
+ BeanDefinition definition = registry.getBeanDefinition(this.beanPostProcessorName);
+ ConstructorArgumentValues.ValueHolder constructorArguments = definition.getConstructorArgumentValues()
.getGenericArgumentValue(String[].class);
Set mergedPackages = new LinkedHashSet();
mergedPackages.addAll(Arrays.asList((String[]) constructorArguments.getValue()));
@@ -110,52 +136,4 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar {
return set.toArray(new String[set.size()]);
}
- /**
- * {@link BeanPostProcessor} to set
- * {@link LocalContainerEntityManagerFactoryBean#setPackagesToScan(String...)} based
- * on an {@link EntityScan} annotation.
- */
- static class EntityScanBeanPostProcessor
- implements BeanPostProcessor, SmartInitializingSingleton, Ordered {
-
- private final String[] packagesToScan;
-
- private boolean processed;
-
- EntityScanBeanPostProcessor(String[] packagesToScan) {
- this.packagesToScan = packagesToScan;
- }
-
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName)
- throws BeansException {
- if (bean instanceof LocalContainerEntityManagerFactoryBean) {
- LocalContainerEntityManagerFactoryBean factoryBean = (LocalContainerEntityManagerFactoryBean) bean;
- factoryBean.setPackagesToScan(this.packagesToScan);
- this.processed = true;
- }
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName)
- throws BeansException {
- return bean;
- }
-
- @Override
- public void afterSingletonsInstantiated() {
- Assert.state(this.processed,
- "Unable to configure "
- + "LocalContainerEntityManagerFactoryBean from @EntityScan, "
- + "ensure an appropriate bean is registered.");
- }
-
- @Override
- public int getOrder() {
- return 0;
- }
-
- }
-
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/scan/package-info.java b/spring-boot/src/main/java/org/springframework/boot/context/scan/package-info.java
new file mode 100644
index 0000000000..c6a135a597
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/context/scan/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012-2016 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.
+ */
+
+/**
+ * Support for component scanning.
+ */
+package org.springframework.boot.context.scan;
diff --git a/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScan.java b/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScan.java
new file mode 100644
index 0000000000..149234fa99
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScan.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2012-2016 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.boot.neo4j;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.neo4j.ogm.session.SessionFactory;
+
+import org.springframework.context.annotation.Import;
+
+/**
+ * Configures the {@link SessionFactory} to scan for node entity
+ * classes in the classpath. This annotation provides an alternative to manually setting
+ * {@link SessionFactoryProvider#setPackagesToScan(String...)} and is
+ * particularly useful if you want to configure entity scanning in a type-safe way, or if
+ * your {@link SessionFactory} is auto-configured.
+ *
+ * A {@link SessionFactoryProvider} must be configured within your Spring
+ * ApplicationContext in order to use entity scanning. Furthermore, any existing
+ * {@code packagesToScan} setting will be replaced.
+ *
+ * One of {@link #basePackageClasses()}, {@link #basePackages()} or its alias
+ * {@link #value()} may be specified to define specific packages to scan. If specific
+ * packages are not defined scanning will occur from the package of the class with this
+ * annotation.
+ *
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ * @since 1.4.0
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(NodeEntityScanRegistrar.class)
+public @interface NodeEntityScan {
+
+ /**
+ * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
+ * declarations e.g.: {@code @NodeEntityScan("org.my.pkg")} instead of
+ * {@code @NodeEntityScan(basePackages="org.my.pkg")}.
+ * @return the base packages to scan
+ */
+ String[] value() default {};
+
+ /**
+ * Base packages to scan for node entities. {@link #value()} is an alias for (and
+ * mutually exclusive with) this attribute.
+ *
+ * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
+ * package names.
+ * @return the base packages to scan
+ */
+ String[] basePackages() default {};
+
+ /**
+ * Type-safe alternative to {@link #basePackages()} for specifying the packages to
+ * scan for node entities. The package of each class specified will be scanned.
+ *
+ * Consider creating a special no-op marker class or interface in each package that
+ * serves no purpose other than being referenced by this attribute.
+ * @return classes from the base packages to scan
+ */
+ Class>[] basePackageClasses() default {};
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScanRegistrar.java b/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScanRegistrar.java
new file mode 100644
index 0000000000..64c2cdfbdc
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/neo4j/NodeEntityScanRegistrar.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012-2016 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.boot.neo4j;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.context.scan.AbstractEntityScanBeanPostProcessor;
+import org.springframework.boot.context.scan.AbstractEntityScanRegistrar;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.util.Assert;
+
+/**
+ * {@link ImportBeanDefinitionRegistrar} used by {@link NodeEntityScan}.
+ *
+ * @author Stephane Nicoll
+ */
+class NodeEntityScanRegistrar extends AbstractEntityScanRegistrar {
+
+ NodeEntityScanRegistrar() {
+ super(NodeEntityScan.class, "nodeEntityScanBeanPostProcessor", NodeEntityScanBeanPostProcessor.class);
+ }
+
+ /**
+ * {@link BeanPostProcessor} to set
+ * {@link SessionFactoryProvider#setPackagesToScan(String...)} based
+ * on an {@link NodeEntityScan} annotation.
+ */
+ static class NodeEntityScanBeanPostProcessor extends AbstractEntityScanBeanPostProcessor
+ implements SmartInitializingSingleton {
+
+ private boolean processed;
+
+ NodeEntityScanBeanPostProcessor(String[] packagesToScan) {
+ super(packagesToScan);
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof SessionFactoryProvider) {
+ SessionFactoryProvider provider = (SessionFactoryProvider) bean;
+ provider.setPackagesToScan(getPackagesToScan());
+ this.processed = true;
+ }
+ return bean;
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ Assert.state(this.processed,
+ "Unable to configure "
+ + "SessionFactoryFactoryBean from @NodeEntityScan, "
+ + "ensure an appropriate bean is registered.");
+ }
+
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/neo4j/SessionFactoryProvider.java b/spring-boot/src/main/java/org/springframework/boot/neo4j/SessionFactoryProvider.java
new file mode 100644
index 0000000000..7ab1793bb8
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/neo4j/SessionFactoryProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2016 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.boot.neo4j;
+
+import org.neo4j.ogm.config.Configuration;
+import org.neo4j.ogm.session.SessionFactory;
+
+/**
+ * Provide a a Neo4j {@link SessionFactory} instance based on a
+ * configurable {@link Configuration} and custom packages to scan.
+ *
+ * @author Stephane Nicoll
+ * @since 1.4.0
+ * @see NodeEntityScan
+ */
+public class SessionFactoryProvider {
+
+ private Configuration configuration;
+
+ private String[] packagesToScan;
+
+ /**
+ * Set the configuration to use.
+ * @param configuration the configuration
+ */
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Set the packages to scan.
+ * @param packagesToScan the packages to scan
+ */
+ public void setPackagesToScan(String[] packagesToScan) {
+ this.packagesToScan = packagesToScan;
+ }
+
+ public SessionFactory getSessionFactory() {
+ return new SessionFactory(this.configuration, this.packagesToScan);
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/neo4j/package-info.java b/spring-boot/src/main/java/org/springframework/boot/neo4j/package-info.java
new file mode 100644
index 0000000000..b588d49c29
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/neo4j/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012-2016 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.
+ */
+
+/**
+ * Neo4j support classes.
+ */
+package org.springframework.boot.neo4j;
diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java
index 4c37d96f87..9e231c556e 100644
--- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java
+++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java
@@ -46,7 +46,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
-@Import(EntityScanRegistrar.class)
+@Import(JpaEntityScanRegistrar.class)
public @interface EntityScan {
/**
diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/JpaEntityScanRegistrar.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/JpaEntityScanRegistrar.java
new file mode 100644
index 0000000000..b73e0e08aa
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/JpaEntityScanRegistrar.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012-2015 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.boot.orm.jpa;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.context.scan.AbstractEntityScanBeanPostProcessor;
+import org.springframework.boot.context.scan.AbstractEntityScanRegistrar;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.util.Assert;
+
+/**
+ * {@link ImportBeanDefinitionRegistrar} used by {@link EntityScan}.
+ *
+ * @author Phillip Webb
+ * @author Oliver Gierke
+ */
+class JpaEntityScanRegistrar extends AbstractEntityScanRegistrar {
+
+ JpaEntityScanRegistrar() {
+ super(EntityScan.class, "entityScanBeanPostProcessor", JpaEntityScanBeanPostProcessor.class);
+ }
+
+ /**
+ * {@link BeanPostProcessor} to set
+ * {@link LocalContainerEntityManagerFactoryBean#setPackagesToScan(String...)} based
+ * on an {@link EntityScan} annotation.
+ */
+ static class JpaEntityScanBeanPostProcessor extends AbstractEntityScanBeanPostProcessor
+ implements SmartInitializingSingleton {
+
+ private boolean processed;
+
+ JpaEntityScanBeanPostProcessor(String[] packagesToScan) {
+ super(packagesToScan);
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof LocalContainerEntityManagerFactoryBean) {
+ LocalContainerEntityManagerFactoryBean factoryBean = (LocalContainerEntityManagerFactoryBean) bean;
+ factoryBean.setPackagesToScan(getPackagesToScan());
+ this.processed = true;
+ }
+ return bean;
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ Assert.state(this.processed,
+ "Unable to configure "
+ + "LocalContainerEntityManagerFactoryBean from @EntityScan, "
+ + "ensure an appropriate bean is registered.");
+ }
+
+ }
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScan.java b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScan.java
new file mode 100644
index 0000000000..af45607ccc
--- /dev/null
+++ b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScan.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2016 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.boot.context.scan;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+/**
+ * EntityScan test annotation.
+ *
+ * @author Stephane Nicoll
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(TestEntityScanRegistrar.class)
+public @interface TestEntityScan {
+
+ String[] value() default {};
+
+ String[] basePackages() default {};
+
+ Class>[] basePackageClasses() default {};
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanRegistrar.java b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanRegistrar.java
new file mode 100644
index 0000000000..7976c19eea
--- /dev/null
+++ b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanRegistrar.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2016 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.boot.context.scan;
+
+import org.springframework.beans.BeansException;
+
+/**
+ * Test implementation of {@link AbstractEntityScanRegistrar}.
+ *
+ * @author Stephane Nicoll
+ */
+class TestEntityScanRegistrar extends AbstractEntityScanRegistrar {
+
+ static final String BEAN_NAME = "testEntityScanBeanPostProcessor";
+
+ TestEntityScanRegistrar() {
+ super(TestEntityScan.class, BEAN_NAME, TestEntityScanBeanPostProcessor.class);
+ }
+
+ static class TestFactoryBean {
+ private String[] packagesToScan;
+
+ public void setPackagesToScan(String... packagesToScan) {
+ this.packagesToScan = packagesToScan;
+ }
+
+ public String[] getPackagesToScan() {
+ return this.packagesToScan;
+ }
+
+ }
+
+ static class TestEntityScanBeanPostProcessor extends AbstractEntityScanBeanPostProcessor {
+
+ TestEntityScanBeanPostProcessor(String[] packagesToScan) {
+ super(packagesToScan);
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof TestFactoryBean) {
+ TestFactoryBean factoryBean = (TestFactoryBean) bean;
+ factoryBean.setPackagesToScan(getPackagesToScan());
+ }
+ return bean;
+ }
+
+ }
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanTests.java b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanTests.java
new file mode 100644
index 0000000000..c21377cc34
--- /dev/null
+++ b/spring-boot/src/test/java/org/springframework/boot/context/scan/TestEntityScanTests.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2012-2016 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.boot.context.scan;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.boot.context.scan.TestEntityScanRegistrar.TestFactoryBean;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link TestEntityScan}.
+ *
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ */
+public class TestEntityScanTests {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void closeContext() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void testValue() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(ValueConfig.class);
+ assertSetPackagesToScan("com.mycorp.entity");
+ }
+
+ @Test
+ public void basePackages() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(BasePackagesConfig.class);
+ assertSetPackagesToScan("com.mycorp.entity2");
+ }
+
+ @Test
+ public void basePackageClasses() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(
+ BasePackageClassesConfig.class);
+ assertSetPackagesToScan(getClass().getPackage().getName());
+ }
+
+ @Test
+ public void fromConfigurationClass() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(FromConfigConfig.class);
+ assertSetPackagesToScan(getClass().getPackage().getName());
+ }
+
+ @Test
+ public void valueAndBasePackagesThrows() throws Exception {
+ this.thrown.expect(IllegalStateException.class);
+ this.thrown.expectMessage("@TestEntityScan basePackages and value "
+ + "attributes are mutually exclusive");
+ this.context = new AnnotationConfigApplicationContext(ValueAndBasePackages.class);
+ }
+
+ @Test
+ public void valueAndBasePackageClassesMerges() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(
+ ValueAndBasePackageClasses.class);
+ assertSetPackagesToScan("com.mycorp.entity", getClass().getPackage().getName());
+ }
+
+ @Test
+ public void basePackageAndBasePackageClassesMerges() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(
+ BasePackagesAndBasePackageClasses.class);
+ assertSetPackagesToScan("com.mycorp.entity2", getClass().getPackage().getName());
+ }
+
+ @Test
+ public void considersMultipleAnnotations() {
+ this.context = new AnnotationConfigApplicationContext(MultiScanFirst.class,
+ MultiScanSecond.class);
+ assertSetPackagesToScan("foo", "bar");
+ }
+
+ private void assertSetPackagesToScan(String... expected) {
+ String[] actual = this.context
+ .getBean(TestFactoryBean.class)
+ .getPackagesToScan();
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Configuration
+ static class BaseConfig {
+
+ @Bean
+ public TestFactoryBean testFactoryBean() {
+ return new TestFactoryBean();
+ }
+
+ }
+
+ @TestEntityScan("com.mycorp.entity")
+ static class ValueConfig extends BaseConfig {
+ }
+
+ @TestEntityScan(basePackages = "com.mycorp.entity2")
+ static class BasePackagesConfig extends BaseConfig {
+ }
+
+ @TestEntityScan(basePackageClasses = TestEntityScanTests.class)
+ static class BasePackageClassesConfig extends BaseConfig {
+ }
+
+ @TestEntityScan
+ static class FromConfigConfig extends BaseConfig {
+ }
+
+ @TestEntityScan(value = "com.mycorp.entity", basePackages = "com.mycorp")
+ static class ValueAndBasePackages extends BaseConfig {
+ }
+
+ @TestEntityScan(value = "com.mycorp.entity", basePackageClasses = TestEntityScanTests.class)
+ static class ValueAndBasePackageClasses extends BaseConfig {
+ }
+
+ @TestEntityScan(basePackages = "com.mycorp.entity2", basePackageClasses = TestEntityScanTests.class)
+ static class BasePackagesAndBasePackageClasses extends BaseConfig {
+ }
+
+ @TestEntityScan(basePackages = "foo")
+ static class MultiScanFirst extends BaseConfig {
+ }
+
+ @TestEntityScan(basePackages = "bar")
+ static class MultiScanSecond extends BaseConfig {
+ }
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/neo4j/NodeEntityScanTests.java b/spring-boot/src/test/java/org/springframework/boot/neo4j/NodeEntityScanTests.java
new file mode 100644
index 0000000000..bb24396be7
--- /dev/null
+++ b/spring-boot/src/test/java/org/springframework/boot/neo4j/NodeEntityScanTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012-2016 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.boot.neo4j;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link NodeEntityScan}.
+ *
+ * @author Stephane Nicoll
+ */
+public class NodeEntityScanTests {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private AnnotationConfigApplicationContext context;
+
+ @After
+ public void closeContext() {
+ if (this.context != null) {
+ this.context.close();
+ }
+ }
+
+ @Test
+ public void simpleValue() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(ValueConfig.class);
+ assertSetPackagesToScan("com.mycorp.entity");
+ }
+
+ @Test
+ public void needsSessionFactoryFactory() throws Exception {
+ this.thrown.expect(IllegalStateException.class);
+ this.thrown.expectMessage("Unable to configure "
+ + "SessionFactoryFactoryBean from @NodeEntityScan, "
+ + "ensure an appropriate bean is registered.");
+ this.context = new AnnotationConfigApplicationContext(MissingSessionFactory.class);
+ }
+
+ private void assertSetPackagesToScan(String... expected) {
+ String[] actual = this.context
+ .getBean(TestSessionFactoryProvider.class)
+ .getPackagesToScan();
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Configuration
+ static class BaseConfig {
+
+ @Bean
+ public SessionFactoryProvider sessionFactoryFactoryBean() {
+ return new TestSessionFactoryProvider();
+ }
+
+ }
+
+ @NodeEntityScan("com.mycorp.entity")
+ static class ValueConfig extends BaseConfig {
+ }
+
+ @Configuration
+ @NodeEntityScan("com.mycorp.entity")
+ static class MissingSessionFactory {
+ }
+
+
+ private static class TestSessionFactoryProvider
+ extends SessionFactoryProvider {
+
+ private String[] packagesToScan;
+
+ @Override
+ public void setPackagesToScan(String... packagesToScan) {
+ this.packagesToScan = packagesToScan;
+ }
+
+ public String[] getPackagesToScan() {
+ return this.packagesToScan;
+ }
+
+ }
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java b/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java
index 01cdcfdb39..93101b2d1f 100644
--- a/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/orm/jpa/EntityScanTests.java
@@ -19,6 +19,7 @@ package org.springframework.boot.orm.jpa;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
+import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -37,6 +38,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link EntityScan}.
*
* @author Phillip Webb
+ * @author Stephane Nicoll
*/
public class EntityScanTests {
@@ -45,51 +47,17 @@ public class EntityScanTests {
private AnnotationConfigApplicationContext context;
- @Test
- public void testValue() throws Exception {
- this.context = new AnnotationConfigApplicationContext(ValueConfig.class);
- assertSetPackagesToScan("com.mycorp.entity");
- }
-
- @Test
- public void basePackages() throws Exception {
- this.context = new AnnotationConfigApplicationContext(BasePackagesConfig.class);
- assertSetPackagesToScan("com.mycorp.entity2");
- }
-
- @Test
- public void basePackageClasses() throws Exception {
- this.context = new AnnotationConfigApplicationContext(
- BasePackageClassesConfig.class);
- assertSetPackagesToScan(getClass().getPackage().getName());
- }
-
- @Test
- public void fromConfigurationClass() throws Exception {
- this.context = new AnnotationConfigApplicationContext(FromConfigConfig.class);
- assertSetPackagesToScan(getClass().getPackage().getName());
- }
-
- @Test
- public void valueAndBasePackagesThrows() throws Exception {
- this.thrown.expect(IllegalStateException.class);
- this.thrown.expectMessage("@EntityScan basePackages and value "
- + "attributes are mutually exclusive");
- this.context = new AnnotationConfigApplicationContext(ValueAndBasePackages.class);
- }
-
- @Test
- public void valueAndBasePackageClassesMerges() throws Exception {
- this.context = new AnnotationConfigApplicationContext(
- ValueAndBasePackageClasses.class);
- assertSetPackagesToScan("com.mycorp.entity", getClass().getPackage().getName());
+ @After
+ public void closeContext() {
+ if (this.context != null) {
+ this.context.close();
+ }
}
@Test
- public void basePackageAndBasePackageClassesMerges() throws Exception {
- this.context = new AnnotationConfigApplicationContext(
- BasePackagesAndBasePackageClasses.class);
- assertSetPackagesToScan("com.mycorp.entity2", getClass().getPackage().getName());
+ public void simpleValue() throws Exception {
+ this.context = new AnnotationConfigApplicationContext(ValueConfig.class);
+ assertSetPackagesToScan("com.mycorp.entity");
}
@Test
@@ -108,13 +76,6 @@ public class EntityScanTests {
assertSetPackagesToScan("com.mycorp.entity");
}
- @Test
- public void considersMultipleEntityScanAnnotations() {
- this.context = new AnnotationConfigApplicationContext(MultiScanFirst.class,
- MultiScanSecond.class);
- assertSetPackagesToScan("foo", "bar");
- }
-
private void assertSetPackagesToScan(String... expected) {
String[] actual = this.context
.getBean(TestLocalContainerEntityManagerFactoryBean.class)
@@ -136,30 +97,6 @@ public class EntityScanTests {
static class ValueConfig extends BaseConfig {
}
- @EntityScan(basePackages = "com.mycorp.entity2")
- static class BasePackagesConfig extends BaseConfig {
- }
-
- @EntityScan(basePackageClasses = EntityScanTests.class)
- static class BasePackageClassesConfig extends BaseConfig {
- }
-
- @EntityScan
- static class FromConfigConfig extends BaseConfig {
- }
-
- @EntityScan(value = "com.mycorp.entity", basePackages = "com.mycorp")
- static class ValueAndBasePackages extends BaseConfig {
- }
-
- @EntityScan(value = "com.mycorp.entity", basePackageClasses = EntityScanTests.class)
- static class ValueAndBasePackageClasses extends BaseConfig {
- }
-
- @EntityScan(basePackages = "com.mycorp.entity2", basePackageClasses = EntityScanTests.class)
- static class BasePackagesAndBasePackageClasses extends BaseConfig {
- }
-
@Configuration
@EntityScan("com.mycorp.entity")
static class MissingEntityManager {
@@ -195,16 +132,6 @@ public class EntityScanTests {
}
}
- @EntityScan(basePackages = "foo")
- static class MultiScanFirst extends BaseConfig {
-
- }
-
- @EntityScan(basePackages = "bar")
- static class MultiScanSecond extends BaseConfig {
-
- }
-
private static class TestLocalContainerEntityManagerFactoryBean
extends LocalContainerEntityManagerFactoryBean {