diff --git a/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java b/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java index 6f8cf192e..69697a228 100644 --- a/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java +++ b/src/main/java/org/springframework/data/convert/ReflectionEntityInstantiator.java @@ -17,6 +17,7 @@ package org.springframework.data.convert; import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.springframework.beans.BeanInstantiationException; @@ -60,7 +61,7 @@ public enum ReflectionEntityInstantiator implements EntityInstantiator { return BeanUtils.instantiateClass(entity.getType()); } } catch (BeanInstantiationException e) { - throw new MappingInstantiationException(e.getMessage(), e); + new MappingInstantiationException(entity, Collections.emptyList(), e); } } @@ -74,7 +75,7 @@ public enum ReflectionEntityInstantiator implements EntityInstantiator { try { return BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray()); } catch (BeanInstantiationException e) { - throw new MappingInstantiationException(e.getMessage(), e); + throw new MappingInstantiationException(entity, params, e); } } } diff --git a/src/main/java/org/springframework/data/mapping/model/MappingInstantiationException.java b/src/main/java/org/springframework/data/mapping/model/MappingInstantiationException.java index 7f8c88251..488967574 100644 --- a/src/main/java/org/springframework/data/mapping/model/MappingInstantiationException.java +++ b/src/main/java/org/springframework/data/mapping/model/MappingInstantiationException.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,21 +13,102 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.mapping.model; +import java.lang.reflect.Constructor; +import java.util.List; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PreferredConstructor; +import org.springframework.util.StringUtils; + /** - * @author Jon Brisbin + * Exception being thrown in case an entity could not be instantiated in the process of a to-object-mapping. + * + * @author Oliver Gierke + * @author Jon Brisbin */ public class MappingInstantiationException extends RuntimeException { + private static final long serialVersionUID = 822211065035487628L; + private static final String TEXT_TEMPLATE = "Failed to instantiate %s using constructor %s with arguments %s"; + + private final Class entityType; + private final Constructor constructor; + private final List constructorArguments; + + /** + * Creates a {@link MappingInstantiationException} using the given message and cause. + * + * @deprecated use {@link #MappingInstantiationException(PersistentEntity, List, String, Exception)} instead. + * @param message + * @param cause + */ + @Deprecated + public MappingInstantiationException(String message, Exception cause) { + this(null, null, message, cause); + } + + /** + * Creates a new {@link MappingInstantiationException} for the given {@link PersistentEntity}, constructor arguments + * and the causing exception. + * + * @param entity + * @param arguments + * @param cause + */ + public MappingInstantiationException(PersistentEntity entity, List arguments, Exception cause) { + this(entity, arguments, null, cause); + } + + private MappingInstantiationException(PersistentEntity entity, List arguments, String message, + Exception cause) { + + super(buildExceptionMessage(entity, arguments, null), cause); + + this.entityType = entity == null ? null : entity.getType(); + this.constructor = entity == null || entity.getPersistenceConstructor() == null ? null : entity + .getPersistenceConstructor().getConstructor(); + this.constructorArguments = arguments; + } + + private static final String buildExceptionMessage(PersistentEntity entity, List arguments, + String defaultMessage) { + + if (entity == null) { + return defaultMessage; + } + + PreferredConstructor constructor = entity.getPersistenceConstructor(); + + return String.format(TEXT_TEMPLATE, entity.getType().getName(), constructor == null ? "NO_CONSTRUCTOR" + : constructor.getConstructor().toString(), StringUtils.collectionToCommaDelimitedString(arguments)); + } + /** - * + * Returns the type of the entity that was attempted to instantiate. + * + * @return the entityType */ - private static final long serialVersionUID = 1L; + public Class getEntityType() { + return entityType; + } - public MappingInstantiationException(String s, Throwable throwable) { - super(s, throwable); + /** + * The constructor used during the instantiation attempt. + * + * @return the constructor + */ + public Constructor getConstructor() { + return constructor; } + /** + * The constructor arguments used to invoke the constructor. + * + * @return the constructorArguments + */ + public List getConstructorArguments() { + return constructorArguments; + } } diff --git a/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTests.java b/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTests.java index b352d944e..e843e8b27 100644 --- a/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTests.java +++ b/src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,17 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.springframework.data.convert.ReflectionEntityInstantiator.*; +import static org.springframework.data.util.ClassTypeInformation.*; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.convert.ReflectionEntityInstantiatorUnitTests.Outer.Inner; import org.springframework.data.mapping.PersistentEntity; @@ -32,9 +37,9 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PreferredConstructor.Parameter; import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.mapping.model.MappingInstantiationException; import org.springframework.data.mapping.model.ParameterValueProvider; import org.springframework.data.mapping.model.PreferredConstructorDiscoverer; -import org.springframework.data.util.ClassTypeInformation; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldCallback; @@ -92,7 +97,7 @@ public class ReflectionEntityInstantiatorUnitTests

entity = new BasicPersistentEntity(ClassTypeInformation.from(Inner.class)); + BasicPersistentEntity entity = new BasicPersistentEntity(from(Inner.class)); PreferredConstructor constructor = entity.getPersistenceConstructor(); Parameter parameter = constructor.getParameters().iterator().next(); @@ -114,6 +119,37 @@ public class ReflectionEntityInstantiatorUnitTests

entity = new BasicPersistentEntity(from(Sample.class)); + + when(provider.getParameterValue(Mockito.any(Parameter.class))).thenReturn("FOO"); + + Constructor constructor = Sample.class.getConstructor(Long.class, String.class); + List parameters = Arrays.asList((Object) "FOO", (Object) "FOO"); + + try { + INSTANCE.createInstance(entity, provider); + fail("Expected MappingInstantiationException!"); + } catch (MappingInstantiationException o_O) { + + assertThat(o_O.getConstructor(), is(constructor)); + assertThat(o_O.getConstructorArguments(), is(parameters)); + assertEquals(Sample.class, o_O.getEntityType()); + + assertThat(o_O.getMessage(), containsString(Sample.class.getName())); + assertThat(o_O.getMessage(), containsString(Long.class.getName())); + assertThat(o_O.getMessage(), containsString(String.class.getName())); + assertThat(o_O.getMessage(), containsString("FOO")); + System.out.println(o_O.getMessage()); + } + } + static class Foo { Foo(String foo) { @@ -127,4 +163,16 @@ public class ReflectionEntityInstantiatorUnitTests