Merge pull request #5458 from jexp/contrib

* pr/5458:
  Polish contribution
  Add Neo4j support
pull/5551/head
Stephane Nicoll 9 years ago
commit aa171d1945

@ -166,6 +166,11 @@
<artifactId>spring-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>

@ -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" })

@ -379,6 +379,17 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<artifactId>jcl-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
@ -635,5 +646,6 @@
<artifactId>tomcat-embed-jasper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -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.
* <p>
* 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}.
* <p>
* 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 {
}

@ -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 {
}
}

@ -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;

@ -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.
* <p>
* 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> sessionFactoryProvider;
public SpringBootNeo4jConfiguration(ObjectProvider<SessionFactoryProvider> 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<String> 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();
}
}
}

@ -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;
}
}
}

@ -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;

@ -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": [

@ -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,\

@ -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<City> {
}

@ -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<String> names = new ArrayList<String>();
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()]);
}
}
}

@ -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();
}
}
}

@ -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 {
}
}

@ -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();
}
}

@ -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<City> {
Page<City> findAll(Pageable pageable);
}

@ -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();
}
}

@ -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<Country> {
}

@ -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;
}
}
}

@ -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 {
}
}

@ -55,6 +55,7 @@
<cassandra-driver.version>2.1.9</cassandra-driver.version>
<commons-beanutils.version>1.9.2</commons-beanutils.version>
<commons-collections.version>3.2.2</commons-collections.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-dbcp.version>1.4</commons-dbcp.version>
<commons-dbcp2.version>2.1.1</commons-dbcp2.version>
<commons-digester.version>2.1</commons-digester.version>
@ -122,6 +123,7 @@
<mongodb.version>2.14.1</mongodb.version>
<mysql.version>5.1.38</mysql.version>
<nekohtml.version>1.9.22</nekohtml.version>
<neo4j-ogm.version>2.0.0-M04</neo4j-ogm.version>
<postgresql.version>9.4.1208.jre7</postgresql.version>
<reactor.version>2.0.7.RELEASE</reactor.version>
<reactor-spring.version>2.0.7.RELEASE</reactor-spring.version>
@ -325,6 +327,11 @@
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>1.4.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
<version>1.4.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
@ -768,6 +775,11 @@
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
@ -1760,6 +1772,26 @@
<artifactId>mongo-java-driver</artifactId>
<version>${mongodb.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-api</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-compiler</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>

@ -488,6 +488,11 @@
<artifactId>spring-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>

@ -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.

@ -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<City> {
Page<City> 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

@ -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`.

@ -38,6 +38,7 @@
<module>spring-boot-sample-data-gemfire</module>
<module>spring-boot-sample-data-jpa</module>
<module>spring-boot-sample-data-mongodb</module>
<module>spring-boot-sample-data-neo4j</module>
<module>spring-boot-sample-data-redis</module>
<module>spring-boot-sample-data-rest</module>
<module>spring-boot-sample-data-solr</module>

@ -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:
```
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-embedded-driver</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
```

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.4.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-data-neo4j</artifactId>
<name>Spring Boot Data Neo4j Sample</name>
<description>Spring Boot Data Neo4j Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -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);
}
}

@ -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<Customer> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}

@ -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);
}
}

@ -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()));
}
}

@ -33,6 +33,7 @@
<module>spring-boot-starter-data-gemfire</module>
<module>spring-boot-starter-data-jpa</module>
<module>spring-boot-starter-data-mongodb</module>
<module>spring-boot-starter-data-neo4j</module>
<module>spring-boot-starter-data-redis</module>
<module>spring-boot-starter-data-rest</module>
<module>spring-boot-starter-data-solr</module>
@ -69,6 +70,7 @@
<module>spring-boot-starter-websocket</module>
<module>spring-boot-starter-ws</module>
</modules>
<build>
<plugins>
<plugin>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>1.4.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
<name>Spring Boot Data Neo4j Starter</name>
<description>Spring Boot Data Neo4j Starter</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
</dependency>
</dependencies>
</project>

@ -189,6 +189,11 @@
<artifactId>liquibase-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

@ -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;
}
}

@ -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.
* <p>
* 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}.
* <p>
* 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<String> 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<String> getPackagesToScan(AnnotationMetadata metadata) {
protected Set<String> 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<String> packagesToScan = new LinkedHashSet<String>();
packagesToScan.addAll(Arrays.asList(value));
@ -85,20 +111,20 @@ class EntityScanRegistrar implements ImportBeanDefinitionRegistrar {
private void addEntityScanBeanPostProcessor(BeanDefinitionRegistry registry,
Set<String> 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<String> 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<String> mergedPackages = new LinkedHashSet<String>();
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;
}
}
}

@ -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;

@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 {};
}

@ -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.");
}
}
}

@ -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);
}
}

@ -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;

@ -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 {
/**

@ -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.");
}
}
}

@ -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 {};
}

@ -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;
}
}
}

@ -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 {
}
}

@ -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;
}
}
}

@ -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 {

Loading…
Cancel
Save