DATACASS-91 - now defaulting mapping context, converter, & template if not defined in XML

This commit is contained in:
Matthew Adams
2014-02-12 18:32:08 -06:00
parent a031ff77fe
commit eb68f6f555
17 changed files with 605 additions and 9 deletions

View File

@@ -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:
* <p/>
* {@link BeanDefinitionBuilder#addConstructorArgReference(String)},
* {@link BeanDefinitionBuilder#addConstructorArgValue(Object)},
* {@link BeanDefinitionBuilder#addPropertyReference(String, String)}, and
* {@link BeanDefinitionBuilder#addPropertyValue(String, Object)}.
* <p/>
* 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 <code>true</code>. 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 <code>false</code>. 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;
}
}

View File

@@ -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 <code>null</code>, 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
* <code>null</code>.
* @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
* <code>required</code> was <code>false</code>, otherwise throws {@link IllegalArgumentException}.
*
* @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the <code>factor</code>
* parameter.
* @param factory The {@link ListableBeanFactory}, often the very same instance as the <code>registry</code>
* parameter.
* @param type The required {@link BeanDefinition}'s type.
* @param includeNonSingletons Whether to include beans with scope other than <code>singleton</code>
* @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 <code>required</code>.
* @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 <code>factor</code>
* parameter.
* @param factory The {@link ListableBeanFactory}, often the very same instance as the <code>registry</code>
* parameter.
* @param type The required {@link BeanDefinition}'s type.
* @param includeNonSingletons Whether to include beans with scope other than <code>singleton</code>
* @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;
}
}

View File

@@ -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:
* <ul>
* <li>{@link CassandraOperations} via {@link CassandraTemplate}</li>
* <li>{@link CassandraMappingContext} via {@link DefaultCassandraMappingContext}</li>
* <li> {@link CassandraConverter} via {@link MappingCassandraConverter}</li>
* </ul>
* <p/>
* 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}.
* <p/>
* 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.
* <p/>
* 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;
}
}

View File

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

View File

@@ -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 &lt;cluster&gt; 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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -61,6 +61,7 @@ public class DefaultCassandraMappingContext extends
protected Map<String, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<String, Set<CassandraPersistentEntity<?>>>();
protected Set<CassandraPersistentEntity<?>> nonPrimaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Map<Class<?>, CassandraPersistentEntity<?>> entitiesByType = new HashMap<Class<?>, 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);
}
}

View File

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

View File

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

View File

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

View File

@@ -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<AbsMin, String> {
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cass="http://www.springframework.org/schema/data/cassandra"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- this is needed just so it runs correctly as part of the build, so ignore it & the port attribute of the cluster below -->
<context:property-placeholder
location="classpath:/spring-data-cassandra-build.properties" />
<cass:cluster port="${cassandra.native_transport_port}" />
<cass:session keyspace-name="system" />
<cass:repositories
base-package="org.springframework.data.cassandra.test.integration.minimal.config" />
</beans>