From e08b7d12ee9ff6ccb3a615fed07db6492f49e723 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Jan 2013 15:38:57 +0100 Subject: [PATCH] DATAGRAPH-328 Upgrade SD-Commons to 1.5-SNAPSHOT, Added Auditing Support --- spring-data-neo4j-aspects/pom.xml | 7 +- spring-data-neo4j-cross-store/pom.xml | 7 +- spring-data-neo4j-parent/pom.xml | 14 +- spring-data-neo4j/pom.xml | 7 +- .../data/neo4j/annotation/GraphId.java | 3 + .../config/DataGraphBeanDefinitionParser.java | 3 + .../Neo4jAuditingBeanDefinitionParser.java | 101 +++++++++++++++ .../data/neo4j/config/Neo4jConfiguration.java | 8 ++ .../neo4j/config/Neo4jNamespaceHandler.java | 33 +++++ .../data/neo4j/lifecycle/AfterSaveEvent.java | 20 +-- .../lifecycle/AuditingEventListener.java | 53 ++++++++ .../data/neo4j/lifecycle/BeforeSaveEvent.java | 18 +-- .../data/neo4j/lifecycle/DeleteEvent.java | 14 +- .../neo4j/lifecycle/Neo4jLifecycleEvent.java | 31 +++++ .../mapping/Neo4jPersistentProperty.java | 2 +- .../MappingInfrastructureFactoryBean.java | 16 ++- .../mapping/Neo4JPersistentPropertyImpl.java | 19 ++- .../main/resources/META-INF/spring.handlers | 2 +- .../main/resources/META-INF/spring.schemas | 3 +- .../data/neo4j/config/spring-neo4j-2.2.xsd | 120 ++++++++++++++++++ .../config/AuditingIntegrationTests.java | 78 ++++++++++++ .../AuditingEventListenerUnitTests.java | 92 ++++++++++++++ .../data/neo4j/config/auditing-bean.xml | 19 +++ .../data/neo4j/config/auditing.xml | 14 ++ 24 files changed, 626 insertions(+), 58 deletions(-) create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingBeanDefinitionParser.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jNamespaceHandler.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AuditingEventListener.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/Neo4jLifecycleEvent.java create mode 100644 spring-data-neo4j/src/main/resources/org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/AuditingIntegrationTests.java create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/lifecycle/AuditingEventListenerUnitTests.java create mode 100644 spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing-bean.xml create mode 100644 spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing.xml diff --git a/spring-data-neo4j-aspects/pom.xml b/spring-data-neo4j-aspects/pom.xml index 0227bedf0..b10b490dc 100644 --- a/spring-data-neo4j-aspects/pom.xml +++ b/spring-data-neo4j-aspects/pom.xml @@ -33,9 +33,14 @@ + + joda-time + joda-time + true + org.springframework.data - spring-data-commons-core + spring-data-commons org.springframework.data diff --git a/spring-data-neo4j-cross-store/pom.xml b/spring-data-neo4j-cross-store/pom.xml index f6cf8d84a..3575ac9a6 100644 --- a/spring-data-neo4j-cross-store/pom.xml +++ b/spring-data-neo4j-cross-store/pom.xml @@ -32,9 +32,14 @@ + + joda-time + joda-time + true + org.springframework.data - spring-data-commons-core + spring-data-commons org.springframework.data diff --git a/spring-data-neo4j-parent/pom.xml b/spring-data-neo4j-parent/pom.xml index afe762039..d3c275099 100644 --- a/spring-data-neo4j-parent/pom.xml +++ b/spring-data-neo4j-parent/pom.xml @@ -124,7 +124,7 @@ 1.6.1 3.0.7.RELEASE 3.1.2.RELEASE - 1.4.0.RELEASE + 1.5.0.BUILD-SNAPSHOT 1.8 0.9 1.6.12 @@ -281,9 +281,15 @@ + + joda-time + joda-time + 1.6 + true + org.springframework.data - spring-data-commons-core + spring-data-commons ${data.commons.version} @@ -752,6 +758,10 @@ spring-libs-milestone http://repo.springsource.org/libs-milestone + + spring-libs-snapshot + http://repo.springsource.org/libs-snapshot + tinkerpop-repository http://tinkerpop.com/maven2 diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml index 7db27e5bb..479b287ad 100644 --- a/spring-data-neo4j/pom.xml +++ b/spring-data-neo4j/pom.xml @@ -36,9 +36,14 @@ + + joda-time + joda-time + true + org.springframework.data - spring-data-commons-core + spring-data-commons diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphId.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphId.java index 1059d44b9..34939598a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphId.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphId.java @@ -16,6 +16,8 @@ package org.springframework.data.neo4j.annotation; +import org.springframework.data.annotation.Id; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -26,5 +28,6 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.METHOD}) +@Id public @interface GraphId { } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java index cf9e45b12..85f3aea43 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java @@ -29,6 +29,8 @@ import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.annotation.Persistent; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelationshipEntity; +import org.springframework.data.repository.config.RepositoryBeanDefinitionParser; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.util.ClassUtils; import org.w3c.dom.Element; @@ -43,6 +45,7 @@ public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser public static final String ASPECTJ_CONFIG = "org.springframework.data.neo4j.aspects.config.Neo4jAspectConfiguration"; public static final String CROSS_STORE_CONFIG = "org.springframework.data.neo4j.cross_store.config.CrossStoreNeo4jConfiguration"; + @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext context) { BeanDefinitionBuilder configBuilder = createConfigurationBeanDefinition(element); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingBeanDefinitionParser.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingBeanDefinitionParser.java new file mode 100644 index 000000000..8c6c0c855 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingBeanDefinitionParser.java @@ -0,0 +1,101 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.config; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.config.BeanComponentDefinitionBuilder; +import org.springframework.data.config.IsNewAwareAuditingHandlerBeanDefinitionParser; +import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; +import org.springframework.data.neo4j.lifecycle.AuditingEventListener; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * {@link BeanDefinitionParser} to register a {@link AuditingEventListener} to transparently set auditing information on + * an entity. + * + * @author Oliver Gierke + */ +public class Neo4jAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected Class getBeanClass(Element element) { + return AuditingEventListener.class; + } + + @Override + protected boolean shouldGenerateId() { + return true; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) + */ + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + BeanDefinitionRegistry registry = parserContext.getRegistry(); + + if (!registry.containsBeanDefinition(IS_NEW_STRATEGY_FACTORY)) { + + String templateName = MAPPING_CONTEXT; + + if (!registry.containsBeanDefinition(MAPPING_CONTEXT)) { + templateName = resolveMappingContextRef(element); + } + + createIsNewStrategyFactoryBeanDefinition(templateName, parserContext, element); + } + + BeanDefinitionParser parser = new IsNewAwareAuditingHandlerBeanDefinitionParser(IS_NEW_STRATEGY_FACTORY); + BeanDefinition handlerBeanDefinition = parser.parse(element, parserContext); + + builder.addConstructorArgValue(handlerBeanDefinition); + } + + static String resolveMappingContextRef(Element element) { + + String ctxRef = element.getAttribute("mapping-context-ref"); + + if (StringUtils.hasText(ctxRef)) { + return ctxRef; + } + + return "neo4jMappingContext"; + } + + private String createIsNewStrategyFactoryBeanDefinition(String mappingContextRef, ParserContext context, + Element element) { + + BeanDefinitionBuilder mappingContextStrategyFactoryBuilder = BeanDefinitionBuilder + .rootBeanDefinition(MappingContextIsNewStrategyFactory.class); + mappingContextStrategyFactoryBuilder.addConstructorArgReference(mappingContextRef); + BeanComponentDefinitionBuilder builder = new BeanComponentDefinitionBuilder(element, context); + context.registerBeanComponent(builder.getComponent(mappingContextStrategyFactoryBuilder, IS_NEW_STRATEGY_FACTORY)); + + return IS_NEW_STRATEGY_FACTORY; + } + + + static final String MAPPING_CONTEXT = "neo4jMappingContext"; + static final String IS_NEW_STRATEGY_FACTORY = "isNewStrategyFactory"; +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java index dc1428626..34cc4f8f2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.core.convert.ConversionService; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.convert.DefaultTypeMapper; import org.springframework.data.convert.TypeMapper; +import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; import org.springframework.data.neo4j.core.GraphDatabase; import org.springframework.data.neo4j.core.TypeRepresentationStrategy; import org.springframework.data.neo4j.fieldaccess.FieldAccessorFactoryFactory; @@ -55,6 +56,7 @@ import org.springframework.data.neo4j.support.relationship.RelationshipEntityIns import org.springframework.data.neo4j.support.relationship.RelationshipEntityStateFactory; import org.springframework.data.neo4j.support.typerepresentation.ClassValueTypeInformationMapper; import org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory; +import org.springframework.data.support.IsNewStrategyFactory; import org.springframework.transaction.PlatformTransactionManager; import javax.validation.Validator; @@ -114,6 +116,7 @@ public abstract class Neo4jConfiguration { factoryBean.setTransactionManager(neo4jTransactionManager()); factoryBean.setGraphDatabase(graphDatabase()); + factoryBean.setIsNewStrategyFactory(isNewStrategyFactory()); factoryBean.setIndexProvider(indexProvider()); @@ -122,6 +125,11 @@ public abstract class Neo4jConfiguration { } return factoryBean; } + + @Bean + public IsNewStrategyFactory isNewStrategyFactory() throws Exception { + return new MappingContextIsNewStrategyFactory(neo4jMappingContext()); + } @Bean public Neo4jTemplate neo4jTemplate() throws Exception { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jNamespaceHandler.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jNamespaceHandler.java new file mode 100644 index 000000000..fe3183338 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jNamespaceHandler.java @@ -0,0 +1,33 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.config; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; +import org.springframework.data.repository.config.RepositoryBeanDefinitionParser; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +public class Neo4jNamespaceHandler extends NamespaceHandlerSupport { + + public void init() { + + RepositoryConfigurationExtension extension = new Neo4jRepositoryConfigurationExtension(); + RepositoryBeanDefinitionParser repositoryBeanDefinitionParser = new RepositoryBeanDefinitionParser(extension); + + registerBeanDefinitionParser("repositories", repositoryBeanDefinitionParser); + registerBeanDefinitionParser("config", new DataGraphBeanDefinitionParser()); + registerBeanDefinitionParser("auditing", new Neo4jAuditingBeanDefinitionParser()); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AfterSaveEvent.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AfterSaveEvent.java index 4e3762708..ecaf7de1f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AfterSaveEvent.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AfterSaveEvent.java @@ -15,24 +15,8 @@ */ package org.springframework.data.neo4j.lifecycle; -import org.springframework.context.ApplicationEvent; - -public class AfterSaveEvent extends ApplicationEvent { - private final T entity; - - /** - * Create a new ApplicationEvent. - * - * @param source the component that published the event (never null) - * @param entity - */ +public class AfterSaveEvent extends Neo4jLifecycleEvent { public AfterSaveEvent(Object source, T entity) { - super(source); - - this.entity = entity; - } - - public T getEntity() { - return entity; + super(source, entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AuditingEventListener.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AuditingEventListener.java new file mode 100644 index 000000000..4fc455cef --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/AuditingEventListener.java @@ -0,0 +1,53 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.lifecycle; + +import org.springframework.context.ApplicationListener; +import org.springframework.util.Assert; + +import org.springframework.data.auditing.AuditingHandler; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; + +/** + * Event listener to populate auditing related fields on an entity about to be saved. + * + * @author Oliver Gierke + */ +public class AuditingEventListener implements ApplicationListener> { + + private final IsNewAwareAuditingHandler auditingHandler; + + /** + * Creates a new {@link AuditingEventListener} using the given {@link org.springframework.data.mapping.context.MappingContext} and {@link AuditingHandler}. + * + * @param auditingHandler must not be {@literal null}. + */ + public AuditingEventListener(IsNewAwareAuditingHandler auditingHandler) { + + Assert.notNull(auditingHandler, "IsNewAwareAuditingHandler must not be null!"); + this.auditingHandler = auditingHandler; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + public void onApplicationEvent(BeforeSaveEvent event) { + + Object entity = event.getEntity(); + auditingHandler.markAudited(entity); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/BeforeSaveEvent.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/BeforeSaveEvent.java index 82580ba62..c87c076fd 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/BeforeSaveEvent.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/BeforeSaveEvent.java @@ -15,22 +15,8 @@ */ package org.springframework.data.neo4j.lifecycle; -import org.springframework.context.ApplicationEvent; - -public class BeforeSaveEvent extends ApplicationEvent { - private final T entity; - - /** - * @param source the component that published the event (never null) - * @param entity the entity that is about to be saved - */ +public class BeforeSaveEvent extends Neo4jLifecycleEvent { public BeforeSaveEvent(Object source, T entity) { - super(source); - - this.entity = entity; - } - - public T getEntity() { - return entity; + super(source, entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/DeleteEvent.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/DeleteEvent.java index 7b67ca021..b42087911 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/DeleteEvent.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/DeleteEvent.java @@ -15,18 +15,8 @@ */ package org.springframework.data.neo4j.lifecycle; -import org.springframework.context.ApplicationEvent; - -public class DeleteEvent extends ApplicationEvent { - private final T entity; - +public class DeleteEvent extends Neo4jLifecycleEvent { public DeleteEvent(Object source, T entity) { - super(source); - - this.entity = entity; - } - - public T getEntity() { - return entity; + super(source, entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/Neo4jLifecycleEvent.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/Neo4jLifecycleEvent.java new file mode 100644 index 000000000..d33b31f69 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/lifecycle/Neo4jLifecycleEvent.java @@ -0,0 +1,31 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.lifecycle; + +import org.springframework.context.ApplicationEvent; + +public class Neo4jLifecycleEvent extends ApplicationEvent { + private final T entity; + + public Neo4jLifecycleEvent(Object source,T entity) { + super(source); + this.entity = entity; + } + + public T getEntity() { + return entity; + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java index 125d60603..3bff3e0c8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jPersistentProperty.java @@ -66,7 +66,7 @@ public interface Neo4jPersistentProperty extends PersistentProperty getAnnotations(); - T getAnnotation(Class annotationType); + T getAnnotation(Class annotationType); boolean isAnnotationPresent(Class annotationType); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructureFactoryBean.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructureFactoryBean.java index aaa5f3c05..3f9a2ad73 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructureFactoryBean.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructureFactoryBean.java @@ -21,6 +21,8 @@ import org.neo4j.graphdb.Relationship; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; import org.springframework.data.neo4j.annotation.QueryType; import org.springframework.data.neo4j.conversion.ResultConverter; import org.springframework.data.neo4j.core.GraphDatabase; @@ -45,6 +47,7 @@ import org.springframework.data.neo4j.support.relationship.RelationshipEntityIns import org.springframework.data.neo4j.support.relationship.RelationshipEntityStateFactory; import org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategies; import org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory; +import org.springframework.data.support.IsNewStrategyFactory; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; @@ -76,7 +79,7 @@ public class MappingInfrastructureFactoryBean implements FactoryBean owner, SimpleTypeHolder simpleTypeHolder, Neo4jMappingContext ctx) { @@ -83,6 +85,7 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty T getAnnotation(Class annotationType) { - return (T) annotations.get(annotationType); + public T getAnnotation(Class annotationType) { + return findAnnotation(annotationType); } + @SuppressWarnings("unchecked") + public A findAnnotation(Class annotationType) { + return (A) annotations.get(annotationType); + } + + private RelationshipInfo extractRelationshipInfo(final Field field, Neo4jMappingContext ctx) { if (isAnnotationPresent(RelatedTo.class)) { return RelationshipInfo.fromField(field, getAnnotation(RelatedTo.class), getTypeInformation(), ctx); @@ -166,6 +174,11 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty createAssociation() { return new Association(this, null); diff --git a/spring-data-neo4j/src/main/resources/META-INF/spring.handlers b/spring-data-neo4j/src/main/resources/META-INF/spring.handlers index 2d4ecbcd0..2c4d2c6ca 100644 --- a/spring-data-neo4j/src/main/resources/META-INF/spring.handlers +++ b/spring-data-neo4j/src/main/resources/META-INF/spring.handlers @@ -1 +1 @@ -http\://www.springframework.org/schema/data/neo4j=org.springframework.data.neo4j.config.DataGraphNamespaceHandler +http\://www.springframework.org/schema/data/neo4j=org.springframework.data.neo4j.config.Neo4jNamespaceHandler diff --git a/spring-data-neo4j/src/main/resources/META-INF/spring.schemas b/spring-data-neo4j/src/main/resources/META-INF/spring.schemas index a3d25e3ba..cc2f5ca60 100644 --- a/spring-data-neo4j/src/main/resources/META-INF/spring.schemas +++ b/spring-data-neo4j/src/main/resources/META-INF/spring.schemas @@ -1,3 +1,4 @@ http\://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd=org/springframework/data/neo4j/config/spring-neo4j-2.0.xsd http\://www.springframework.org/schema/data/neo4j/spring-neo4j-2.1.xsd=org/springframework/data/neo4j/config/spring-neo4j-2.1.xsd -http\://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd=org/springframework/data/neo4j/config/spring-neo4j-2.1.xsd \ No newline at end of file +http\://www.springframework.org/schema/data/neo4j/spring-neo4j-2.2.xsd=org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd +http\://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd=org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd \ No newline at end of file diff --git a/spring-data-neo4j/src/main/resources/org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd b/spring-data-neo4j/src/main/resources/org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd new file mode 100644 index 000000000..774f8c5a7 --- /dev/null +++ b/spring-data-neo4j/src/main/resources/org/springframework/data/neo4j/config/spring-neo4j-2.2.xsd @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + default store-directory of the Neo4j database + + + + + + + + + base package for persistent entities. + + + + + + + entity manager factory bean reference for cross-store configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/AuditingIntegrationTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/AuditingIntegrationTests.java new file mode 100644 index 000000000..aeded5ea2 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/AuditingIntegrationTests.java @@ -0,0 +1,78 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.config; + +import org.joda.time.DateTime; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.neo4j.annotation.GraphId; +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.lifecycle.BeforeSaveEvent; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +public class AuditingIntegrationTests { + + @Test + public void enablesAuditingAndSetsPropertiesAccordingly() throws InterruptedException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("auditing.xml", getClass()); + runTest(context); + context.close(); + } + + @Test + public void enablesAuditingWithBeanConfigAndSetsPropertiesAccordingly() throws InterruptedException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("auditing-bean.xml", getClass()); + runTest(context); + context.close(); + } + + private void runTest(ApplicationContext context) throws InterruptedException { + Entity entity = new Entity(); + BeforeSaveEvent event = new BeforeSaveEvent(this,entity); + context.publishEvent(event); + + assertThat(entity.created, is(notNullValue())); + assertThat(entity.modified, is(entity.created)); + Thread.sleep(10); + entity.id = 1L; + event = new BeforeSaveEvent(this,entity); + context.publishEvent(event); + + assertThat(entity.created, is(notNullValue())); + assertThat(entity.modified, is(not(entity.created))); + } + + @NodeEntity + class Entity { + + @CreatedDate + DateTime created; + + @LastModifiedDate + DateTime modified; + + @GraphId + Long id; + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/lifecycle/AuditingEventListenerUnitTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/lifecycle/AuditingEventListenerUnitTests.java new file mode 100644 index 000000000..c53855bad --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/lifecycle/AuditingEventListenerUnitTests.java @@ -0,0 +1,92 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.lifecycle; + + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; +import org.springframework.data.neo4j.annotation.GraphId; +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext; +import org.springframework.data.support.IsNewStrategyFactory; + +/** + * Unit tests for {@link AuditingEventListener}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class AuditingEventListenerUnitTests { + + IsNewAwareAuditingHandler handler; + + IsNewStrategyFactory factory; + AuditingEventListener listener; + + @Before + public void setUp() { + + Neo4jMappingContext mappingContext = new Neo4jMappingContext(); + factory = new MappingContextIsNewStrategyFactory(mappingContext); + + handler = spy(new IsNewAwareAuditingHandler(factory)); + doNothing().when(handler).markCreated(Mockito.any(Object.class)); + doNothing().when(handler).markModified(Mockito.any(Object.class)); + + listener = new AuditingEventListener(handler); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullAuditingHandler() { + new AuditingEventListener(null); + } + + @Test + public void triggersCreationMarkForObjectWithEmptyId() { + + Sample sample = new Sample(); + listener.onApplicationEvent(new BeforeSaveEvent(this,sample)); + + verify(handler, times(1)).markCreated(sample); + verify(handler, times(0)).markModified(any(Sample.class)); + } + + @Test + public void triggersModificationMarkForObjectWithSetId() { + + Sample sample = new Sample(); + sample.id = "id"; + listener.onApplicationEvent(new BeforeSaveEvent(this,sample)); + + verify(handler, times(0)).markCreated(any(Sample.class)); + verify(handler, times(1)).markModified(sample); + } + + @NodeEntity + static class Sample { + + @GraphId + String id; + } +} diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing-bean.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing-bean.xml new file mode 100644 index 000000000..3183d58e5 --- /dev/null +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing-bean.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing.xml new file mode 100644 index 000000000..eea79251e --- /dev/null +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/config/auditing.xml @@ -0,0 +1,14 @@ + + + + + + + + + +