diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java index a267e95c4..a6dde5c2e 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java @@ -63,11 +63,8 @@ public class BasicCassandraMappingContext implements CassandraMappingContext, ApplicationContextAware { protected ApplicationContext context; - protected ClassLoader beanClassLoader; - - protected CassandraPersistentEntityMetadataVerifier verifier = new BasicCassandraPersistentEntityMetadataVerifier(); - + protected CassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier(); protected Mapping mapping = new Mapping(); // useful caches 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 fbb4e9a1b..a12b39eb2 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 @@ -48,7 +48,7 @@ import org.springframework.util.StringUtils; public class BasicCassandraPersistentEntity extends BasicPersistentEntity implements CassandraPersistentEntity, ApplicationContextAware { - protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new BasicCassandraPersistentEntityMetadataVerifier(); + protected static final CassandraPersistentEntityMetadataVerifier DEFAULT_VERIFIER = new CompositeCassandraPersistentEntityMetadataVerifier(); protected CqlIdentifier tableName; protected CassandraMappingContext mappingContext; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifier.java index 2d4a03fdd..40ff69fb2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifier.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifier.java @@ -15,66 +15,50 @@ */ package org.springframework.data.cassandra.mapping; -import java.io.Serializable; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.annotation.Persistent; +import org.springframework.data.annotation.Id; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.MappingException; /** - * Default implementation for Cassandra Persistent Entity Verification. Ensures that annotated Persistent Entities will - * map properly to a Cassandra Table. + * Default implementation for Cassandra Persistent Entity Verification. Ensures that annotated + * {@link CassandraPersistentEntity entities} will map properly to a Cassandra Table. * * @author Matthew T Adams * @author David Webb * @author John Blum + * @author Mark Paluch + * @see Table + * @see PrimaryKey + * @see Id */ public class BasicCassandraPersistentEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier { - private static final Logger log = LoggerFactory.getLogger(BasicCassandraPersistentEntityMetadataVerifier.class); - - protected boolean strict = false; + @Deprecated protected boolean strict = false; + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity) + */ @Override - @SuppressWarnings("all") public void verify(CassandraPersistentEntity entity) throws MappingException { - if (entity.getType().isInterface()){ + if (entity.getType().isInterface() || entity.findAnnotation(Table.class) == null) { return; } - VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity, String.format( - "Mapping Exceptions from BasicCassandraPersistentEntityMetadataVerifier for %s", entity.getName())); + List exceptions = new ArrayList(); final List idProperties = new ArrayList(); - final List compositePrimaryKeys = new ArrayList(); final List partitionKeyColumns = new ArrayList(); - final List clusterKeyColumns = new ArrayList(); final List primaryKeyColumns = new ArrayList(); - Class entityType = entity.getType(); - - boolean isTable = (entityType.isAnnotationPresent(Table.class) - || entityType.isAnnotationPresent(Persistent.class)); - - boolean isPrimaryKeyClass = entityType.isAnnotationPresent(PrimaryKeyClass.class); - - // Ensure entity is not both a @Table(@Persistent) and a @PrimaryKey - if (isTable && isPrimaryKeyClass) { - exceptions.add(new MappingException("Entity cannot be of type Table and PrimaryKey")); - throw exceptions; - } - - // Ensure entity is either a @Table/@Persistent or a @PrimaryKey - if (!isTable && !isPrimaryKeyClass) { - exceptions.add(new MappingException( - "Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation")); - throw exceptions; + // Ensure entity is not both a @Table(@Persistent) and a @PrimaryKeyClass + if (entity.isCompositePrimaryKey()) { + exceptions.add(new MappingException(String.format("Entity cannot be of type @%s and @%s", + Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName()))); } // Parse entity properties @@ -85,10 +69,7 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra if (property.isIdProperty()) { idProperties.add(property); } else if (property.isClusterKeyColumn()) { - clusterKeyColumns.add(property); primaryKeyColumns.add(property); - } else if (property.isCompositePrimaryKey()) { - compositePrimaryKeys.add(property); } else if (property.isPartitionKeyColumn()) { partitionKeyColumns.add(property); primaryKeyColumns.add(property); @@ -96,145 +77,87 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra } }); - final int idPropertyCount = idProperties.size(); - final int partitionKeyColumnCount = partitionKeyColumns.size(); - final int primaryKeyColumnCount = primaryKeyColumns.size(); - - // Perform rules verification on PrimaryKeyClass - if (isPrimaryKeyClass) { - - // Must have at least 1 attribute annotated with @PrimaryKeyColumn - if (primaryKeyColumnCount == 0) { - exceptions.add(new MappingException(String.format( - "Composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(), - PrimaryKeyColumn.class.getSimpleName()))); - } - - // At least one of the PrimaryKeyColumns must have a type PARTIONED - if (partitionKeyColumnCount == 0) { - exceptions.add(new MappingException( - "At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED")); - } - - // Cannot have any Id or PrimaryKey Annotations - if (idPropertyCount > 0) { - exceptions.add(new MappingException( - "Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass")); - } - - // Ensure that PrimaryKeyColumn is a supported Type. - for (CassandraPersistentProperty property : primaryKeyColumns) { - if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) { - exceptions.add(new MappingException( - "Fields annotated with @PrimaryKeyColumn must be simple CassandraTypes")); - } - } - - // Ensure PrimaryKeyClass is Serializable - if (!Serializable.class.isAssignableFrom(entityType)) { - exceptions.add(new MappingException("@PrimaryKeyClass must be Serializable")); - } - - // Ensure PrimaryKeyClass only extends Object - if (!entityType.getSuperclass().equals(Object.class)) { - exceptions.add(new MappingException("@PrimaryKeyClass must only extend Object")); - } - - // Check that PrimaryKeyClass overrides "boolean equals(Object)" - verifyMethodPresent(entityType, "equals", "boolean equals(Object)", exceptions); - - // Ensure PrimaryKeyClass overrides "int hashCode()" - verifyMethodPresent(entityType, "hashCode", "int hashCode()", exceptions); - } - /* * Perform rules verification on Table/Persistent */ - if (isTable) { - // TODO Verify annotation values with CqlIndentifier + // TODO Verify annotation values with CqlIndentifier - // Ensure only one PK or at least one partitioned PK Column and not both PK(s) & PK Column(s) exist - if (primaryKeyColumnCount == 0) { - // Can only have one PK - if (idPropertyCount != 1) { - exceptions.add(new MappingException(String.format( - "@Table/@Persistent types must have only one @PrimaryKey attribute, if any; Found %s", - idPropertyCount))); + // Ensure only one PK or at least one partitioned PK Column and not both PK(s) & PK Column(s) exist + if (primaryKeyColumns.isEmpty()) { - throw exceptions; - } + // Can only have one PK + if (idProperties.size() != 1) { + exceptions + .add(new MappingException(String.format("@%s types must have only one primary attribute, if any; Found %s", + Table.class.getSimpleName(), idProperties.size()))); - // Ensure that Id is a supported Type. At this point there is only 1. - Class idType = idProperties.get(0).getType(); + fail(entity, exceptions); + } - if (!idType.isAnnotationPresent(PrimaryKeyClass.class) - && CassandraSimpleTypeHolder.getDataTypeFor(idType) == null) { + // Ensure that Id is a supported Type. At this point there is only 1. + CassandraPersistentProperty idProperty = idProperties.get(0); + Class idType = idProperty.getType(); - exceptions.add(new MappingException( - "Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type")); - } - } else if (idPropertyCount > 0) { - // Then we have both PK(s) & PK Column(s) + if (!idType.isAnnotationPresent(PrimaryKeyClass.class) + && CassandraSimpleTypeHolder.getDataTypeFor(idType) == null) { + + exceptions + .add(new MappingException(String.format("Property [%s] annotated with @%s must be a simple CassandraType", + idProperty.getName(), Id.class.getSimpleName()))); + } + } + + if (!idProperties.isEmpty() && !primaryKeyColumns.isEmpty()) { + + // Then we have both PK(s) & PK Column(s) + exceptions.add(new MappingException(String.format("@%s types must not define both @%s and @%s properties", + Table.class.getSimpleName(), Id.class.getSimpleName(), PrimaryKeyColumn.class.getSimpleName()))); + + fail(entity, exceptions); + } + + // We have no PKs & only PK Column(s); ensure at least one is of type PARTITIONED + if (!primaryKeyColumns.isEmpty() && partitionKeyColumns.isEmpty()) { + exceptions + .add(new MappingException(String.format("At least one of the @%s annotations must have a type of PARTITIONED", + PrimaryKeyColumn.class.getSimpleName()))); + } + + for (CassandraPersistentProperty property : primaryKeyColumns) { + if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) { exceptions.add(new MappingException(String.format( - "@Table/@Persistent types must not define both @PrimaryKeyColumn field(s) (found %s) and @PrimaryKey field(s) (found %s)", - primaryKeyColumnCount, idPropertyCount))); - - throw exceptions; - } else { - // We have no PKs & only PK Column(s); ensure at least one is of type PARTITIONED - if (partitionKeyColumnCount == 0) { - exceptions.add(new MappingException(String.format( - "@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED"))); - } + "Property [%s] annotated with @PrimaryKeyColumn must be a simple CassandraType", property.getName()))); } } // Determine whether or not to throw Exception based on errors found - if (exceptions.getCount() > 0) { - log.error("Exceptions while verifying PersistentEntity", exceptions); - throw exceptions; - } - } - - boolean verifyMethodPresent(Class type, String methodName, String methodDescription, - VerifierMappingExceptions exceptions) { - try { - Method method = type.getDeclaredMethod(methodName, Object.class); - - if (method == null || !method.getDeclaringClass().equals(type)) { - throw new NoSuchMethodException(); - } - - return true; - } catch (NoSuchMethodException e) { - String message = String.format( - "@PrimaryKeyClass should override '%s' method and use all @PrimaryKeyColumn fields", - methodDescription); - - if (strict) { - exceptions.add(new MappingException(message, e)); - } else { - log.warn(message); - } - - return false; + if (!exceptions.isEmpty()) { + fail(entity, exceptions); } } /** * @return the setting for strict. + * @deprecated Will be removed in future versions. */ @SuppressWarnings("unused") + @Deprecated public boolean isStrict() { return strict; } /** * @param strict boolean setting for strict. + * @deprecated Will be removed in future versions. */ @SuppressWarnings("unused") + @Deprecated public void setStrict(boolean strict) { this.strict = strict; } + + private static void fail(CassandraPersistentEntity entity, List exceptions) { + throw new VerifierMappingExceptions(entity, exceptions); + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifier.java new file mode 100644 index 000000000..3f5e420c4 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifier.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.mapping; + +import java.util.Arrays; +import java.util.Collection; + +import org.springframework.data.annotation.Persistent; +import org.springframework.data.mapping.model.MappingException; +import org.springframework.util.Assert; + +/** + * Composite {@link CassandraPersistentEntityMetadataVerifier} to verify persistent entities and primary key classes. + * + * @author Mark Paluch + * @since 1.5 + * @see BasicCassandraPersistentEntityMetadataVerifier + * @see PrimaryKeyClassEntityMetadataVerifier + */ +public class CompositeCassandraPersistentEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier { + + private Collection verifiers; + + /** + * Creates a new {@link CompositeCassandraPersistentEntityMetadataVerifier} using default entity and primary key + * verifiers. + * + * @see BasicCassandraPersistentEntityMetadataVerifier + * @see PrimaryKeyClassEntityMetadataVerifier + */ + public CompositeCassandraPersistentEntityMetadataVerifier() { + this(Arrays.asList(new PersistentAnnotationVerifier(), // + new PrimaryKeyClassEntityMetadataVerifier(), // + new BasicCassandraPersistentEntityMetadataVerifier())); + } + + /** + * Creates a new {@link CompositeCassandraPersistentEntityMetadataVerifier} for the given {@code verifiers} + * + * @param verifiers must not be {@literal null}. + */ + private CompositeCassandraPersistentEntityMetadataVerifier( + Collection verifiers) { + + Assert.notNull(verifiers, "Verifiers must not be null"); + + this.verifiers = verifiers; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity) + */ + @Override + public void verify(CassandraPersistentEntity entity) throws MappingException { + + for (CassandraPersistentEntityMetadataVerifier verifier : verifiers) { + verifier.verify(entity); + } + } + + /** + * {@link CassandraPersistentEntityMetadataVerifier} implementation that requires classes to be annotated with + * {@link Persistent}, {@link Table} or {@link PrimaryKeyClass}. + * + * @author Mark Paluch + */ + private static class PersistentAnnotationVerifier implements CassandraPersistentEntityMetadataVerifier { + + @Override + public void verify(CassandraPersistentEntity entity) throws MappingException { + + if (entity.getType().isInterface()) { + return; + } + + // Ensure entity is either a @Table/@Persistent or a @PrimaryKey + if (entity.findAnnotation(Persistent.class) == null) { + + VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity, + Arrays.asList(new MappingException(String.format( + "Cassandra entities must be annotated with either @%s, @%s, or @%s", Persistent.class.getSimpleName(), + Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName())))); + throw exceptions; + } + } + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java new file mode 100644 index 000000000..3d51b9b40 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifier.java @@ -0,0 +1,129 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.mapping; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.model.MappingException; + +/** + * {@link CassandraPersistentEntityMetadataVerifier} for {@link PrimaryKeyClass} entities. Ensures a valid mapping for + * composite primary keys. + * + * @author Mark Paluch + * @since 1.5 + * @see PrimaryKeyClass + */ +public class PrimaryKeyClassEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier { + + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntityMetadataVerifier#verify(org.springframework.data.cassandra.mapping.CassandraPersistentEntity) + */ + @Override + public void verify(CassandraPersistentEntity entity) throws MappingException { + + if (entity.getType().isInterface() || !entity.isCompositePrimaryKey()) { + return; + } + + List exceptions = new ArrayList(); + + final List idProperties = new ArrayList(); + final List compositePrimaryKeys = new ArrayList(); + final List partitionKeyColumns = new ArrayList(); + final List primaryKeyColumns = new ArrayList(); + + Class entityType = entity.getType(); + + // Ensure entity is not both a @Table(@Persistent) and a @PrimaryKey + if (entity.findAnnotation(Table.class) != null) { + exceptions.add(new MappingException(String.format("Entity cannot be of type @%s and @%s", + Table.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName()))); + } + + // Ensure PrimaryKeyClass only extends Object + if (!entityType.getSuperclass().equals(Object.class)) { + exceptions.add( + new MappingException(String.format("@%s must only extend Object", PrimaryKeyClass.class.getSimpleName()))); + } + + // Parse entity properties + entity.doWithProperties(new PropertyHandler() { + + @Override + public void doWithPersistentProperty(CassandraPersistentProperty property) { + if (property.isCompositePrimaryKey()) { + compositePrimaryKeys.add(property); + } else if (property.isIdProperty()) { + idProperties.add(property); + } else if (property.isClusterKeyColumn()) { + primaryKeyColumns.add(property); + } else if (property.isPartitionKeyColumn()) { + partitionKeyColumns.add(property); + primaryKeyColumns.add(property); + } + } + }); + + if (!compositePrimaryKeys.isEmpty()) { + exceptions + .add(new MappingException("Composite primary keys are not allowed inside of composite primary key classes")); + } + + // Must have at least 1 attribute annotated with @PrimaryKeyColumn + if (primaryKeyColumns.isEmpty()) { + exceptions + .add(new MappingException(String.format("Composite primary key type [%s] has no fields annotated with @%s", + entity.getType().getName(), PrimaryKeyColumn.class.getSimpleName()))); + } + + // At least one of the PrimaryKeyColumns must have a type PARTIONED + if (partitionKeyColumns.isEmpty()) { + exceptions + .add(new MappingException(String.format("At least one of the @%s annotations must have a type of PARTITIONED", + PrimaryKeyColumn.class.getSimpleName()))); + } + + // Cannot have any Id or PrimaryKey Annotations + if (!idProperties.isEmpty()) { + exceptions + .add(new MappingException(String.format("Annotations @%s and @%s are invalid for type annotated with @%s", + Id.class.getSimpleName(), PrimaryKey.class.getSimpleName(), PrimaryKeyClass.class.getSimpleName()))); + } + + // Ensure that PrimaryKeyColumn is a supported Type. + for (CassandraPersistentProperty property : primaryKeyColumns) { + if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) { + exceptions + .add(new MappingException(String.format("Property [%s] annotated with @%s must be a simple CassandraType", + property.getName(), PrimaryKeyColumn.class.getSimpleName()))); + } + } + + // Determine whether or not to throw Exception based on errors found + if (!exceptions.isEmpty()) { + fail(entity, exceptions); + } + } + + private static void fail(CassandraPersistentEntity entity, List exceptions) { + throw new VerifierMappingExceptions(entity, exceptions); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java index 7a0864216..d98d27f2f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors + * Copyright 2013-2016 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,35 +16,68 @@ package org.springframework.data.cassandra.mapping; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import org.springframework.data.mapping.model.MappingException; +import org.springframework.util.Assert; /** * Aggregator of multiple {@link MappingException} for convenience when verifying persistent entities. This allows the * framework to communicate all verification errors to the user of the framework, rather than one at a time. * * @author David Webb + * @author Mark Paluch */ @SuppressWarnings("serial") public class VerifierMappingExceptions extends MappingException { - Collection exceptions = new LinkedList(); - private String className; + final Collection exceptions; + private final String className; /** - * @param s + * Creates a new {@link VerifierMappingExceptions} for the given {@code entity} and message. + * + * @param entity must not be {@literal null}. + * @param exceptions must not be {@literal null}. + * @since 1.5 */ - public VerifierMappingExceptions(CassandraPersistentEntity entity, String s) { - super(s); + public VerifierMappingExceptions(CassandraPersistentEntity entity, Collection exceptions) { + + super(String.format("Mapping Exceptions for %s", entity.getName())); + + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + + this.exceptions = Collections.unmodifiableCollection(new LinkedList(exceptions)); this.className = entity.getType().getName(); } /** + * Creates a new {@link VerifierMappingExceptions} for the given {@code entity} and message. + * + * @param entity must not be {@literal null}. * @param s */ - public void add(MappingException e) { - exceptions.add(e); + public VerifierMappingExceptions(CassandraPersistentEntity entity, String s) { + super(s); + + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + + this.exceptions = new LinkedList(); + this.className = entity.getType().getName(); + } + + /** + * @param mappingException must not be {@literal null}. + * @deprecated Exceptions should be immutable so this method is subject to be removed in future versions + */ + @Deprecated + public void add(MappingException mappingException) { + + Assert.notNull(mappingException, "MappingException must not be null"); + + exceptions.add(mappingException); } /** @@ -53,7 +86,7 @@ public class VerifierMappingExceptions extends MappingException { * @return The Collection of MappingException */ public Collection getMappingExceptions() { - return exceptions; + return Collections.unmodifiableCollection(exceptions); } /** @@ -82,7 +115,7 @@ public class VerifierMappingExceptions extends MappingException { public String getMessage() { StringBuilder builder = new StringBuilder(className).append(":\n"); for (MappingException e : exceptions) { - builder.append(e.getMessage()).append("\n"); + builder.append(" - ").append(e.getMessage()).append("\n"); } return builder.toString(); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifierUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifierUnitTests.java index a1034c20c..561c8918e 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifierUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntityMetadataVerifierUnitTests.java @@ -15,21 +15,16 @@ */ package org.springframework.data.cassandra.mapping; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import java.io.Serializable; - import org.junit.Before; import org.junit.Test; -import org.slf4j.LoggerFactory; import org.springframework.cassandra.core.Ordering; import org.springframework.cassandra.core.PrimaryKeyType; import org.springframework.data.annotation.Id; import org.springframework.data.mapping.model.MappingException; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; - /** * Unit tests for {@link org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntityMetadataVerifier} * through {@link CassandraMappingContext} @@ -39,120 +34,181 @@ import ch.qos.logback.classic.LoggerContext; */ public class BasicCassandraPersistentEntityMetadataVerifierUnitTests { - private static LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - private Logger logger = loggerContext.getLogger(BasicCassandraPersistentEntityMetadataVerifier.class); - private CassandraMappingContext mappingContext; + private BasicCassandraPersistentEntityMetadataVerifier verifier = new BasicCassandraPersistentEntityMetadataVerifier(); + private BasicCassandraMappingContext context = new BasicCassandraMappingContext(); @Before - public void setUp() { - mappingContext = new BasicCassandraMappingContext(); - } - - @Test(expected = MappingException.class) - public void testNonPersistentType() { - mappingContext.getPersistentEntity(NonPersistentClass.class); - } - - @Test(expected = MappingException.class) - public void testTooManyAnnotations() { - mappingContext.getPersistentEntity(TooManyAnnotations.class); + public void setUp() throws Exception { + context.setVerifier(new NoOpVerifier()); } + /** + * @see DATACASS-258 + */ @Test - public void testNonPrimaryKeyClass() { - mappingContext.getPersistentEntity(Person.class); - - } - - @Test(expected = MappingException.class) - public void testPrimaryKeyClassNotFullyImplemented() { - mappingContext.getPersistentEntity(AnimalPkNoOverrides.class); + public void shouldAllowInterfaceTypes() { + verifier.verify(getEntity(MyInterface.class)); } + /** + * @see DATACASS-258 + */ @Test public void testPrimaryKeyClass() { - - mappingContext.getPersistentEntity(AnimalPK.class); - mappingContext.getPersistentEntity(Animal.class); + verifier.verify(getEntity(Animal.class)); } - @Test(expected = MappingException.class) - public void testNoPartitionKey() { - mappingContext.getPersistentEntity(NoPartitionKey.class); + /** + * @see DATACASS-258 + */ + @Test + public void testNonPrimaryKeyClass() { + verifier.verify(getEntity(Person.class)); } - @Test(expected = MappingException.class) + /** + * @see DATACASS-258 + */ + @Test + public void testNonPersistentType() { + verifier.verify(getEntity(NonPersistentClass.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() { + + try { + verifier.verify(getEntity(TooManyAnnotations.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithComplexTypePrimaryKey() { + + try { + verifier.verify(getEntity(EntityWithComplexTypePrimaryKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("Property [species] annotated with @PrimaryKeyColumn must be a simple CassandraType")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithComplexTypeId() { + + try { + verifier.verify(getEntity(EntityWithComplexTypeId.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("Property [species] annotated with @Id must be a simple CassandraType")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithoutPartitionKey() { + + try { + verifier.verify(getEntity(NoPartitionKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithoutPrimaryKey() { + + try { + verifier.verify(getEntity(NoPrimaryKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("@Table types must have only one primary attribute, if any; Found 0")); + } + } + + /** + * @see DATACASS-258 + */ + @Test public void testPkAndPkc() { - mappingContext.getPersistentEntity(PkAndPkc.class); + + try { + verifier.verify(getEntity(PrimaryKeyAndPrimaryKeyColumn.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("@Table types must not define both @Id and @PrimaryKeyColumn properties")); + } } - @Test - public void testOnePkc() { - - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(OnePkc.class); - - assertNull(entity.getIdProperty()); + private CassandraPersistentEntity getEntity(Class entityClass) { + return context.getPersistentEntity(entityClass); } - @Test - public void testMultiPkc() { - - CassandraPersistentEntity entity = mappingContext.getPersistentEntity(MultiPkc.class); - - assertNull(entity.getIdProperty()); - } + interface MyInterface {} static class NonPersistentClass { - @Id private String id; - - private String foo; - private String bar; + @Id String id; + String foo; + String bar; } @Table static class Person { - @Id private String id; - - private String firstName; - private String lastName; + @Id String id; + String firstName; + String lastName; } @Table static class Animal { @PrimaryKey AnimalPK key; - private String name; + String name; } @PrimaryKeyClass - static class AnimalPK implements Serializable { - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + static class AnimalPK { @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species; @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed; @PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color; - } - @PrimaryKeyClass - static class AnimalPkNoOverrides { + @Table + static class EntityWithComplexTypePrimaryKey { - @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species; - @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed; - @PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color; + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object species; + } + + @Table + static class EntityWithComplexTypeId { + + @Id Object species; } @Table @@ -160,28 +216,37 @@ public class BasicCassandraPersistentEntityMetadataVerifierUnitTests { static class TooManyAnnotations {} @Table - public static class NoPartitionKey { + static class NoPartitionKey { @PrimaryKeyColumn(ordinal = 0) String key; } @Table - public static class PkAndPkc { + static class NoPrimaryKey {} + + @Table + static class PrimaryKeyAndPrimaryKeyColumn { @PrimaryKey String primaryKey; @PrimaryKeyColumn(ordinal = 0) String primaryKeyColumn; } @Table - public static class OnePkc { + static class OnePrimaryKeyColumn { @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk; } @Table - public static class MultiPkc { + static class MultiplePrimaryKeyColumns { @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk0; @PrimaryKeyColumn(ordinal = 1) String pk1; } + + private static class NoOpVerifier implements CassandraPersistentEntityMetadataVerifier { + + @Override + public void verify(CassandraPersistentEntity entity) throws MappingException {} + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifierUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifierUnitTests.java new file mode 100644 index 000000000..71b25f736 --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/CompositeCassandraPersistentEntityMetadataVerifierUnitTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.mapping; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cassandra.core.Ordering; +import org.springframework.cassandra.core.PrimaryKeyType; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.model.MappingException; + +/** + * Unit tests for {@link CompositeCassandraPersistentEntityMetadataVerifier}. + * + * @author Mark Paluch + */ +public class CompositeCassandraPersistentEntityMetadataVerifierUnitTests { + + private CompositeCassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier(); + private BasicCassandraMappingContext context = new BasicCassandraMappingContext(); + + @Before + public void setUp() throws Exception { + context.setVerifier(verifier); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldAllowInterfaceTypes() { + verifier.verify(getEntity(MyInterface.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void testPrimaryKeyClass() { + verifier.verify(getEntity(Animal.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void testNonPrimaryKeyClass() { + verifier.verify(getEntity(Person.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithNonPersistentClasses() { + + try { + verifier.verify(getEntity(NonPersistentClass.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("Cassandra entities must be annotated with either @Persistent, @Table, or @PrimaryKeyClass")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() { + + try { + verifier.verify(getEntity(TooManyAnnotations.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass")); + } + } + + private CassandraPersistentEntity getEntity(Class entityClass) { + return context.getPersistentEntity(entityClass); + } + + interface MyInterface {} + + static class NonPersistentClass { + + @Id String id; + + String foo; + String bar; + } + + @Table + @PrimaryKeyClass + static class TooManyAnnotations {} + + @Table + static class Person { + + @Id String id; + + String firstName; + String lastName; + } + + @Table + static class Animal { + + @PrimaryKey AnimalPK key; + private String name; + } + + @PrimaryKeyClass + static class AnimalPK { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species; + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed; + @PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color; + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifierUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifierUnitTests.java new file mode 100644 index 000000000..6f2145eec --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/PrimaryKeyClassEntityMetadataVerifierUnitTests.java @@ -0,0 +1,295 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.mapping; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cassandra.core.Ordering; +import org.springframework.cassandra.core.PrimaryKeyType; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.model.MappingException; + +/** + * Unit tests for {@link PrimaryKeyClassEntityMetadataVerifier}. + * + * @author Mark Paluch + */ +public class PrimaryKeyClassEntityMetadataVerifierUnitTests { + + private PrimaryKeyClassEntityMetadataVerifier verifier = new PrimaryKeyClassEntityMetadataVerifier(); + private BasicCassandraMappingContext context = new BasicCassandraMappingContext(); + + @Before + public void setUp() throws Exception { + context.setVerifier(new NoOpVerifier()); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldAllowNonPersistentClasses() { + verifier.verify(getEntity(NonPersistentClass.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldAllowInterfaceTypes() { + verifier.verify(getEntity(MyInterface.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldAllowTableClass() { + verifier.verify(getEntity(Person.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldVerifyPrimaryKeyClass() { + verifier.verify(getEntity(AnimalPK.class)); + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithPersistentAndPrimaryKeyClassAnnotations() { + + try { + verifier.verify(getEntity(TooManyAnnotations.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("Entity cannot be of type @Table and @PrimaryKeyClass")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithoutPartitionKey() { + + try { + verifier.verify(getEntity(NoPartitionKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithoutPrimaryKey() { + + try { + verifier.verify(getEntity(NoPrimaryKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailOnPrimaryKeyCycles() { + + try { + verifier.verify(getEntity(TypeCycle.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("Composite primary keys are not allowed inside of composite primary key classes")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithNestedPrimaryKeyClassReference() { + + try { + verifier.verify(getEntity(PKClassWithNestedCompositeKey.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("Composite primary keys are not allowed inside of composite primary key classes")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithComplexType() { + + try { + verifier.verify(getEntity(PKWithComplexType.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("Property [species] annotated with @PrimaryKeyColumn must be a simple CassandraType")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailWithPrimaryKeyClassAndPrimaryKeyAnnotations() { + + try { + verifier.verify(getEntity(PrimaryKeyAndPrimaryKeyColumn.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), + containsString("Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass")); + } + } + + /** + * @see DATACASS-258 + */ + @Test + public void shouldFailForPrimaryKeyDerivedFromOtherThanObject() { + + try { + verifier.verify(getEntity(SubclassPK.class)); + fail("Missing MappingException"); + } catch (MappingException e) { + assertThat(e.toString(), containsString("@PrimaryKeyClass must only extend Object")); + } + } + + private CassandraPersistentEntity getEntity(Class entityClass) { + return context.getPersistentEntity(entityClass); + } + + interface MyInterface {} + + static class NonPersistentClass { + + @Id String id; + + String foo; + String bar; + } + + @Table + static class Person { + + @Id String id; + + String firstName; + String lastName; + } + + @Table + static class Animal { + + @PrimaryKey AnimalPK key; + private String name; + } + + @PrimaryKeyClass + static class AnimalPK { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species; + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED) String breed; + @PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color; + } + + @PrimaryKeyClass + static class PKWithComplexType { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object species; + } + + @Table + @PrimaryKeyClass + static class TooManyAnnotations {} + + @PrimaryKeyClass + static class NoPartitionKey { + + @PrimaryKeyColumn(ordinal = 0) String key; + } + + @PrimaryKeyClass + static class NoPrimaryKey { + + } + + @PrimaryKeyClass + static class TypeCycle { + + @PrimaryKey TypeCycle typeCycle; + } + + @PrimaryKeyClass + static class PKClassWithNestedCompositeKey { + + @PrimaryKey OnePrimaryKeyColumn pkc; + } + + @PrimaryKeyClass + static class PrimaryKeyAndPrimaryKeyColumn { + + @PrimaryKey String primaryKey; + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String primaryKeyColumn; + } + + @PrimaryKeyClass + static class OnePrimaryKeyColumn { + + @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk; + } + + static class MultiPrimaryKeyColumns { + + @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk0; + @PrimaryKeyColumn(ordinal = 1) String pk1; + } + + @PrimaryKeyClass + static class SubclassPK extends MultiPrimaryKeyColumns { + + @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0) String pk; + } + + private static class NoOpVerifier implements CassandraPersistentEntityMetadataVerifier { + + @Override + public void verify(CassandraPersistentEntity entity) throws MappingException {} + } +} diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptionsUnitTests.java new file mode 100644 index 000000000..23da7d62a --- /dev/null +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/mapping/VerifierMappingExceptionsUnitTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.mapping; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mapping.model.MappingException; + +/** + * Unit tests for {@link VerifierMappingExceptions}. + * + * @author Mark Paluch + */ +@RunWith(MockitoJUnitRunner.class) +public class VerifierMappingExceptionsUnitTests { + + @Mock CassandraPersistentEntity entityMock; + + @Before + @SuppressWarnings("unchecked") + public void setUp() throws Exception { + + when(entityMock.getType()).thenReturn((Class) VerifierMappingExceptionsUnitTests.class); + + } + + /** + * @see DATACASS-258 + */ + @Test + public void testDeprecatedMutability() { + + VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entityMock, "err"); + exceptions.add(new MappingException("my error")); + + assertThat(exceptions.toString(), containsString("my error")); + } +}