From f451050a15f81757ef83a468b45dd4a38c1fc46e Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Mon, 10 Feb 2014 18:02:47 -0600 Subject: [PATCH 1/8] DATACASS-91 - ready to test multiple base package entity scanning --- .../config/xml/DefaultBeanNames.java | 6 +- ...NamespaceCreatingXmlConfigTest-context.xml | 91 +++++++-------- .../config/xml/XmlConfigTest-context.xml | 15 ++- ...ataCassandraMappingContextFactoryBean.java | 46 ++++++++ ...sandraDataMappingConverterFactoryBean.java | 49 ++++++++ .../config/CassandraEntityClassScanner.java | 110 ++++++++++++++++++ .../config/DefaultDataBeanNames.java | 5 +- ...tractSpringDataCassandraConfiguration.java | 55 +++------ ...ndraDataCassandraMappingContextParser.java | 52 +++++++++ ...raDataMappingCassandraConverterParser.java | 62 ++++++++++ .../xml/CassandraDataNamespaceHandler.java | 2 + .../xml/CassandraDataSessionParser.java | 9 ++ .../DefaultCassandraMappingContext.java | 16 +++ ...andraRepositoryConfigurationExtension.java | 3 +- ...andra-1.0-trying-to-use-xml-extension.xsd} | 0 .../cassandra/config/spring-cassandra-1.0.xsd | 68 ++++++++++- ...tRepositoryJavaConfigIntegrationTests.java | 4 +- ...rRepositoryJavaConfigIntegrationTests.java | 4 +- .../template/CassandraDataOperationsTest.java | 13 +-- ...itoryXmlConfigIntegrationTests-context.xml | 11 +- .../CassandraNamespaceTests-context.xml | 25 ++-- ...itoryXmlConfigIntegrationTests-context.xml | 12 +- .../resources/spring-data-cassandra-basic.xml | 11 +- 23 files changed, 509 insertions(+), 160 deletions(-) create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java rename spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/{ext-spring-cassandra-1.0.xsd => spring-cassandra-1.0-trying-to-use-xml-extension.xsd} (100%) diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java index c50dc5316..89b34678b 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java @@ -22,7 +22,7 @@ package org.springframework.cassandra.config.xml; */ public interface DefaultBeanNames { - public static final String CLUSTER = "cassandra-cluster"; - public static final String SESSION = "cassandra-session"; - public static final String TEMPLATE = "cql-template"; + public static final String CLUSTER = "cassandraCluster"; + public static final String SESSION = "cassandraSession"; + public static final String TEMPLATE = "cqlTemplate"; } diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml index 906a2a32f..8259eb66c 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml @@ -10,69 +10,66 @@ location="classpath:/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties" /> - - + + - + - - - + + + - - - - - + - + + + + - + - + - - - + max-simultaneous-requests="${remote.max.requests}" core-connections="${remote.core.connections}" + max-connections="${remote.max.connections}" /> + + + - - + + - - + - + diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml index 7f63c803a..c779fde4b 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml @@ -9,8 +9,8 @@ - + @@ -18,16 +18,15 @@ min-simultaneous-requests="25" max-simultaneous-requests="100" core-connections="1" max-connections="2" /> + connect-timeout-millis="5000" keep-alive="true" read-timeout-millis="60000" + reuse-address="true" so-linger="60" tcp-no-delay="true" + receive-buffer-size="65536" send-buffer-size="65536" /> - + - + diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java new file mode 100644 index 000000000..3a087a06c --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java @@ -0,0 +1,46 @@ +package org.springframework.data.cassandra.config; + +import java.util.HashSet; +import java.util.Set; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; + +public class CassandraDataCassandraMappingContextFactoryBean implements FactoryBean, + InitializingBean { + + protected Set basePackages = new HashSet(); + + @Override + public void afterPropertiesSet() throws Exception { + } + + @Override + public CassandraMappingContext getObject() throws Exception { + + DefaultCassandraMappingContext cmc = new DefaultCassandraMappingContext(); + cmc.setInitialEntitySet(new CassandraEntityClassScanner(basePackages).scanForEntityClasses()); + + return cmc; + } + + @Override + public Class getObjectType() { + return CassandraMappingContext.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public Set getBasePackages() { + return basePackages; + } + + public void setBasePackages(Set basePackages) { + this.basePackages = basePackages; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java new file mode 100644 index 000000000..48fba014a --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java @@ -0,0 +1,49 @@ +package org.springframework.data.cassandra.config; + +import java.util.HashSet; +import java.util.Set; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.cassandra.convert.CassandraConverter; +import org.springframework.data.cassandra.convert.MappingCassandraConverter; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.util.Assert; + +public class CassandraDataMappingConverterFactoryBean implements FactoryBean, InitializingBean { + + protected CassandraMappingContext mappingContext; + protected Set basePackages = new HashSet(); + + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(mappingContext); + Assert.notNull(basePackages); + } + + @Override + public CassandraConverter getObject() throws Exception { + return new MappingCassandraConverter(mappingContext); + } + + @Override + public Class getObjectType() { + return CassandraConverter.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public CassandraMappingContext getMappingContext() { + return mappingContext; + } + + public void setMappingContext(CassandraMappingContext mappingContext) { + + Assert.notNull(mappingContext); + + this.mappingContext = mappingContext; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java new file mode 100644 index 000000000..aeef0a590 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java @@ -0,0 +1,110 @@ +package org.springframework.data.cassandra.config; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.data.cassandra.mapping.PrimaryKeyClass; +import org.springframework.data.cassandra.mapping.Table; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * Scans packages for Cassandra entities. + * + * @author Matthew T. Adams + */ +public class CassandraEntityClassScanner { + + protected Set entityBasePackages = new HashSet(); + protected Set> entityBasePackageClasses = new HashSet>(); + protected ClassLoader beanClassLoader; + + public CassandraEntityClassScanner() { + } + + public CassandraEntityClassScanner(Collection entityBasePackages) { + this(entityBasePackages, null); + } + + public CassandraEntityClassScanner(Collection entityBasePackages, + Collection> entityBasePackageClasses) { + + setEntityBasePackages(entityBasePackages); + setEntityBasePackageClasses(entityBasePackageClasses); + } + + public Set getEntityBasePackages() { + return Collections.unmodifiableSet(entityBasePackages); + } + + public void setEntityBasePackages(Collection entityBasePackages) { + this.entityBasePackages = entityBasePackages == null ? new HashSet() : new HashSet( + entityBasePackages); + } + + public Set> getEntityBasePackageClasses() { + return Collections.unmodifiableSet(entityBasePackageClasses); + } + + public void setEntityBasePackageClasses(Collection> entityBasePackageClasses) { + this.entityBasePackageClasses = entityBasePackageClasses == null ? new HashSet>() : new HashSet>( + entityBasePackageClasses); + } + + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } + + /** + * Scans the mapping base package for entity classes annotated with {@link Table} or {@link Persistent}. + * + * @see #getEntityBasePackages() + * @return Set<Class<?>> representing the annotated entity classes found. + * @throws ClassNotFoundException + */ + public Set> scanForEntityClasses() throws ClassNotFoundException { + + Set> classes = new HashSet>(); + + for (String basePackage : getEntityBasePackages()) { + classes.addAll(scanBasePackageForEntities(basePackage)); + } + + for (Class basePackageClass : getEntityBasePackageClasses()) { + classes.addAll(scanBasePackageForEntities(basePackageClass.getPackage().getName())); + } + + return classes; + } + + protected Set> scanBasePackageForEntities(String basePackage) throws ClassNotFoundException { + + HashSet> classes = new HashSet>(); + + if (StringUtils.hasText(basePackage)) { + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( + false); + for (Class annoClass : getEntityAnnotations()) { + componentProvider.addIncludeFilter(new AnnotationTypeFilter(annoClass)); + } + + for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + classes.add(ClassUtils.forName(candidate.getBeanClassName(), beanClassLoader)); + } + } + + return classes; + } + + @SuppressWarnings("unchecked") + public Class[] getEntityAnnotations() { + return new Class[] { Table.class, Persistent.class, PrimaryKeyClass.class }; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java index 43e4ded92..4443e54d6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java @@ -4,6 +4,7 @@ import org.springframework.cassandra.config.xml.DefaultBeanNames; public interface DefaultDataBeanNames extends DefaultBeanNames { - public static final String DATA_TEMPLATE = "cassandra-template"; - public static final String CONVERTER = "cassandra-converter"; + public static final String DATA_TEMPLATE = "cassandraTemplate"; + public static final String CONVERTER = "cassandraConverter"; + public static final String MAPPING_CONTEXT = "cassandra-mapping"; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index 736aa0922..a86e13b5c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -15,18 +15,15 @@ */ package org.springframework.data.cassandra.config.java; -import java.util.HashSet; +import java.util.Arrays; import java.util.Set; import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.cassandra.config.java.AbstractClusterConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.data.annotation.Persistent; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; +import org.springframework.data.cassandra.config.CassandraEntityClassScanner; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.data.cassandra.convert.MappingCassandraConverter; @@ -35,11 +32,8 @@ import org.springframework.data.cassandra.core.CassandraAdminTemplate; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; import org.springframework.data.cassandra.mapping.Mapping; -import org.springframework.data.cassandra.mapping.PrimaryKeyClass; import org.springframework.data.cassandra.mapping.Table; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Base class for Spring Data Cassandra configuration using JavaConfig. @@ -64,11 +58,11 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC } /** - * The base package to scan for entities annotated with {@link Table} annotations. By default, returns the package - * name of {@literal this} (this.getClass().getPackage().getName()). + * The base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package + * name of {@literal this} (this.getClass().getPackage().getName()). This method must never return null. */ - public String getEntityBasePackage() { - return getClass().getPackage().getName(); + public String[] getMappingBasePackages() { + return new String[] { getClass().getPackage().getName() }; } @Bean @@ -118,46 +112,27 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC */ @Bean public CassandraConverter converter() throws ClassNotFoundException { - MappingCassandraConverter converter = new MappingCassandraConverter(cassandraMappingContext()); - converter.setBeanClassLoader(beanClassLoader); - return converter; + return new MappingCassandraConverter(cassandraMappingContext()); } /** - * Scans the mapping base package for entity classes annotated with {@link Table} or {@link Persistent}. + * Scans the mapping base package for entity classes. * - * @see #getEntityBasePackage() + * @see #getMappingBasePackages() + * @see #getEntityScanner() * @return Set<Class<?>> representing the annotated entity classes found. * @throws ClassNotFoundException */ protected Set> getInitialEntitySet() throws ClassNotFoundException { - String basePackage = getEntityBasePackage(); - Set> initialEntitySet = new HashSet>(); + CassandraEntityClassScanner entityScanner = getEntityScanner(); + entityScanner.setEntityBasePackages(Arrays.asList(getMappingBasePackages())); - if (StringUtils.hasText(basePackage)) { - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( - false); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Table.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(PrimaryKeyClass.class)); - - for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { - - Class clazz = ClassUtils.forName(candidate.getBeanClassName(), beanClassLoader); - initialEntitySet.add(clazz); - } - } - - processMappingOverrides(initialEntitySet); - - return initialEntitySet; + return entityScanner.scanForEntityClasses(); } - protected void processMappingOverrides(Set> entityTypes) { - - // TODO: search for external entity mapping info (xml/properties/yaml/etc) here & update this.mapping - // similar to JPA's or JDO's external metadata search algorithms + public CassandraEntityClassScanner getEntityScanner() { + return new CassandraEntityClassScanner(); } @Override diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java new file mode 100644 index 000000000..fe696044c --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java @@ -0,0 +1,52 @@ +package org.springframework.data.cassandra.config.xml; + +import java.util.Set; + +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.cassandra.config.CassandraDataCassandraMappingContextFactoryBean; +import org.springframework.data.cassandra.config.DefaultDataBeanNames; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Spring Data Cassandra XML namespace parser for the <mapping-context> element. + * + * @author Matthew T. Adams + */ +public class CassandraDataCassandraMappingContextParser extends AbstractSimpleBeanDefinitionParser { + + @Override + protected Class getBeanClass(Element element) { + return CassandraDataCassandraMappingContextFactoryBean.class; + } + + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + + String id = super.resolveId(element, definition, parserContext); + return StringUtils.hasText(id) ? id : DefaultDataBeanNames.MAPPING_CONTEXT; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + super.doParse(element, parserContext, builder); + + parseBasePackagesAttribute(element, parserContext, builder); + } + + protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + String basePackages = element.getAttribute("base-packages"); + if (!StringUtils.hasText(basePackages)) { + return; + } + + Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); + builder.addPropertyValue("basePackages", basePackageSet); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java new file mode 100644 index 000000000..18eefc9c7 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java @@ -0,0 +1,62 @@ +package org.springframework.data.cassandra.config.xml; + +import java.util.Set; + +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.cassandra.config.CassandraDataMappingConverterFactoryBean; +import org.springframework.data.cassandra.config.DefaultDataBeanNames; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Spring Data Cassandra XML namespace parser for the <mapping-converter> element. + * + * @author Matthew T. Adams + */ +public class CassandraDataMappingCassandraConverterParser extends AbstractSimpleBeanDefinitionParser { + + @Override + protected Class getBeanClass(Element element) { + return CassandraDataMappingConverterFactoryBean.class; + } + + @Override + protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + + String id = super.resolveId(element, definition, parserContext); + return StringUtils.hasText(id) ? id : DefaultDataBeanNames.CONVERTER; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + super.doParse(element, parserContext, builder); + + parseMappingContextAttribute(element, parserContext, builder); + parseBasePackagesAttribute(element, parserContext, builder); + } + + protected void parseMappingContextAttribute(Element element, ParserContext parserContext, + BeanDefinitionBuilder builder) { + + String mappingContextRef = element.getAttribute("mapping-context-ref"); + if (!StringUtils.hasText(mappingContextRef)) { + mappingContextRef = DefaultDataBeanNames.MAPPING_CONTEXT; + } + builder.addPropertyReference("mappingContext", mappingContextRef); + } + + protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + String basePackages = element.getAttribute("base-packages"); + if (!StringUtils.hasText(basePackages)) { + return; + } + + Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java index fdd7fc80c..349b87ce3 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java @@ -36,5 +36,7 @@ public class CassandraDataNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("cluster", new CassandraDataClusterParser()); registerBeanDefinitionParser("session", new CassandraDataSessionParser()); registerBeanDefinitionParser("template", new CassandraDataTemplateParser()); + registerBeanDefinitionParser("mapping-converter", new CassandraDataMappingCassandraConverterParser()); + registerBeanDefinitionParser("mapping-context", new CassandraDataCassandraMappingContextParser()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java index ea2327801..fef2f1842 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java @@ -45,6 +45,15 @@ public class CassandraDataSessionParser extends CassandraSessionParser { } } + @Override + protected void setDefaultProperties(BeanDefinitionBuilder builder) { + + super.setDefaultProperties(builder); + + addRequiredPropertyReference(builder, "converter", DefaultDataBeanNames.CONVERTER); + addRequiredPropertyReference(builder, "schemaAction", SchemaAction.NONE.name()); + } + @Override protected void parseUnhandledElement(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index e48f7ec7d..36ac828e4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -52,6 +52,8 @@ public class DefaultCassandraMappingContext extends CassandraMappingContext, ApplicationContextAware { protected ApplicationContext context; + + // useful caches protected Map>> entitySetsByTableName = new HashMap>>(); protected Set> nonPrimaryKeyEntities = new HashSet>(); protected Set> primaryKeyEntities = new HashSet>(); @@ -63,6 +65,20 @@ public class DefaultCassandraMappingContext extends setSimpleTypeHolder(new CassandraSimpleTypeHolder()); } + @Override + public void initialize() { + + super.initialize(); + + processMappingOverrides(); + } + + protected void processMappingOverrides() { + + // TODO: search for external entity mapping info (xml/properties/yaml/etc) here & update this.mapping + // similar to JPA's or JDO's external metadata search algorithms + } + @Override public Collection> getPersistentEntities() { return getPersistentEntities(false); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java index 91b2fc35a..5821450f0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java @@ -18,6 +18,7 @@ package org.springframework.data.cassandra.repository.config; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.cassandra.config.xml.ParsingUtils; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.data.cassandra.config.DefaultDataBeanNames; import org.springframework.data.cassandra.repository.support.CassandraRepositoryFactoryBean; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtension; @@ -52,7 +53,7 @@ public class CassandraRepositoryConfigurationExtension extends RepositoryConfigu Element element = config.getElement(); ParsingUtils.addOptionalPropertyReference(builder, "cassandraTemplate", element, CASSANDRA_TEMPLATE_REF, - "cassandra-template"); + DefaultDataBeanNames.TEMPLATE); } @Override diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/ext-spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0-trying-to-use-xml-extension.xsd similarity index 100% rename from spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/ext-spring-cassandra-1.0.xsd rename to spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0-trying-to-use-xml-extension.xsd diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd index bed6fd896..14d048be2 100644 --- a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd @@ -482,7 +482,7 @@ The name of a Cassandra Keyspace. No default; for the system keyspace, use the use="optional"> @@ -500,7 +500,7 @@ The schema action to perform; default is NONE. @@ -515,7 +515,7 @@ The reference to a Cassandra session; default is "cassandra-session". use="optional"> @@ -676,11 +676,10 @@ Table name override. - + @@ -696,4 +695,61 @@ The reference to a cassandraTemplate. Will default to 'cassandra-template'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java index 4515b97af..99a396907 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java @@ -69,8 +69,8 @@ public class CommentRepositoryJavaConfigIntegrationTests extends AbstractSpringD } @Override - public String getEntityBasePackage() { - return Comment.class.getPackage().getName(); + public String[] getMappingBasePackages() { + return new String[] { Comment.class.getPackage().getName() }; } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java index 9ca1eac3e..1d6a180bf 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java @@ -68,8 +68,8 @@ public class UserRepositoryJavaConfigIntegrationTests extends AbstractSpringData } @Override - public String getEntityBasePackage() { - return User.class.getPackage().getName(); + public String[] getMappingBasePackages() { + return new String[] { User.class.getPackage().getName() }; } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java index 047545bc8..6406ba08a 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java @@ -16,7 +16,6 @@ package org.springframework.data.cassandra.test.integration.template; import static org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification.createKeyspace; -import static org.springframework.cassandra.core.keyspace.DropTableSpecification.dropTable; import java.io.IOException; import java.util.ArrayList; @@ -24,30 +23,22 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; -import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cassandra.core.ConsistencyLevel; import org.springframework.cassandra.core.QueryOptions; import org.springframework.cassandra.core.RetryPolicy; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; import org.springframework.data.cassandra.config.SchemaAction; -import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.test.integration.simpletons.Book; import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest; import org.springframework.data.cassandra.test.integration.support.TestConfig; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.datastax.driver.core.TableMetadata; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; @@ -75,8 +66,8 @@ public class CassandraDataOperationsTest extends AbstractSpringDataEmbeddedCassa } @Override - public String getEntityBasePackage() { - return Book.class.getPackage().getName(); + public String[] getMappingBasePackages() { + return new String[] { Book.class.getPackage().getName() }; } } diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml index a181a4745..325c8e0c7 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml @@ -2,24 +2,23 @@ - - + - + diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml index 5709113fc..a25253303 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml @@ -11,9 +11,8 @@ - + @@ -27,21 +26,13 @@ durable-writes="true" /> - - + + + - - - - + - - - - + diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml index a80188b22..973c3c938 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml @@ -1,7 +1,8 @@ - - + - + diff --git a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml index 7c6868246..39c99629f 100644 --- a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml +++ b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml @@ -10,15 +10,10 @@ - + - - - + - + From e3a131007c423b36fafe286da6bb933a8d917d9c Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Mon, 10 Feb 2014 23:14:30 -0600 Subject: [PATCH 2/8] DATACASS-91 - now scanning packages via XML & Java config --- ...ataCassandraMappingContextFactoryBean.java | 46 ------------ .../CassandraDataSessionFactoryBean.java | 56 -------------- .../CassandraMappingContextFactoryBean.java | 73 +++++++++++++++++++ ...CassandraMappingConverterFactoryBean.java} | 2 +- .../config/DefaultDataBeanNames.java | 2 +- ...tractSpringDataCassandraConfiguration.java | 36 ++++----- .../xml/CassandraDataNamespaceHandler.java | 4 +- .../xml/CassandraDataSessionParser.java | 66 +---------------- ...ava => CassandraMappingContextParser.java} | 56 +++++++++++++- ...a => CassandraMappingConverterParser.java} | 23 ++---- .../DefaultCassandraMappingContext.java | 70 ++++++++++++++++-- .../cassandra/config/spring-cassandra-1.0.xsd | 21 ++---- ...itoryXmlConfigIntegrationTests-context.xml | 30 ++++---- .../CassandraNamespaceTests-context.xml | 2 +- ...itoryXmlConfigIntegrationTests-context.xml | 30 ++++---- .../resources/spring-data-cassandra-basic.xml | 8 +- 16 files changed, 266 insertions(+), 259 deletions(-) delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/{CassandraDataMappingConverterFactoryBean.java => CassandraMappingConverterFactoryBean.java} (91%) rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/{CassandraDataCassandraMappingContextParser.java => CassandraMappingContextParser.java} (50%) rename spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/{CassandraDataMappingCassandraConverterParser.java => CassandraMappingConverterParser.java} (68%) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java deleted file mode 100644 index 3a087a06c..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataCassandraMappingContextFactoryBean.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.springframework.data.cassandra.config; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.data.cassandra.mapping.CassandraMappingContext; -import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; - -public class CassandraDataCassandraMappingContextFactoryBean implements FactoryBean, - InitializingBean { - - protected Set basePackages = new HashSet(); - - @Override - public void afterPropertiesSet() throws Exception { - } - - @Override - public CassandraMappingContext getObject() throws Exception { - - DefaultCassandraMappingContext cmc = new DefaultCassandraMappingContext(); - cmc.setInitialEntitySet(new CassandraEntityClassScanner(basePackages).scanForEntityClasses()); - - return cmc; - } - - @Override - public Class getObjectType() { - return CassandraMappingContext.class; - } - - @Override - public boolean isSingleton() { - return true; - } - - public Set getBasePackages() { - return basePackages; - } - - public void setBasePackages(Set basePackages) { - this.basePackages = basePackages; - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java index 436b66b68..69bc1be26 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java @@ -7,10 +7,7 @@ import org.springframework.data.cassandra.convert.CassandraConverter; import org.springframework.data.cassandra.core.CassandraAdminTemplate; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; -import org.springframework.data.cassandra.mapping.EntityMapping; -import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.Metadata; @@ -22,8 +19,6 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean protected CassandraAdminTemplate admin; protected CassandraConverter converter; protected CassandraMappingContext mappingContext; - protected Mapping mapping; - protected ClassLoader entityClassLoader = getClass().getClassLoader(); @Override public void afterPropertiesSet() throws Exception { @@ -34,42 +29,9 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean admin = new CassandraAdminTemplate(session, converter); - mapping = mapping == null ? new Mapping() : mapping; - - processMappingOverrides(); performSchemaAction(); } - protected void processMappingOverrides() throws ClassNotFoundException { - - if (mapping == null) { - return; - } - - for (EntityMapping entityMapping : mapping.getEntityMappings()) { - - if (entityMapping == null) { - continue; - } - - String entityClassName = entityMapping.getEntityClassName(); - Class entityClass = Class.forName(entityClassName, false, entityClassLoader); - - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); - - if (entity == null) { - throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName)); - } - - String tableName = entityMapping.getTableName(); - if (!StringUtils.hasText(tableName)) { - continue; - } - - entity.setTableName(tableName); - } - } - protected void performSchemaAction() { boolean dropTables = false; @@ -138,22 +100,4 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean this.converter = converter; this.mappingContext = converter.getMappingContext(); } - - public Mapping getMapping() { - return mapping; - } - - public void setMapping(Mapping mapping) { - Assert.notNull(mapping); - this.mapping = mapping; - } - - public ClassLoader getEntityClassLoader() { - return entityClassLoader; - } - - public void setEntityClassLoader(ClassLoader entityClassLoader) { - Assert.notNull(entityClassLoader); - this.entityClassLoader = entityClassLoader; - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java new file mode 100644 index 000000000..2489a7ad9 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java @@ -0,0 +1,73 @@ +package org.springframework.data.cassandra.config; + +import java.util.HashSet; +import java.util.Set; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.data.cassandra.mapping.Mapping; +import org.springframework.util.Assert; + +public class CassandraMappingContextFactoryBean implements FactoryBean, InitializingBean { + + protected Set basePackages = new HashSet(); + protected Mapping mapping; + protected ClassLoader entityClassLoader; + + @Override + public void afterPropertiesSet() throws Exception { + mapping = mapping == null ? new Mapping() : mapping; + } + + @Override + public CassandraMappingContext getObject() throws Exception { + + DefaultCassandraMappingContext mappingContext = new DefaultCassandraMappingContext(); + mappingContext.setInitialEntitySet(new CassandraEntityClassScanner(basePackages).scanForEntityClasses()); + mappingContext.setMapping(mapping); + mappingContext.setBeanClassLoader(entityClassLoader); + + mappingContext.initialize(); // this is necessary here + + return mappingContext; + } + + @Override + public Class getObjectType() { + return CassandraMappingContext.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public Set getBasePackages() { + return basePackages; + } + + public void setBasePackages(Set basePackages) { + this.basePackages = basePackages == null ? new HashSet() : new HashSet(basePackages); + } + + public Mapping getMapping() { + return mapping; + } + + public void setMapping(Mapping mapping) { + + Assert.notNull(mapping); + + this.mapping = mapping; + } + + public ClassLoader getEntityClassLoader() { + return entityClassLoader; + } + + public void setEntityClassLoader(ClassLoader entityClassLoader) { + this.entityClassLoader = entityClassLoader; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java similarity index 91% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java index 48fba014a..e6e3353f5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataMappingConverterFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java @@ -10,7 +10,7 @@ import org.springframework.data.cassandra.convert.MappingCassandraConverter; import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.util.Assert; -public class CassandraDataMappingConverterFactoryBean implements FactoryBean, InitializingBean { +public class CassandraMappingConverterFactoryBean implements FactoryBean, InitializingBean { protected CassandraMappingContext mappingContext; protected Set basePackages = new HashSet(); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java index 4443e54d6..c91114d88 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java @@ -6,5 +6,5 @@ public interface DefaultDataBeanNames extends DefaultBeanNames { public static final String DATA_TEMPLATE = "cassandraTemplate"; public static final String CONVERTER = "cassandraConverter"; - public static final String MAPPING_CONTEXT = "cassandra-mapping"; + public static final String MAPPING_CONTEXT = "cassandraMapping"; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index a86e13b5c..992da0ac6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.data.cassandra.config.java; import java.util.Arrays; +import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.BeanClassLoaderAware; @@ -24,14 +25,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; import org.springframework.data.cassandra.config.CassandraEntityClassScanner; +import org.springframework.data.cassandra.config.CassandraMappingContextFactoryBean; +import org.springframework.data.cassandra.config.CassandraMappingConverterFactoryBean; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.convert.CassandraConverter; -import org.springframework.data.cassandra.convert.MappingCassandraConverter; import org.springframework.data.cassandra.core.CassandraAdminOperations; import org.springframework.data.cassandra.core.CassandraAdminTemplate; -import org.springframework.data.cassandra.mapping.CassandraMappingContext; -import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; -import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.data.cassandra.mapping.Table; import org.springframework.data.mapping.context.MappingContext; @@ -48,7 +47,6 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC protected abstract String getKeyspaceName(); protected ClassLoader beanClassLoader; - protected Mapping mapping = new Mapping(); /** * The {@link SchemaAction} to perform. Defaults to {@link SchemaAction#NONE}. @@ -71,15 +69,12 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC CassandraDataSessionFactoryBean bean = new CassandraDataSessionFactoryBean(); bean.setCluster(cluster().getObject()); - bean.setConverter(converter()); + bean.setConverter(converter().getObject()); bean.setSchemaAction(getSchemaAction()); bean.setKeyspaceName(getKeyspaceName()); bean.setStartupScripts(getStartupScripts()); bean.setShutdownScripts(getShutdownScripts()); - bean.setEntityClassLoader(beanClassLoader); - bean.setMapping(mapping); - return bean; } @@ -90,7 +85,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC */ @Bean public CassandraAdminOperations cassandraTemplate() throws Exception { - return new CassandraAdminTemplate(session().getObject(), converter()); + return new CassandraAdminTemplate(session().getObject(), converter().getObject()); } /** @@ -99,20 +94,25 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC * @throws ClassNotFoundException */ @Bean - public CassandraMappingContext cassandraMappingContext() throws ClassNotFoundException { - DefaultCassandraMappingContext context = new DefaultCassandraMappingContext(); - context.setInitialEntitySet(getInitialEntitySet()); - return context; + public CassandraMappingContextFactoryBean cassandraMapping() throws ClassNotFoundException { + + CassandraMappingContextFactoryBean bean = new CassandraMappingContextFactoryBean(); + bean.setBasePackages(new HashSet(Arrays.asList(getMappingBasePackages()))); + bean.setEntityClassLoader(beanClassLoader); + + return bean; } /** * Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements - * - * @throws ClassNotFoundException */ @Bean - public CassandraConverter converter() throws ClassNotFoundException { - return new MappingCassandraConverter(cassandraMappingContext()); + public CassandraMappingConverterFactoryBean converter() throws Exception { + + CassandraMappingConverterFactoryBean bean = new CassandraMappingConverterFactoryBean(); + bean.setMappingContext(cassandraMapping().getObject()); + + return bean; } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java index 349b87ce3..8dc3ac7b2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java @@ -36,7 +36,7 @@ public class CassandraDataNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("cluster", new CassandraDataClusterParser()); registerBeanDefinitionParser("session", new CassandraDataSessionParser()); registerBeanDefinitionParser("template", new CassandraDataTemplateParser()); - registerBeanDefinitionParser("mapping-converter", new CassandraDataMappingCassandraConverterParser()); - registerBeanDefinitionParser("mapping-context", new CassandraDataCassandraMappingContextParser()); + registerBeanDefinitionParser("mapping-converter", new CassandraMappingConverterParser()); + registerBeanDefinitionParser("mapping", new CassandraMappingContextParser()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java index fef2f1842..e850bb07b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java @@ -1,20 +1,15 @@ package org.springframework.data.cassandra.config.xml; -import static org.springframework.cassandra.config.xml.ParsingUtils.*; - -import java.util.HashSet; -import java.util.Set; +import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyReference; +import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyValue; +import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredPropertyReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.xml.CassandraSessionParser; -import org.springframework.data.cassandra.config.DefaultDataBeanNames; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; +import org.springframework.data.cassandra.config.DefaultDataBeanNames; import org.springframework.data.cassandra.config.SchemaAction; -import org.springframework.data.cassandra.mapping.EntityMapping; -import org.springframework.data.cassandra.mapping.Mapping; -import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; import org.w3c.dom.Attr; import org.w3c.dom.Element; @@ -53,57 +48,4 @@ public class CassandraDataSessionParser extends CassandraSessionParser { addRequiredPropertyReference(builder, "converter", DefaultDataBeanNames.CONVERTER); addRequiredPropertyReference(builder, "schemaAction", SchemaAction.NONE.name()); } - - @Override - protected void parseUnhandledElement(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - if ("mapping".equals(element.getLocalName())) { - parseMapping(element, parserContext, builder); - } else { - super.parseUnhandledElement(element, parserContext, builder); - } - } - - protected void parseMapping(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - // TODO: parse attributes here, if there ever are any - - Set mappings = new HashSet(); - - for (Element child : DomUtils.getChildElementsByTagName(element, "entity")) { - - EntityMapping entityMapping = parseEntity(child); - - if (entityMapping != null) { - mappings.add(entityMapping); - } - } - - Mapping mapping = new Mapping(); - mapping.setEntityMappings(mappings); - - builder.addPropertyValue("mapping", mapping); - } - - protected EntityMapping parseEntity(Element entity) { - - String className = entity.getAttribute("class"); - if (!StringUtils.hasText(className)) { - throw new IllegalStateException("class attribute must not be empty"); - } - - Element table = DomUtils.getChildElementByTagName(entity, "table"); - if (table == null) { - return null; - } - - String tableName = table.getAttribute("name"); - if (!StringUtils.hasText(tableName)) { - tableName = null; - } - - // TODO: parse future entity mappings here, like table options - - return new EntityMapping(className, tableName); - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java similarity index 50% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java index fe696044c..ee3991c39 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataCassandraMappingContextParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java @@ -1,5 +1,6 @@ package org.springframework.data.cassandra.config.xml; +import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -7,21 +8,24 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.cassandra.config.CassandraDataCassandraMappingContextFactoryBean; +import org.springframework.data.cassandra.config.CassandraMappingContextFactoryBean; import org.springframework.data.cassandra.config.DefaultDataBeanNames; +import org.springframework.data.cassandra.mapping.EntityMapping; +import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; /** - * Spring Data Cassandra XML namespace parser for the <mapping-context> element. + * Spring Data Cassandra XML namespace parser for the <mapping> element. * * @author Matthew T. Adams */ -public class CassandraDataCassandraMappingContextParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraMappingContextParser extends AbstractSimpleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { - return CassandraDataCassandraMappingContextFactoryBean.class; + return CassandraMappingContextFactoryBean.class; } @Override @@ -37,6 +41,7 @@ public class CassandraDataCassandraMappingContextParser extends AbstractSimpleBe super.doParse(element, parserContext, builder); parseBasePackagesAttribute(element, parserContext, builder); + parseMapping(element, parserContext, builder); } protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { @@ -49,4 +54,47 @@ public class CassandraDataCassandraMappingContextParser extends AbstractSimpleBe Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); builder.addPropertyValue("basePackages", basePackageSet); } + + protected void parseMapping(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + // TODO: parse **attributes** here, if there ever are any + + Set mappings = new HashSet(); + + for (Element child : DomUtils.getChildElementsByTagName(element, "entity")) { + + EntityMapping entityMapping = parseEntity(child); + + if (entityMapping != null) { + mappings.add(entityMapping); + } + } + + Mapping mapping = new Mapping(); + mapping.setEntityMappings(mappings); + + builder.addPropertyValue("mapping", mapping); + } + + protected EntityMapping parseEntity(Element entity) { + + String className = entity.getAttribute("class"); + if (!StringUtils.hasText(className)) { + throw new IllegalStateException("class attribute must not be empty"); + } + + Element table = DomUtils.getChildElementByTagName(entity, "table"); + if (table == null) { + return null; + } + + String tableName = table.getAttribute("name"); + if (!StringUtils.hasText(tableName)) { + tableName = null; + } + + // TODO: parse future entity mappings here, like table options + + return new EntityMapping(className, tableName); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java similarity index 68% rename from spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java rename to spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java index 18eefc9c7..6a830f119 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataMappingCassandraConverterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java @@ -1,13 +1,11 @@ package org.springframework.data.cassandra.config.xml; -import java.util.Set; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.cassandra.config.CassandraDataMappingConverterFactoryBean; +import org.springframework.data.cassandra.config.CassandraMappingConverterFactoryBean; import org.springframework.data.cassandra.config.DefaultDataBeanNames; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -17,11 +15,11 @@ import org.w3c.dom.Element; * * @author Matthew T. Adams */ -public class CassandraDataMappingCassandraConverterParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraMappingConverterParser extends AbstractSimpleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { - return CassandraDataMappingConverterFactoryBean.class; + return CassandraMappingConverterFactoryBean.class; } @Override @@ -34,29 +32,20 @@ public class CassandraDataMappingCassandraConverterParser extends AbstractSimple @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + super.doParse(element, parserContext, builder); parseMappingContextAttribute(element, parserContext, builder); - parseBasePackagesAttribute(element, parserContext, builder); } protected void parseMappingContextAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - String mappingContextRef = element.getAttribute("mapping-context-ref"); + String mappingContextRef = element.getAttribute("mapping-ref"); if (!StringUtils.hasText(mappingContextRef)) { mappingContextRef = DefaultDataBeanNames.MAPPING_CONTEXT; } + builder.addPropertyReference("mappingContext", mappingContextRef); } - - protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - String basePackages = element.getAttribute("base-packages"); - if (!StringUtils.hasText(basePackages)) { - return; - } - - Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index 36ac828e4..02417f1fd 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -37,6 +37,7 @@ import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import com.datastax.driver.core.TableMetadata; @@ -52,6 +53,9 @@ public class DefaultCassandraMappingContext extends CassandraMappingContext, ApplicationContextAware { protected ApplicationContext context; + protected Mapping mapping = new Mapping(); + protected ClassLoader beanClassLoader; + protected boolean initialized; // useful caches protected Map>> entitySetsByTableName = new HashMap>>(); @@ -65,18 +69,27 @@ public class DefaultCassandraMappingContext extends setSimpleTypeHolder(new CassandraSimpleTypeHolder()); } + @Override + public void afterPropertiesSet() { + if (initialized) { + return; + } + + super.afterPropertiesSet(); + } + @Override public void initialize() { + if (initialized) { + return; + } + super.initialize(); processMappingOverrides(); - } - protected void processMappingOverrides() { - - // TODO: search for external entity mapping info (xml/properties/yaml/etc) here & update this.mapping - // similar to JPA's or JDO's external metadata search algorithms + initialized = true; } @Override @@ -224,4 +237,51 @@ public class DefaultCassandraMappingContext extends return entity; } + + public void setMapping(Mapping mapping) { + + Assert.notNull(mapping); + + this.mapping = mapping; + } + + protected void processMappingOverrides() { + + if (mapping == null) { + return; + } + + for (EntityMapping entityMapping : mapping.getEntityMappings()) { + + if (entityMapping == null) { + continue; + } + + String entityClassName = entityMapping.getEntityClassName(); + Class entityClass; + try { + entityClass = Class.forName(entityClassName, false, beanClassLoader == null ? getClass().getClassLoader() + : beanClassLoader); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(String.format("unknown persistent entity name [%s]", entityClassName), e); + } + + CassandraPersistentEntity entity = getPersistentEntity(entityClass); + + if (entity == null) { + throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName)); + } + + String tableName = entityMapping.getTableName(); + if (!StringUtils.hasText(tableName)) { + continue; + } + + entity.setTableName(tableName); + } + } + + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } } diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd index 14d048be2..1f784ee6c 100644 --- a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd @@ -452,8 +452,6 @@ Arbitrary CQL script to be executed against the session's keyspace during bean d ]]> - @@ -629,13 +627,6 @@ The replication factor for the data center. - - - - - - - + + + + @@ -729,12 +724,12 @@ Defines a CassandraConverter for getting rich mapping functionality. - diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml index 325c8e0c7..8f3c5e1ca 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml @@ -1,6 +1,6 @@ - - + + + + + + + - - + + - - - - - - - + - diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml index a25253303..5b0e5d619 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml @@ -27,7 +27,7 @@ - + - - + + + + + + + - - + + - - - - - - - + - diff --git a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml index 39c99629f..15653c53d 100644 --- a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml +++ b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml @@ -1,6 +1,6 @@ - + - - - + From e71c446a3f0408b77a5ca9bf624611e2c5ef846b Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Mon, 10 Feb 2014 23:17:48 -0600 Subject: [PATCH 3/8] DATACASS-91 - renamed Java config @Bean converter() to cassandraConverter() in order to match DefaultDataBeanNames --- .../java/AbstractSpringDataCassandraConfiguration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index 992da0ac6..5d1e35dc8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -69,7 +69,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC CassandraDataSessionFactoryBean bean = new CassandraDataSessionFactoryBean(); bean.setCluster(cluster().getObject()); - bean.setConverter(converter().getObject()); + bean.setConverter(cassandraConverter().getObject()); bean.setSchemaAction(getSchemaAction()); bean.setKeyspaceName(getKeyspaceName()); bean.setStartupScripts(getStartupScripts()); @@ -85,7 +85,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC */ @Bean public CassandraAdminOperations cassandraTemplate() throws Exception { - return new CassandraAdminTemplate(session().getObject(), converter().getObject()); + return new CassandraAdminTemplate(session().getObject(), cassandraConverter().getObject()); } /** @@ -107,7 +107,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC * Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements */ @Bean - public CassandraMappingConverterFactoryBean converter() throws Exception { + public CassandraMappingConverterFactoryBean cassandraConverter() throws Exception { CassandraMappingConverterFactoryBean bean = new CassandraMappingConverterFactoryBean(); bean.setMappingContext(cassandraMapping().getObject()); From 7b8cd2ab60dea2dc67beceb60628330a5d80c7bd Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Tue, 11 Feb 2014 09:17:04 -0600 Subject: [PATCH 4/8] DATACASS-91 - aligning terminology --- .../CassandraMappingContextFactoryBean.java | 24 +++++++++---------- ...tractSpringDataCassandraConfiguration.java | 10 ++++---- .../xml/CassandraMappingContextParser.java | 4 ++-- .../DefaultCassandraMappingContext.java | 4 ++-- .../cassandra/config/spring-cassandra-1.0.xsd | 2 +- ...tRepositoryJavaConfigIntegrationTests.java | 2 +- ...rRepositoryJavaConfigIntegrationTests.java | 2 +- .../template/CassandraDataOperationsTest.java | 2 +- ...itoryXmlConfigIntegrationTests-context.xml | 2 +- ...itoryXmlConfigIntegrationTests-context.xml | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java index 2489a7ad9..5fc72ddf0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java @@ -12,9 +12,9 @@ import org.springframework.util.Assert; public class CassandraMappingContextFactoryBean implements FactoryBean, InitializingBean { - protected Set basePackages = new HashSet(); + protected Set entityBasePackages = new HashSet(); protected Mapping mapping; - protected ClassLoader entityClassLoader; + protected ClassLoader beanClassLoader; @Override public void afterPropertiesSet() throws Exception { @@ -25,9 +25,9 @@ public class CassandraMappingContextFactoryBean implements FactoryBean getBasePackages() { - return basePackages; + public Set getEntityBasePackages() { + return entityBasePackages; } - public void setBasePackages(Set basePackages) { - this.basePackages = basePackages == null ? new HashSet() : new HashSet(basePackages); + public void setEntityBasePackages(Set basePackages) { + this.entityBasePackages = basePackages == null ? new HashSet() : new HashSet(basePackages); } public Mapping getMapping() { @@ -63,11 +63,11 @@ public class CassandraMappingContextFactoryBean implements FactoryBeanthis.getClass().getPackage().getName()). This method must never return null. */ - public String[] getMappingBasePackages() { + public String[] getEntityBasePackages() { return new String[] { getClass().getPackage().getName() }; } @@ -97,8 +97,8 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC public CassandraMappingContextFactoryBean cassandraMapping() throws ClassNotFoundException { CassandraMappingContextFactoryBean bean = new CassandraMappingContextFactoryBean(); - bean.setBasePackages(new HashSet(Arrays.asList(getMappingBasePackages()))); - bean.setEntityClassLoader(beanClassLoader); + bean.setEntityBasePackages(new HashSet(Arrays.asList(getEntityBasePackages()))); + bean.setBeanClassLoader(beanClassLoader); return bean; } @@ -118,7 +118,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC /** * Scans the mapping base package for entity classes. * - * @see #getMappingBasePackages() + * @see #getEntityBasePackages() * @see #getEntityScanner() * @return Set<Class<?>> representing the annotated entity classes found. * @throws ClassNotFoundException @@ -126,7 +126,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC protected Set> getInitialEntitySet() throws ClassNotFoundException { CassandraEntityClassScanner entityScanner = getEntityScanner(); - entityScanner.setEntityBasePackages(Arrays.asList(getMappingBasePackages())); + entityScanner.setEntityBasePackages(Arrays.asList(getEntityBasePackages())); return entityScanner.scanForEntityClasses(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java index ee3991c39..a7e0cf95e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java @@ -46,13 +46,13 @@ public class CassandraMappingContextParser extends AbstractSimpleBeanDefinitionP protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - String basePackages = element.getAttribute("base-packages"); + String basePackages = element.getAttribute("entity-base-packages"); if (!StringUtils.hasText(basePackages)) { return; } Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); - builder.addPropertyValue("basePackages", basePackageSet); + builder.addPropertyValue("entityBasePackages", basePackageSet); } protected void parseMapping(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index 02417f1fd..b3bb25d5d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -37,6 +37,7 @@ import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import com.datastax.driver.core.TableMetadata; @@ -260,8 +261,7 @@ public class DefaultCassandraMappingContext extends String entityClassName = entityMapping.getEntityClassName(); Class entityClass; try { - entityClass = Class.forName(entityClassName, false, beanClassLoader == null ? getClass().getClassLoader() - : beanClassLoader); + entityClass = ClassUtils.forName(entityClassName, beanClassLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException(String.format("unknown persistent entity name [%s]", entityClassName), e); } diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd index 1f784ee6c..40e8a3b54 100644 --- a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd @@ -702,7 +702,7 @@ Defines a CassandraMappingContext for holding rich entity mapping information. - + entity-base-packages="org.springframework.data.cassandra.test.integration.composites"> diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml index 656c0a620..418d3d343 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml @@ -13,7 +13,7 @@ + entity-base-packages="org.springframework.data.cassandra.test.integration.repository"> From 0f121597f21c09db4ba78192dbfae7456be2ae79 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Tue, 11 Feb 2014 09:33:25 -0600 Subject: [PATCH 5/8] DATACASS-91 - renamed mapping-converter XML element to converter --- .../cassandra/config/xml/CassandraDataNamespaceHandler.java | 2 +- .../cassandra/config/xml/CassandraMappingConverterParser.java | 2 +- .../data/cassandra/config/spring-cassandra-1.0.xsd | 2 +- .../test/integration/config/CassandraNamespaceTests-context.xml | 2 +- .../src/test/resources/spring-data-cassandra-basic.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java index 8dc3ac7b2..d432cd458 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java @@ -36,7 +36,7 @@ public class CassandraDataNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("cluster", new CassandraDataClusterParser()); registerBeanDefinitionParser("session", new CassandraDataSessionParser()); registerBeanDefinitionParser("template", new CassandraDataTemplateParser()); - registerBeanDefinitionParser("mapping-converter", new CassandraMappingConverterParser()); + registerBeanDefinitionParser("converter", new CassandraMappingConverterParser()); registerBeanDefinitionParser("mapping", new CassandraMappingContextParser()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java index 6a830f119..2ee61c67b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java @@ -11,7 +11,7 @@ import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** - * Spring Data Cassandra XML namespace parser for the <mapping-converter> element. + * Spring Data Cassandra XML namespace parser for the <converter> element. * * @author Matthew T. Adams */ diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd index 40e8a3b54..ee2d195ee 100644 --- a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd @@ -713,7 +713,7 @@ The comma-delimited base packages in which to scan for entities and their mappin - + - + diff --git a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml index 15653c53d..3d736d478 100644 --- a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml +++ b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml @@ -10,7 +10,7 @@ - + From 24be302c039da5b0c471b44e3d40ba1dd8db7f8e Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Tue, 11 Feb 2014 11:05:38 -0600 Subject: [PATCH 6/8] DATACASS-91 - improved XML parsing & Java config --- .../config/xml/CassandraSessionParser.java | 4 +- .../config/xml/CassandraTemplateParser.java | 6 +- .../config/CassandraEntityClassScanner.java | 26 +++++++ .../CassandraMappingContextFactoryBean.java | 73 ------------------- .../CassandraMappingConverterFactoryBean.java | 49 ------------- ...tractSpringDataCassandraConfiguration.java | 47 +++--------- .../xml/CassandraMappingContextParser.java | 34 +++------ .../xml/CassandraMappingConverterParser.java | 26 +++---- .../DefaultCassandraMappingContext.java | 16 ---- .../resources/spring-data-cassandra-basic.xml | 2 +- 10 files changed, 60 insertions(+), 223 deletions(-) delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java delete mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java index 344308e42..184fdc0d3 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java @@ -22,7 +22,7 @@ import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredP import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.CassandraSessionFactoryBean; import org.springframework.util.StringUtils; @@ -37,7 +37,7 @@ import org.w3c.dom.NamedNodeMap; * @author David Webb * @author Matthew T. Adams */ -public class CassandraSessionParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraSessionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java index 871bd0bbe..03f370e7a 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java @@ -15,12 +15,12 @@ */ package org.springframework.cassandra.config.xml; -import static org.springframework.cassandra.config.xml.ParsingUtils.*; +import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyReference; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.CassandraTemplateFactoryBean; import org.springframework.util.StringUtils; @@ -32,7 +32,7 @@ import org.w3c.dom.Element; * @author David Webb * @author Matthew T. Adams */ -public class CassandraTemplateParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraTemplateParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java index aeef0a590..a32c21055 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java @@ -1,6 +1,7 @@ package org.springframework.data.cassandra.config; import java.lang.annotation.Annotation; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -22,6 +23,23 @@ import org.springframework.util.StringUtils; */ public class CassandraEntityClassScanner { + public static Set> scan(String... entityBasePackages) throws ClassNotFoundException { + return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses(); + } + + public static Set> scan(Class... entityBasePackageClasses) throws ClassNotFoundException { + return new CassandraEntityClassScanner(entityBasePackageClasses).scanForEntityClasses(); + } + + public static Set> scan(Collection entityBasePackages) throws ClassNotFoundException { + return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses(); + } + + public static Set> scan(Collection entityBasePackages, Collection> entityBasePackageClasses) + throws ClassNotFoundException { + return new CassandraEntityClassScanner(entityBasePackages, entityBasePackageClasses).scanForEntityClasses(); + } + protected Set entityBasePackages = new HashSet(); protected Set> entityBasePackageClasses = new HashSet>(); protected ClassLoader beanClassLoader; @@ -29,6 +47,14 @@ public class CassandraEntityClassScanner { public CassandraEntityClassScanner() { } + public CassandraEntityClassScanner(Class... entityBasePackageClasses) { + this(null, Arrays.asList(entityBasePackageClasses)); + } + + public CassandraEntityClassScanner(String... entityBasePackages) { + this(Arrays.asList(entityBasePackages)); + } + public CassandraEntityClassScanner(Collection entityBasePackages) { this(entityBasePackages, null); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java deleted file mode 100644 index 5fc72ddf0..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingContextFactoryBean.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.springframework.data.cassandra.config; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.data.cassandra.mapping.CassandraMappingContext; -import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; -import org.springframework.data.cassandra.mapping.Mapping; -import org.springframework.util.Assert; - -public class CassandraMappingContextFactoryBean implements FactoryBean, InitializingBean { - - protected Set entityBasePackages = new HashSet(); - protected Mapping mapping; - protected ClassLoader beanClassLoader; - - @Override - public void afterPropertiesSet() throws Exception { - mapping = mapping == null ? new Mapping() : mapping; - } - - @Override - public CassandraMappingContext getObject() throws Exception { - - DefaultCassandraMappingContext mappingContext = new DefaultCassandraMappingContext(); - mappingContext.setInitialEntitySet(new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses()); - mappingContext.setMapping(mapping); - mappingContext.setBeanClassLoader(beanClassLoader); - - mappingContext.initialize(); // this is necessary here - - return mappingContext; - } - - @Override - public Class getObjectType() { - return CassandraMappingContext.class; - } - - @Override - public boolean isSingleton() { - return true; - } - - public Set getEntityBasePackages() { - return entityBasePackages; - } - - public void setEntityBasePackages(Set basePackages) { - this.entityBasePackages = basePackages == null ? new HashSet() : new HashSet(basePackages); - } - - public Mapping getMapping() { - return mapping; - } - - public void setMapping(Mapping mapping) { - - Assert.notNull(mapping); - - this.mapping = mapping; - } - - public ClassLoader getBeanClassLoader() { - return beanClassLoader; - } - - public void setBeanClassLoader(ClassLoader entityClassLoader) { - this.beanClassLoader = entityClassLoader; - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java deleted file mode 100644 index e6e3353f5..000000000 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingConverterFactoryBean.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.springframework.data.cassandra.config; - -import java.util.HashSet; -import java.util.Set; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.data.cassandra.convert.CassandraConverter; -import org.springframework.data.cassandra.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.mapping.CassandraMappingContext; -import org.springframework.util.Assert; - -public class CassandraMappingConverterFactoryBean implements FactoryBean, InitializingBean { - - protected CassandraMappingContext mappingContext; - protected Set basePackages = new HashSet(); - - @Override - public void afterPropertiesSet() throws Exception { - Assert.notNull(mappingContext); - Assert.notNull(basePackages); - } - - @Override - public CassandraConverter getObject() throws Exception { - return new MappingCassandraConverter(mappingContext); - } - - @Override - public Class getObjectType() { - return CassandraConverter.class; - } - - @Override - public boolean isSingleton() { - return true; - } - - public CassandraMappingContext getMappingContext() { - return mappingContext; - } - - public void setMappingContext(CassandraMappingContext mappingContext) { - - Assert.notNull(mappingContext); - - this.mappingContext = mappingContext; - } -} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java index 2408b305a..601235749 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java @@ -15,22 +15,19 @@ */ package org.springframework.data.cassandra.config.java; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.cassandra.config.java.AbstractClusterConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean; import org.springframework.data.cassandra.config.CassandraEntityClassScanner; -import org.springframework.data.cassandra.config.CassandraMappingContextFactoryBean; -import org.springframework.data.cassandra.config.CassandraMappingConverterFactoryBean; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.convert.CassandraConverter; +import org.springframework.data.cassandra.convert.MappingCassandraConverter; import org.springframework.data.cassandra.core.CassandraAdminOperations; import org.springframework.data.cassandra.core.CassandraAdminTemplate; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; import org.springframework.data.cassandra.mapping.Table; import org.springframework.data.mapping.context.MappingContext; @@ -69,7 +66,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC CassandraDataSessionFactoryBean bean = new CassandraDataSessionFactoryBean(); bean.setCluster(cluster().getObject()); - bean.setConverter(cassandraConverter().getObject()); + bean.setConverter(cassandraConverter()); bean.setSchemaAction(getSchemaAction()); bean.setKeyspaceName(getKeyspaceName()); bean.setStartupScripts(getStartupScripts()); @@ -85,7 +82,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC */ @Bean public CassandraAdminOperations cassandraTemplate() throws Exception { - return new CassandraAdminTemplate(session().getObject(), cassandraConverter().getObject()); + return new CassandraAdminTemplate(session().getObject(), cassandraConverter()); } /** @@ -94,10 +91,10 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC * @throws ClassNotFoundException */ @Bean - public CassandraMappingContextFactoryBean cassandraMapping() throws ClassNotFoundException { + public CassandraMappingContext cassandraMapping() throws ClassNotFoundException { - CassandraMappingContextFactoryBean bean = new CassandraMappingContextFactoryBean(); - bean.setEntityBasePackages(new HashSet(Arrays.asList(getEntityBasePackages()))); + DefaultCassandraMappingContext bean = new DefaultCassandraMappingContext(); + bean.setInitialEntitySet(CassandraEntityClassScanner.scan(getEntityBasePackages())); bean.setBeanClassLoader(beanClassLoader); return bean; @@ -107,32 +104,8 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC * Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements */ @Bean - public CassandraMappingConverterFactoryBean cassandraConverter() throws Exception { - - CassandraMappingConverterFactoryBean bean = new CassandraMappingConverterFactoryBean(); - bean.setMappingContext(cassandraMapping().getObject()); - - return bean; - } - - /** - * Scans the mapping base package for entity classes. - * - * @see #getEntityBasePackages() - * @see #getEntityScanner() - * @return Set<Class<?>> representing the annotated entity classes found. - * @throws ClassNotFoundException - */ - protected Set> getInitialEntitySet() throws ClassNotFoundException { - - CassandraEntityClassScanner entityScanner = getEntityScanner(); - entityScanner.setEntityBasePackages(Arrays.asList(getEntityBasePackages())); - - return entityScanner.scanForEntityClasses(); - } - - public CassandraEntityClassScanner getEntityScanner() { - return new CassandraEntityClassScanner(); + public CassandraConverter cassandraConverter() throws Exception { + return new MappingCassandraConverter(cassandraMapping()); } @Override diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java index a7e0cf95e..6565af3ee 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java @@ -6,10 +6,10 @@ import java.util.Set; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.cassandra.config.CassandraMappingContextFactoryBean; import org.springframework.data.cassandra.config.DefaultDataBeanNames; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; import org.springframework.data.cassandra.mapping.EntityMapping; import org.springframework.data.cassandra.mapping.Mapping; import org.springframework.util.StringUtils; @@ -21,11 +21,11 @@ import org.w3c.dom.Element; * * @author Matthew T. Adams */ -public class CassandraMappingContextParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraMappingContextParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { - return CassandraMappingContextFactoryBean.class; + return DefaultCassandraMappingContext.class; } @Override @@ -37,33 +37,17 @@ public class CassandraMappingContextParser extends AbstractSimpleBeanDefinitionP } @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - super.doParse(element, parserContext, builder); - - parseBasePackagesAttribute(element, parserContext, builder); - parseMapping(element, parserContext, builder); + protected void doParse(Element element, BeanDefinitionBuilder builder) { + parseMapping(element, builder); } - protected void parseBasePackagesAttribute(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - String basePackages = element.getAttribute("entity-base-packages"); - if (!StringUtils.hasText(basePackages)) { - return; - } - - Set basePackageSet = StringUtils.commaDelimitedListToSet(basePackages); - builder.addPropertyValue("entityBasePackages", basePackageSet); - } - - protected void parseMapping(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - // TODO: parse **attributes** here, if there ever are any + protected void parseMapping(Element element, BeanDefinitionBuilder builder) { Set mappings = new HashSet(); - for (Element child : DomUtils.getChildElementsByTagName(element, "entity")) { + for (Element entity : DomUtils.getChildElementsByTagName(element, "entity")) { - EntityMapping entityMapping = parseEntity(child); + EntityMapping entityMapping = parseEntity(entity); if (entityMapping != null) { mappings.add(entityMapping); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java index 2ee61c67b..8f75493f9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java @@ -3,10 +3,10 @@ package org.springframework.data.cassandra.config.xml; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.cassandra.config.CassandraMappingConverterFactoryBean; import org.springframework.data.cassandra.config.DefaultDataBeanNames; +import org.springframework.data.cassandra.convert.MappingCassandraConverter; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -15,11 +15,11 @@ import org.w3c.dom.Element; * * @author Matthew T. Adams */ -public class CassandraMappingConverterParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraMappingConverterParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { - return CassandraMappingConverterFactoryBean.class; + return MappingCassandraConverter.class; } @Override @@ -31,21 +31,13 @@ public class CassandraMappingConverterParser extends AbstractSimpleBeanDefinitio } @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + protected void doParse(Element element, BeanDefinitionBuilder builder) { - super.doParse(element, parserContext, builder); - - parseMappingContextAttribute(element, parserContext, builder); - } - - protected void parseMappingContextAttribute(Element element, ParserContext parserContext, - BeanDefinitionBuilder builder) { - - String mappingContextRef = element.getAttribute("mapping-ref"); - if (!StringUtils.hasText(mappingContextRef)) { - mappingContextRef = DefaultDataBeanNames.MAPPING_CONTEXT; + String mappingRef = element.getAttribute("mapping-ref"); + if (!StringUtils.hasText(mappingRef)) { + mappingRef = DefaultDataBeanNames.MAPPING_CONTEXT; } - builder.addPropertyReference("mappingContext", mappingContextRef); + builder.addConstructorArgReference(mappingRef); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index b3bb25d5d..7971580c4 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -56,7 +56,6 @@ public class DefaultCassandraMappingContext extends protected ApplicationContext context; protected Mapping mapping = new Mapping(); protected ClassLoader beanClassLoader; - protected boolean initialized; // useful caches protected Map>> entitySetsByTableName = new HashMap>>(); @@ -70,27 +69,12 @@ public class DefaultCassandraMappingContext extends setSimpleTypeHolder(new CassandraSimpleTypeHolder()); } - @Override - public void afterPropertiesSet() { - if (initialized) { - return; - } - - super.afterPropertiesSet(); - } - @Override public void initialize() { - if (initialized) { - return; - } - super.initialize(); processMappingOverrides(); - - initialized = true; } @Override diff --git a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml index 3d736d478..b5ac038be 100644 --- a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml +++ b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml @@ -10,7 +10,7 @@ - + From a031ff77fe199b49e6ee4af4a37c64a4e6f5c5c4 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Tue, 11 Feb 2014 13:13:10 -0600 Subject: [PATCH 7/8] DATACASS-91 - added test that searches multiple packages --- .../cassandra/core/CassandraTemplate.java | 10 ++-- .../BasicCassandraPersistentEntity.java | 1 - .../mapping/CassandraMappingContext.java | 20 -------- .../DefaultCassandraMappingContext.java | 25 ---------- .../MappingContextIntegrationTests.java | 28 +++++++++++ .../MultipackageScanningIntegrationTests.java | 47 +++++++++++++++++++ .../integration/multipackagescanning/Top.java | 11 +++++ .../multipackagescanning/first/First.java | 11 +++++ .../multipackagescanning/second/Second.java | 11 +++++ .../multipackagescanning/third/Third.java | 11 +++++ 10 files changed, 124 insertions(+), 51 deletions(-) create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 189719802..f000371d7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -123,7 +123,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(type); Assert.notNull(id); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(type); Select select = QueryBuilder.select().countAll().from(entity.getTableName()); appendIdCriteria(select.where(), entity, id); @@ -152,9 +152,9 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation Assert.notNull(type); Assert.notNull(id); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(type); - Delete delete = QueryBuilder.delete().all().from(entity.getTableName()); + Delete delete = QueryBuilder.delete().from(entity.getTableName()); appendIdCriteria(delete.where(), entity, id); execute(delete.getQueryString()); @@ -192,7 +192,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public String getTableName(Class type) { - return mappingContext.getRequiredPersistentEntity(type).getTableName(); + return mappingContext.getPersistentEntity(type).getTableName(); } @Override @@ -252,7 +252,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation @Override public List selectBySimpleIds(Class type, Iterable ids) { - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); + CassandraPersistentEntity entity = mappingContext.getPersistentEntity(type); if (entity.getIdProperty().isCompositePrimaryKey()) { throw new IllegalArgumentException(String.format( diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java index a1b52dd25..863e8f838 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java @@ -27,7 +27,6 @@ import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.data.cassandra.util.CassandraNamingUtils; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; -import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java index d468e4b0a..9d712931d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java @@ -57,24 +57,4 @@ public interface CassandraMappingContext extends * @param table May not be null. */ boolean usesTable(TableMetadata table); - - /** - * Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws - * {@link IllegalArgumentException}. - * - * @param type The Java type of the persistent entity. - * @return The {@link CassandraPersistentEntity} describing the persistent Java type. - * @throws IllegalArgumentException if the persistent entity is unknown - */ - public CassandraPersistentEntity getRequiredPersistentEntity(Class type); - - /** - * Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws - * {@link IllegalArgumentException}. - * - * @param type The {@link TypeInformation} of the persistent entity. - * @return The {@link CassandraPersistentEntity} describing the persistent Java type. - * @throws IllegalArgumentException if the persistent entity is unknown - */ - public CassandraPersistentEntity getRequiredPersistentEntity(TypeInformation type); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index 7971580c4..8f860b505 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -198,31 +198,6 @@ public class DefaultCassandraMappingContext extends return spec; } - @Override - public CassandraPersistentEntity getRequiredPersistentEntity(Class type) { - - CassandraPersistentEntity entity = getPersistentEntity(type); - - if (entity == null) { - throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]", type.getName())); - } - - return entity; - } - - @Override - public CassandraPersistentEntity getRequiredPersistentEntity(TypeInformation type) { - - CassandraPersistentEntity entity = getPersistentEntity(type); - - if (entity == null) { - throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]", - type.getActualType())); - } - - return entity; - } - public void setMapping(Mapping mapping) { Assert.notNull(mapping); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java new file mode 100644 index 000000000..95a372c9f --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java @@ -0,0 +1,28 @@ +package org.springframework.data.cassandra.test.integration.mappingcontext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; + +public class MappingContextIntegrationTests { + + public static class Transient { + } + + @Test + // TODO: (expected = MappingException.class) + public void testGetPersistentEntityOfTransientType() { + + // TODO: when entity verification is added (DATACASS-85), this should throw a MappingException + + DefaultCassandraMappingContext ctx = new DefaultCassandraMappingContext(); + CassandraPersistentEntity entity = ctx.getPersistentEntity(Transient.class); + + // TODO: remove following lines after DATACASS-85 + assertNotNull(entity); + assertEquals(Transient.class.getSimpleName().toLowerCase(), entity.getTableName()); + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java new file mode 100644 index 000000000..ee5fd8014 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java @@ -0,0 +1,47 @@ +package org.springframework.data.cassandra.test.integration.multipackagescanning; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.HashSet; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.cassandra.config.CassandraEntityClassScanner; +import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.data.cassandra.test.integration.multipackagescanning.first.First; +import org.springframework.data.cassandra.test.integration.multipackagescanning.second.Second; +import org.springframework.data.cassandra.test.integration.multipackagescanning.third.Third; + +public class MultipackageScanningIntegrationTests { + + DefaultCassandraMappingContext mapping; + String pkg = getClass().getPackage().getName(); + + @Before + public void before() throws ClassNotFoundException { + + mapping = new DefaultCassandraMappingContext(); + mapping.setInitialEntitySet(CassandraEntityClassScanner.scan(pkg + ".first", pkg + ".second")); + + mapping.initialize(); + } + + @Test + public void test() { + + Collection> entities = mapping.getPersistentEntities(); + + Collection> types = new HashSet>(entities.size()); + for (CassandraPersistentEntity entity : entities) { + types.add(entity.getType()); + } + + assertTrue(types.contains(First.class)); + assertTrue(types.contains(Second.class)); + assertFalse(types.contains(Third.class)); + assertFalse(types.contains(Top.class)); + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java new file mode 100644 index 000000000..2d4bcfea4 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java @@ -0,0 +1,11 @@ +package org.springframework.data.cassandra.test.integration.multipackagescanning; + +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; + +@Table +public class Top { + + @PrimaryKey + String key; +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java new file mode 100644 index 000000000..ac937b8ef --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java @@ -0,0 +1,11 @@ +package org.springframework.data.cassandra.test.integration.multipackagescanning.first; + +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; + +@Table +public class First { + + @PrimaryKey + String key; +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java new file mode 100644 index 000000000..7e3478ba4 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java @@ -0,0 +1,11 @@ +package org.springframework.data.cassandra.test.integration.multipackagescanning.second; + +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; + +@Table +public class Second { + + @PrimaryKey + String key; +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java new file mode 100644 index 000000000..39ccbdda0 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java @@ -0,0 +1,11 @@ +package org.springframework.data.cassandra.test.integration.multipackagescanning.third; + +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; + +@Table +public class Third { + + @PrimaryKey + String key; +} From eb68f6f5558fd6be5acf464ae383249aab6225b4 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Wed, 12 Feb 2014 18:32:08 -0600 Subject: [PATCH 8/8] DATACASS-91 - now defaulting mapping context, converter, & template if not defined in XML --- .../config/BeanDefinitionBuilderArgument.java | 65 ++++++ .../cassandra/config/BeanDefinitionUtils.java | 144 ++++++++++++++ ...sandraMappingBeanFactoryPostProcessor.java | 188 ++++++++++++++++++ .../config/DefaultDataBeanNames.java | 2 +- .../xml/CassandraDataClusterParser.java | 11 + .../xml/CassandraDataSessionParser.java | 11 +- .../xml/CassandraDataTemplateParser.java | 3 + .../xml/CassandraMappingContextParser.java | 7 +- .../xml/CassandraMappingConverterParser.java | 6 +- ...gXmlBeanFactoryPostProcessorRegistrar.java | 44 ++++ .../mapping/CassandraMappingContext.java | 16 +- .../DefaultCassandraMappingContext.java | 19 ++ .../MappingContextIntegrationTests.java | 30 ++- ...soluteMinimumXmlConfigIntegrationTest.java | 28 +++ .../minimal/config/entities/AbsMin.java | 12 ++ .../config/entities/AbsMinRepository.java | 6 + ...inimumXmlConfigIntegrationTest-context.xml | 22 ++ 17 files changed, 605 insertions(+), 9 deletions(-) create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java create mode 100644 spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java create mode 100644 spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java create mode 100644 spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java new file mode 100644 index 000000000..fa72ff549 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java @@ -0,0 +1,65 @@ +package org.springframework.data.cassandra.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.util.StringUtils; + +/** + * Parameter used in conjunction with: + *

+ * {@link BeanDefinitionBuilder#addConstructorArgReference(String)}, + * {@link BeanDefinitionBuilder#addConstructorArgValue(Object)}, + * {@link BeanDefinitionBuilder#addPropertyReference(String, String)}, and + * {@link BeanDefinitionBuilder#addPropertyValue(String, Object)}. + *

+ * Easy and succinct to create if methods {@link #ref(CharSequence)} or {@link #val(Object)} are used and imported + * statically. + * + * @see BeanDefinitionBuilderArgument#ref(CharSequence) + * @see BeanDefinitionBuilderArgument#val(Object) + */ +public class BeanDefinitionBuilderArgument { + + /** + * Returns a {@link BeanDefinitionBuilderArgument} with {@link #reference} equal to true. Convenient if + * imported statically. + * + * @param value The name of the bean reference. + */ + public static BeanDefinitionBuilderArgument ref(CharSequence value) { + return new BeanDefinitionBuilderArgument(true, value); + } + + /** + * Returns a {@link BeanDefinitionBuilderArgument} with {@link #reference} equal to false. Convenient if + * imported statically. + * + * @param value The constructor argument's value. + */ + public static BeanDefinitionBuilderArgument val(Object value) { + return new BeanDefinitionBuilderArgument(false, value); + } + + protected boolean reference; + protected Object value; + + protected BeanDefinitionBuilderArgument(boolean reference, Object value) { + this.reference = reference; + if (this.reference && (value == null || !(value instanceof CharSequence))) { + throw new IllegalArgumentException(String.format( + "reference argument must have value of type CharSequence, not [%s]", value == null ? "null" : value + .getClass().getName())); + } + if (!StringUtils.hasText((CharSequence) value)) { + throw new IllegalArgumentException("given CharSequence has no text"); + } + this.value = value; + } + + public boolean isReference() { + return reference; + } + + public Object getValue() { + return value; + } +} \ No newline at end of file diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java new file mode 100644 index 000000000..666c5beb5 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java @@ -0,0 +1,144 @@ +package org.springframework.data.cassandra.config; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.util.StringUtils; + +public class BeanDefinitionUtils { + + /** + * Returns a {@link BeanDefinitionBuilder} iff no {@link BeanDefinition} of the required type is found in the given + * {@link ListableBeanFactory}, otherwise returns null, indicating that at least one existed. + * + * @param factory The {@link ListableBeanFactory} in which to look for the {@link BeanDefinition}, including + * ancestors. + * @param requiredType The {@link BeanDefinition}'s required type. + * @param instantiableType The instantiable type for the {@link BeanDefinitionBuilder}. + * @param constructorArgs Any {@link BeanDefinitionBuilderArgument}s required by the instantiableType's constructor. + * @return A {@link BeanDefinitionBuilder} iff no {@link BeanDefinition} of the required type is found, otherwise + * null. + * @see BeanDefinitionUtils#createBeanDefinitionBuilderIfNoBeanDefinitionOfTypeExists(ListableBeanFactory, Class, + * Class, BeanDefinitionBuilderArgument...) + * @see BeanDefinitionBuilderArgument#ref(Object) + * @see BeanDefinitionBuilderArgument#val(Object) + */ + public static BeanDefinitionBuilder createBeanDefinitionBuilderIfNoBeanDefinitionOfTypeExists( + ListableBeanFactory factory, Class requiredType, Class instantiableType, + BeanDefinitionBuilderArgument... constructorArgs) { + + String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, requiredType, true, false); + if (names.length > 0) { + return null; + } + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(instantiableType); + if (constructorArgs == null) { + return builder; + } + + for (BeanDefinitionBuilderArgument arg : constructorArgs) { + if (arg.reference) { + builder.addConstructorArgReference(arg.value.toString()); + } else { + builder.addConstructorArgValue(arg.value); + } + } + + return builder; + } + + /** + * Returns the single {@link BeanDefinitionHolder} with the given type, or null of none were found and + * required was false, otherwise throws {@link IllegalArgumentException}. + * + * @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the factor + * parameter. + * @param factory The {@link ListableBeanFactory}, often the very same instance as the registry + * parameter. + * @param type The required {@link BeanDefinition}'s type. + * @param includeNonSingletons Whether to include beans with scope other than singleton + * @param allowEagerInit Whether to allow eager initialization of beans. + * @param required Whether to allow the return of null if none were found. + * @return The {@link BeanDefinitionHolder} or null if none found, depending on the value of required. + * @throws IllegalArgumentException If multiple were found. + * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) + */ + public static BeanDefinitionHolder getSingleBeanDefinitionOfType(BeanDefinitionRegistry registry, + ListableBeanFactory factory, Class type, boolean includeNonSingletons, boolean allowEagerInit, boolean required) { + + BeanDefinitionHolder[] definitions = getBeanDefinitionsOfType(registry, factory, type, includeNonSingletons, + allowEagerInit); + + if (definitions.length == 1) { + return definitions[0]; + } + + if (definitions.length == 0 && !required) { + return null; + } + + String[] names = new String[definitions.length]; + for (int i = 0; i < names.length; i++) { + names[i] = definitions[i].getBeanName(); + } + + throw new IllegalStateException(String.format("expected one bean definition of type [%s], but found %d: %s", + type.getName(), definitions.length, StringUtils.arrayToCommaDelimitedString(names))); + } + + /** + * Returns all {@link BeanDefinitionHolder}s with the given type. + * + * @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the factor + * parameter. + * @param factory The {@link ListableBeanFactory}, often the very same instance as the registry + * parameter. + * @param type The required {@link BeanDefinition}'s type. + * @param includeNonSingletons Whether to include beans with scope other than singleton + * @param allowEagerInit Whether to allow eager initialization of beans. + * @param required Whether to allow the return of null if none were found. + * @return The {@link BeanDefinitionHolder}s -- never returns null. + * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) + */ + public static BeanDefinitionHolder[] getBeanDefinitionsOfType(BeanDefinitionRegistry registry, + ListableBeanFactory factory, Class type, boolean includeNonSingletons, boolean allowEagerInit) { + + String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, type, includeNonSingletons, + allowEagerInit); + + if (names.length == 0) { + return new BeanDefinitionHolder[] {}; + } + + BeanDefinitionHolder[] array = new BeanDefinitionHolder[names.length]; + + for (int i = 0; i < names.length; i++) { + + String name = names[i]; + BeanDefinition beanDefinition = null; + + while (beanDefinition == null) { + try { + beanDefinition = registry.getBeanDefinition(name); + } catch (NoSuchBeanDefinitionException x) { + if (FactoryBean.class.isAssignableFrom(type)) { // try unmangled BeanFactory-prefixed name + name = name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); + } else { + throw x; + } + } + } + + array[i] = new BeanDefinitionHolder(beanDefinition, name); + } + + return array; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java new file mode 100644 index 000000000..7b12fbb3e --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java @@ -0,0 +1,188 @@ +package org.springframework.data.cassandra.config; + +import static org.springframework.data.cassandra.config.BeanDefinitionUtils.getBeanDefinitionsOfType; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.data.cassandra.convert.CassandraConverter; +import org.springframework.data.cassandra.convert.MappingCassandraConverter; +import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.util.StringUtils; + +import com.datastax.driver.core.Session; + +/** + * {@link BeanDefinitionRegistryPostProcessor} that does its best to register any missing Spring Data Cassandra beans + * that can be defaulted. Specifically, it attempts to create default bean definitions for the following required + * interface types via their default implementation types: + *

    + *
  • {@link CassandraOperations} via {@link CassandraTemplate}
  • + *
  • {@link CassandraMappingContext} via {@link DefaultCassandraMappingContext}
  • + *
  • {@link CassandraConverter} via {@link MappingCassandraConverter}
  • + *
+ *

+ * If there are multiple definitions for any type that another type depends on, an {@link IllegalStateException} is + * thrown. For example, if there are two definitions for type {@link CassandraMappingContext} present and no definition + * for type {@link CassandraConverter}, then it's impossible to know which {@link CassandraMappingContext} is to be used + * when creating a default definition for the {@link CassandraConverter}. + *

+ * If a single definition of a required type is present, then it is used. For example, if there is already a + * {@link CassandraMappingContext} definition present, then it will be used in the + * {@link DefaultCassandraMappingContext} bean definition. + *

+ * It requires that a single {@link Session} or {@link CassandraDataSessionFactoryBean} definition be present. As + * described above, multiple {@link Session} definitions, multiple {@link CassandraDataSessionFactoryBean} definitions, + * or both a {@link Session} and {@link CassandraDataSessionFactoryBean} will cause an {@link IllegalStateException} to + * be thrown. + * + * @author Matthew T. Adams + */ +public class CassandraMappingBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { + + /** + * Does nothing. + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + } + + /** + * Ensures that {@link BeanDefinition}s for a {@link CassandraMappingContext} and a {@link CassandraConverter} exist. + */ + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + + if (!(registry instanceof ListableBeanFactory)) { + return; + } + ListableBeanFactory factory = (ListableBeanFactory) registry; + + registerMissingDefaultableBeanDefinitions(registry, factory); + } + + protected void registerMissingDefaultableBeanDefinitions(BeanDefinitionRegistry registry, ListableBeanFactory factory) { + + // see if any template definitions exist, which requires a converter, which requires a mapping context + BeanDefinitionHolder[] templateBeans = getBeanDefinitionsOfType(registry, factory, CassandraOperations.class, true, + false); + + if (templateBeans.length >= 1) { + return; + } + + // need a session & converter for the default template + + // see if an actual Session definition exists + String sessionBeanName = findSessionBeanName(registry, factory); + + // see if any converter bean definitions exist, which requires a mapping context + BeanDefinitionHolder[] converterBeans = getBeanDefinitionsOfType(registry, factory, + MappingCassandraConverter.class, true, false); + + if (converterBeans.length == 1) { + + registerDefaultTemplate(registry, sessionBeanName, converterBeans[0].getBeanName()); + return; + } else if (converterBeans.length > 1) { + // then throw, because we need to create a default converter, but we wouldn't know which mapping context to use + throw new IllegalStateException(String.format( + "found %d beans of type [%s] - can't disambiguate for creation of [%s]", converterBeans.length, + CassandraConverter.class.getName(), CassandraTemplate.class.getName())); + } + + // see if any mapping context bean definitions exist + BeanDefinitionHolder[] contextBeans = getBeanDefinitionsOfType(registry, factory, CassandraMappingContext.class, + true, false); + + if (contextBeans.length > 1) { + // then throw, because we need to create a default converter, but we wouldn't know which mapping context to use + throw new IllegalStateException(String.format( + "found %d beans of type [%s] - can't disambiguate for creation of [%s]", contextBeans.length, + CassandraMappingContext.class.getName(), MappingCassandraConverter.class.getName())); + } + + // create the mapping context if necessary + BeanDefinitionHolder contextBean = contextBeans.length == 1 ? contextBeans[0] : null; + if (contextBean == null) { + contextBean = regsiterDefaultContext(registry); + } + + // create the default converter & template bean definitions + BeanDefinitionHolder converter = registerDefaultConverter(registry, contextBean.getBeanName()); + registerDefaultTemplate(registry, sessionBeanName, converter.getBeanName()); + } + + public String findSessionBeanName(BeanDefinitionRegistry registry, ListableBeanFactory factory) { + + // first, search for any session and session factory beans + BeanDefinitionHolder[] sessionBeans = getBeanDefinitionsOfType(registry, factory, Session.class, true, false); + BeanDefinitionHolder[] sessionFactoryBeans = getBeanDefinitionsOfType(registry, factory, + CassandraDataSessionFactoryBean.class, true, false); + + int sessionCount = sessionBeans.length; + int sessionFactoryCount = sessionFactoryBeans.length; + int totalCount = sessionCount + sessionFactoryCount; + + if (totalCount == 0 || totalCount > 1) { // can't create default template -- none or multiple + throw createSessionException(totalCount, Session.class, CassandraDataSessionFactoryBean.class); + } + + if (sessionCount == 1) { + return sessionBeans[0].getBeanName(); + } + // else it must be the one session factory bean + return sessionFactoryBeans[0].getBeanName(); + } + + protected IllegalStateException createSessionException(int beanDefinitionCount, Class... types) { + + return new IllegalStateException(String.format("found %d beans of type%s [%s] - %s for creation of default [%s]", + beanDefinitionCount, beanDefinitionCount == 1 ? "" : "s", StringUtils.arrayToCommaDelimitedString(types), + beanDefinitionCount == 0 ? "need exactly one" : "can't disambiguate", CassandraTemplate.class.getName())); + } + + protected BeanDefinitionHolder regsiterDefaultContext(BeanDefinitionRegistry registry) { + + BeanDefinitionHolder contextBean = new BeanDefinitionHolder(BeanDefinitionBuilder.genericBeanDefinition( + DefaultCassandraMappingContext.class).getBeanDefinition(), DefaultDataBeanNames.CONTEXT); + + registry.registerBeanDefinition(contextBean.getBeanName(), contextBean.getBeanDefinition()); + + return contextBean; + } + + public BeanDefinitionHolder registerDefaultConverter(BeanDefinitionRegistry registry, String contextBeanName) { + + BeanDefinitionBuilder converterBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition( + MappingCassandraConverter.class).addConstructorArgReference(contextBeanName); + BeanDefinitionHolder beanDefinition = new BeanDefinitionHolder(converterBeanDefinitionBuilder.getBeanDefinition(), + DefaultDataBeanNames.CONVERTER); + + registry.registerBeanDefinition(beanDefinition.getBeanName(), beanDefinition.getBeanDefinition()); + + return beanDefinition; + } + + public BeanDefinitionHolder registerDefaultTemplate(BeanDefinitionRegistry registry, String sessionBeanName, + String converterBeanName) { + + BeanDefinitionBuilder templateBeanDefinitionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(CassandraTemplate.class).addConstructorArgReference(sessionBeanName) + .addConstructorArgReference(converterBeanName); + BeanDefinition beanDefinition = templateBeanDefinitionBuilder.getBeanDefinition(); + + BeanDefinitionHolder template = new BeanDefinitionHolder(beanDefinition, DefaultDataBeanNames.TEMPLATE); + registry.registerBeanDefinition(template.getBeanName(), template.getBeanDefinition()); + + return template; + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java index c91114d88..959c0623b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java @@ -6,5 +6,5 @@ public interface DefaultDataBeanNames extends DefaultBeanNames { public static final String DATA_TEMPLATE = "cassandraTemplate"; public static final String CONVERTER = "cassandraConverter"; - public static final String MAPPING_CONTEXT = "cassandraMapping"; + public static final String CONTEXT = "cassandraMapping"; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java index e2d3dc43f..856057ed1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java @@ -1,6 +1,9 @@ package org.springframework.data.cassandra.config.xml; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.xml.CassandraClusterParser; +import org.w3c.dom.Element; /** * Spring Data Cassandra XML namespace parser for the <cluster> element. @@ -8,4 +11,12 @@ import org.springframework.cassandra.config.xml.CassandraClusterParser; * @author Matthew T. Adams */ public class CassandraDataClusterParser extends CassandraClusterParser { + + @Override + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + + CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext); + + return super.parseInternal(element, parserContext); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java index e850bb07b..8e80f39ce 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java @@ -3,6 +3,7 @@ package org.springframework.data.cassandra.config.xml; import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyReference; import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyValue; import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredPropertyReference; +import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredPropertyValue; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; @@ -25,6 +26,14 @@ public class CassandraDataSessionParser extends CassandraSessionParser { return CassandraDataSessionFactoryBean.class; } + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + super.doParse(element, parserContext, builder); + + CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext); + } + @Override protected void parseUnhandledSessionElementAttribute(Attr attribute, ParserContext parserContext, BeanDefinitionBuilder builder) { @@ -45,7 +54,7 @@ public class CassandraDataSessionParser extends CassandraSessionParser { super.setDefaultProperties(builder); + addRequiredPropertyValue(builder, "schemaAction", SchemaAction.NONE.name()); addRequiredPropertyReference(builder, "converter", DefaultDataBeanNames.CONVERTER); - addRequiredPropertyReference(builder, "schemaAction", SchemaAction.NONE.name()); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java index 13251df72..893086ec1 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java @@ -32,6 +32,9 @@ public class CassandraDataTemplateParser extends CassandraTemplateParser { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext); + super.doParse(element, parserContext, builder); parseConverterAttribute(element, parserContext, builder); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java index 6565af3ee..1c4d482d6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java @@ -33,11 +33,14 @@ public class CassandraMappingContextParser extends AbstractSingleBeanDefinitionP throws BeanDefinitionStoreException { String id = super.resolveId(element, definition, parserContext); - return StringUtils.hasText(id) ? id : DefaultDataBeanNames.MAPPING_CONTEXT; + return StringUtils.hasText(id) ? id : DefaultDataBeanNames.CONTEXT; } @Override - protected void doParse(Element element, BeanDefinitionBuilder builder) { + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext); + parseMapping(element, builder); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java index 8f75493f9..5dd2a6c81 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java @@ -31,11 +31,13 @@ public class CassandraMappingConverterParser extends AbstractSingleBeanDefinitio } @Override - protected void doParse(Element element, BeanDefinitionBuilder builder) { + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext); String mappingRef = element.getAttribute("mapping-ref"); if (!StringUtils.hasText(mappingRef)) { - mappingRef = DefaultDataBeanNames.MAPPING_CONTEXT; + mappingRef = DefaultDataBeanNames.CONTEXT; } builder.addConstructorArgReference(mappingRef); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java new file mode 100644 index 000000000..b7499c34e --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java @@ -0,0 +1,44 @@ +package org.springframework.data.cassandra.config.xml; + +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.data.cassandra.config.CassandraMappingBeanFactoryPostProcessor; +import org.springframework.data.config.BeanComponentDefinitionBuilder; +import org.w3c.dom.Element; + +/** + * Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered. + * + * @author Matthew T. Adams + */ +public class CassandraMappingXmlBeanFactoryPostProcessorRegistrar { + + /** + * Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered. This method is a no-op if one is + * already registered. + */ + public static void ensureRegistration(Element element, ParserContext parserContext) { + + BeanDefinitionRegistry registry = parserContext.getRegistry(); + if (!(registry instanceof GenericApplicationContext)) { + return; + } + ConfigurableListableBeanFactory factory = ((GenericApplicationContext) registry).getBeanFactory(); + + String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, + CassandraMappingBeanFactoryPostProcessor.class, true, false); + if (names.length > 0) { + return; + } + + BeanComponentDefinitionBuilder componentBuilder = new BeanComponentDefinitionBuilder(element, parserContext); + BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(CassandraMappingBeanFactoryPostProcessor.class); + + parserContext.registerBeanComponent(componentBuilder.getComponent(definitionBuilder)); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java index 9d712931d..06bf6fe15 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java @@ -4,7 +4,6 @@ import java.util.Collection; import org.springframework.cassandra.core.keyspace.CreateTableSpecification; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.util.TypeInformation; import com.datastax.driver.core.TableMetadata; @@ -57,4 +56,19 @@ public interface CassandraMappingContext extends * @param table May not be null. */ boolean usesTable(TableMetadata table); + + /** + * Returns the existing {@link CassandraPersistentEntity} for the given {@link Class}. If it is not yet known to this + * {@link CassandraMappingContext}, an {@link IllegalArgumentException} is thrown. + * + * @param type The class of the existing persistent entity. + * @return The existing persistent entity. + */ + CassandraPersistentEntity getExistingPersistentEntity(Class type); + + /** + * Returns whether this {@link CassandraMappingContext} already contains a {@link CassandraPersistentEntity} for the + * given type. + */ + boolean contains(Class type); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java index 8f860b505..ff7747400 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java @@ -61,6 +61,7 @@ public class DefaultCassandraMappingContext extends protected Map>> entitySetsByTableName = new HashMap>>(); protected Set> nonPrimaryKeyEntities = new HashSet>(); protected Set> primaryKeyEntities = new HashSet>(); + protected Map, CassandraPersistentEntity> entitiesByType = new HashMap, CassandraPersistentEntity>(); /** * Creates a new {@link DefaultCassandraMappingContext}. @@ -137,6 +138,8 @@ public class DefaultCassandraMappingContext extends nonPrimaryKeyEntities.add(entity); } + entitiesByType.put(entity.getType(), entity); + return entity; } @@ -243,4 +246,20 @@ public class DefaultCassandraMappingContext extends public void setBeanClassLoader(ClassLoader beanClassLoader) { this.beanClassLoader = beanClassLoader; } + + @Override + public CassandraPersistentEntity getExistingPersistentEntity(Class type) { + + CassandraPersistentEntity entity = entitiesByType.get(type); + if (entity != null) { + return entity; + } + + throw new IllegalArgumentException(String.format("unknown persistent type [%s]", type.getName())); + } + + @Override + public boolean contains(Class type) { + return entitiesByType.containsKey(type); + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java index 95a372c9f..11ab552b4 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java @@ -1,28 +1,54 @@ package org.springframework.data.cassandra.test.integration.mappingcontext; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext; +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; public class MappingContextIntegrationTests { public static class Transient { } + @Table + public static class X { + @PrimaryKey + String key; + } + + @Table + public static class Y { + @PrimaryKey + String key; + } + + DefaultCassandraMappingContext ctx = new DefaultCassandraMappingContext(); + @Test // TODO: (expected = MappingException.class) public void testGetPersistentEntityOfTransientType() { // TODO: when entity verification is added (DATACASS-85), this should throw a MappingException - - DefaultCassandraMappingContext ctx = new DefaultCassandraMappingContext(); CassandraPersistentEntity entity = ctx.getPersistentEntity(Transient.class); // TODO: remove following lines after DATACASS-85 assertNotNull(entity); assertEquals(Transient.class.getSimpleName().toLowerCase(), entity.getTableName()); } + + @Test + public void testGetExistingPersistentEntityHappyPath() { + + ctx.getPersistentEntity(X.class); + + assertTrue(ctx.contains(X.class)); + assertNotNull(ctx.getExistingPersistentEntity(X.class)); + assertFalse(ctx.contains(Y.class)); + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java new file mode 100644 index 000000000..eef313a63 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java @@ -0,0 +1,28 @@ +package org.springframework.data.cassandra.test.integration.minimal.config; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.test.integration.minimal.config.entities.AbsMin; +import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class AbsoluteMinimumXmlConfigIntegrationTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest { + + @Autowired + CassandraMappingContext context; + + @Test + public void test() { + + assertNotNull(context); + + context.getPersistentEntity(AbsMin.class); + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java new file mode 100644 index 000000000..cd49629c3 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java @@ -0,0 +1,12 @@ +package org.springframework.data.cassandra.test.integration.minimal.config.entities; + +import org.springframework.data.cassandra.mapping.PrimaryKey; +import org.springframework.data.cassandra.mapping.Table; + +@Table +public class AbsMin { + + @PrimaryKey + String key; + +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java new file mode 100644 index 000000000..ffcb0fc2f --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java @@ -0,0 +1,6 @@ +package org.springframework.data.cassandra.test.integration.minimal.config.entities; + +import org.springframework.data.cassandra.repository.CassandraRepository; + +public interface AbsMinRepository extends CassandraRepository { +} diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml new file mode 100644 index 000000000..7e2bc7eaa --- /dev/null +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + +