You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ju...@apache.org on 2014/10/03 20:09:32 UTC

svn commit: r1629277 - in /sling/trunk/bundles/extensions/models: api/src/main/java/org/apache/sling/models/factory/ impl/src/main/java/org/apache/sling/models/impl/ impl/src/test/java/org/apache/sling/models/impl/ impl/src/test/java/org/apache/sling/m...

Author: justin
Date: Fri Oct  3 18:09:32 2014
New Revision: 1629277

URL: http://svn.apache.org/r1629277
Log:
SLING-3709 - adding additional testing for missing elements and post construct failures

Added:
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/FailingPostConstuctModel.java
      - copied, changed from r1629167, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java
Modified:
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java Fri Oct  3 18:09:32 2014
@@ -25,7 +25,7 @@ package org.apache.sling.models.factory;
  * @see ModelFactory
  *
  */
-public class InvalidAdaptableException extends RuntimeException {
+public final class InvalidAdaptableException extends RuntimeException {
     private static final long serialVersionUID = -1209301268928038702L;
 
     public InvalidAdaptableException(String message) {

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java Fri Oct  3 18:09:32 2014
@@ -25,7 +25,7 @@ package org.apache.sling.models.factory;
  * 
  * @see ModelFactory
  */
-public class InvalidModelException extends RuntimeException {
+public final class InvalidModelException extends RuntimeException {
 
     private static final long serialVersionUID = 4323592065808565135L;
 

Modified: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java (original)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java Fri Oct  3 18:09:32 2014
@@ -21,7 +21,6 @@ package org.apache.sling.models.factory;
 import java.lang.reflect.AnnotatedElement;
 import java.util.Collection;
 
-
 /**
  * Exception which is triggered whenever a Sling Model cannot be instantiated
  * due to some missing elements (i.e. required fields/methods/constructor params
@@ -30,31 +29,31 @@ import java.util.Collection;
  * @see ModelFactory
  *
  */
-public class MissingElementsException extends RuntimeException {
+public final class MissingElementsException extends RuntimeException {
     private static final long serialVersionUID = 7870762030809272254L;
-    
+
     private final Collection<? extends AnnotatedElement> missingElements;
 
     private String formatString;
 
     private Class<?> type;
-    
+
     public MissingElementsException(String format, Collection<? extends AnnotatedElement> elements, Class<?> type) {
         super();
         this.formatString = format;
         this.missingElements = elements;
         this.type = type;
     }
-    
+
     @Override
     public String getMessage() {
         return String.format(formatString, missingElements, type);
     }
-    
+
     public Class<?> getType() {
         return type;
     }
-    
+
     public Collection<? extends AnnotatedElement> getMissingElements() {
         return missingElements;
     }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java Fri Oct  3 18:09:32 2014
@@ -166,7 +166,7 @@ public class ModelAdapterFactory impleme
 
     public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
         Result<AdapterType> result = internalCreateModel(adaptable, type);
-        result.logFailure(log);
+        result.logFailures(log);
         return result.getModel();
     }
 
@@ -174,12 +174,16 @@ public class ModelAdapterFactory impleme
     public <ModelType> ModelType createModel(Object adaptable, Class<ModelType> type) throws MissingElementsException,
             InvalidAdaptableException, InvalidModelException {
         Result<ModelType> result = internalCreateModel(adaptable, type);
-        result.throwException();
+        result.throwException(log);
         return result.getModel();
     }
 
     @Override
     public boolean canCreateFromAdaptable(Class<?> modelClass, Object adaptable) throws InvalidModelException {
+        return innerCanCreateFromAdaptable(modelClass, adaptable);
+    }
+
+    private static boolean innerCanCreateFromAdaptable(Class<?> modelClass, Object adaptable) throws InvalidModelException {
         Model modelAnnotation = modelClass.getAnnotation(Model.class);
         if (modelAnnotation == null) {
             throw new InvalidModelException(String.format("Model class '%s' does not have a model annotation", modelClass));
@@ -196,7 +200,11 @@ public class ModelAdapterFactory impleme
 
     @Override
     public boolean isModelClass(Class<?> modelClass) {
-        return modelClass.getAnnotation(Model.class) != null;
+        return innerIsModelClass(modelClass);
+    }
+
+    private static boolean innerIsModelClass(Class<?> clazz) {
+        return clazz.getAnnotation(Model.class) != null;
     }
 
     @SuppressWarnings("unchecked")
@@ -206,7 +214,7 @@ public class ModelAdapterFactory impleme
         if (threadInvocationCounter.isMaximumReached()) {
             String msg = String.format("Adapting %s to %s failed, too much recursive invocations (>=%s).",
                     new Object[] { adaptable, type, threadInvocationCounter.maxRecursionDepth });
-            result.setFailure(FailureType.OTHER, msg);
+            result.addFailure(FailureType.OTHER, msg);
             return result;
         };
         threadInvocationCounter.increase();
@@ -216,11 +224,10 @@ public class ModelAdapterFactory impleme
             if (implementationType != null) {
                 type = (Class<ModelType>) implementationType;
             }
-            result.setType(type);
 
             Model modelAnnotation = type.getAnnotation(Model.class);
             if (modelAnnotation == null) {
-                result.setFailure(FailureType.NO_MODEL_ANNOTATION);
+                result.addFailure(FailureType.NO_MODEL_ANNOTATION, type);
                 return result;
             }
             boolean isAdaptable = false;
@@ -232,7 +239,7 @@ public class ModelAdapterFactory impleme
                 }
             }
             if (!isAdaptable) {
-                result.setFailure(FailureType.ADAPTABLE_DOES_NOT_MATCH);
+                result.addFailure(FailureType.ADAPTABLE_DOES_NOT_MATCH, type);
             } else if (type.isInterface()) {
                 InvocationHandler handler = createInvocationHandler(adaptable, type, modelAnnotation, result);
                 if (handler != null) {
@@ -245,7 +252,7 @@ public class ModelAdapterFactory impleme
                     result.setModel(model);
                     return result;
                 } catch (Exception e) {
-                    result.setFailure(FailureType.OTHER, "Unable to create object", e);
+                    result.addFailure(FailureType.OTHER, "Unable to create object", e);
                 }
             }
             return result;
@@ -293,12 +300,13 @@ public class ModelAdapterFactory impleme
          * Is called each time when the given value should be injected into the given element
          * @param element
          * @param value
+         * @param result
          * @return true if injection was successful otherwise false
          */
-        public boolean inject(AnnotatedElement element, Object value);
+        public boolean inject(AnnotatedElement element, Object value, Result<?> result);
     }
 
-    private static class SetFieldCallback implements InjectCallback {
+    private class SetFieldCallback implements InjectCallback {
 
         private final Object object;
 
@@ -307,12 +315,12 @@ public class ModelAdapterFactory impleme
         }
 
         @Override
-        public boolean inject(AnnotatedElement element, Object value) {
-            return setField((Field) element, object, value);
+        public boolean inject(AnnotatedElement element, Object value, Result<?> result) {
+            return setField((Field) element, object, value, result);
         }
     }
 
-    private static class SetMethodsCallback implements InjectCallback {
+    private class SetMethodsCallback implements InjectCallback {
 
         private final Map<Method, Object> methods;
 
@@ -321,12 +329,12 @@ public class ModelAdapterFactory impleme
         }
 
         @Override
-        public boolean inject(AnnotatedElement element, Object value) {
-            return setMethod((Method) element, methods, value);
+        public boolean inject(AnnotatedElement element, Object value, Result<?> result) {
+            return setMethod((Method) element, methods, value, result);
         }
     }
 
-    private static class SetConstructorParameterCallback implements InjectCallback {
+    private class SetConstructorParameterCallback implements InjectCallback {
 
         private final List<Object> parameterValues;
 
@@ -335,14 +343,14 @@ public class ModelAdapterFactory impleme
         }
 
         @Override
-        public boolean inject(AnnotatedElement element, Object value) {
-            return setConstructorParameter((ConstructorParameter)element, parameterValues, value);
+        public boolean inject(AnnotatedElement element, Object value, Result<?> result) {
+            return setConstructorParameter((ConstructorParameter)element, parameterValues, value, result);
         }
     }
 
     private boolean injectElement(final AnnotatedElement element, final Object adaptable, final Type type,
             final boolean injectPrimitiveInitialValue, final Model modelAnnotation, final DisposalCallbackRegistry registry,
-            final InjectCallback callback) {
+            final InjectCallback callback, Result<?> result) {
 
         InjectAnnotationProcessor annotationProcessor = null;
         String source = getSource(element);
@@ -365,7 +373,7 @@ public class ModelAdapterFactory impleme
                 if (source == null || source.equals(injector.getName())) {
                     if (name != null || injector instanceof AcceptsNullName) {
                         Object value = injector.getValue(injectionAdaptable, name, type, element, registry);
-                        if (callback.inject(element, value)) {
+                        if (callback.inject(element, value, result)) {
                             wasInjectionSuccessful = true;
                             break;
                         }
@@ -375,14 +383,14 @@ public class ModelAdapterFactory impleme
         }
         // if injection failed, use default
         if (!wasInjectionSuccessful) {
-            wasInjectionSuccessful = injectDefaultValue(element, type, annotationProcessor, callback);
+            wasInjectionSuccessful = injectDefaultValue(element, type, annotationProcessor, callback, result);
         }
 
         // if default is not set, check if mandatory
         if (!wasInjectionSuccessful) {
             if (isOptional(element, modelAnnotation, annotationProcessor)) {
                 if (injectPrimitiveInitialValue) {
-                    injectPrimitiveInitialValue(element, type, callback);
+                    injectPrimitiveInitialValue(element, type, callback, result);
                 }
             } else {
                 return false;
@@ -409,13 +417,13 @@ public class ModelAdapterFactory impleme
             if (returnType != genericReturnType) {
                 isPrimitive = true;
             }
-            if (!injectElement(method, adaptable, returnType, isPrimitive, modelAnnotation, registry, callback)) {
+            if (!injectElement(method, adaptable, returnType, isPrimitive, modelAnnotation, registry, callback, result)) {
                 requiredMethods.add(method);
             }
         }
         registry.seal();
         if (!requiredMethods.isEmpty()) {
-            result.setFailure(FailureType.MISSING_METHODS, requiredMethods);
+            result.addFailure(FailureType.MISSING_METHODS, requiredMethods, type);
             return null;
         }
         return handler;
@@ -464,7 +472,7 @@ public class ModelAdapterFactory impleme
 
         Constructor<ModelType> constructorToUse = getBestMatchingConstructor(adaptable, type);
         if (constructorToUse == null) {
-            result.setFailure(FailureType.NO_USABLE_CONSTRUCTOR);
+            result.addFailure(FailureType.NO_USABLE_CONSTRUCTOR, type);
             return null;
         }
 
@@ -502,21 +510,24 @@ public class ModelAdapterFactory impleme
         Set<Field> injectableFields = collectInjectableFields(type);
         for (Field field : injectableFields) {
             Type fieldType = mapPrimitiveClasses(field.getGenericType());
-            if (!injectElement(field, adaptable, fieldType, false, modelAnnotation, registry, callback)) {
+            if (!injectElement(field, adaptable, fieldType, false, modelAnnotation, registry, callback, result)) {
                 requiredFields.add(field);
             }
         }
 
         registry.seal();
         if (!requiredFields.isEmpty()) {
-            result.setFailure(FailureType.MISSING_FIELDS, requiredFields);
+            result.addFailure(FailureType.MISSING_FIELDS, requiredFields, type);
             return null;
         }
         try {
             invokePostConstruct(object);
             return object;
-        } catch (Exception e) {
-            result.setFailure(FailureType.FAILED_CALLING_POST_CONSTRUCT, e);
+        } catch (InvocationTargetException e) {
+            result.addFailure(FailureType.FAILED_CALLING_POST_CONSTRUCT, e.getCause());
+            return null;
+        } catch (IllegalAccessException e) {
+            result.addFailure(FailureType.FAILED_CALLING_POST_CONSTRUCT, e);
             return null;
         }
 
@@ -575,12 +586,12 @@ public class ModelAdapterFactory impleme
             }
             ConstructorParameter constructorParameter = new ConstructorParameter(
                     constructor.getParameterAnnotations()[i], constructor.getParameterTypes()[i], genericType, i);
-            if (!injectElement(constructorParameter, adaptable, genericType, isPrimitive, modelAnnotation, registry, callback)) {
+            if (!injectElement(constructorParameter, adaptable, genericType, isPrimitive, modelAnnotation, registry, callback, result)) {
                 requiredParameters.add(constructorParameter);
             }
         }
         if (!requiredParameters.isEmpty()) {
-            result.setFailure(FailureType.MISSING_CONSTRUCTOR_PARAMS, requiredParameters);
+            result.addFailure(FailureType.MISSING_CONSTRUCTOR_PARAMS, requiredParameters, type);
             return null;
         }
         return constructor.newInstance(paramValues.toArray(new Object[paramValues.size()]));
@@ -602,11 +613,11 @@ public class ModelAdapterFactory impleme
     }
 
     private boolean injectDefaultValue(AnnotatedElement point, Type type, InjectAnnotationProcessor processor,
-            InjectCallback callback) {
+            InjectCallback callback, Result<?> result) {
 
         if (processor != null) {
             if (processor.hasDefault()) {
-                return callback.inject(point, processor.getDefault());
+                return callback.inject(point, processor.getDefault(), result);
             }
         }
         Default defaultAnnotation = point.getAnnotation(Default.class);
@@ -675,7 +686,7 @@ public class ModelAdapterFactory impleme
             log.warn("Cannot provide default for {}", type);
             return false;
         }
-        return callback.inject(point, value);
+        return callback.inject(point, value, result);
     }
 
     /**
@@ -685,8 +696,9 @@ public class ModelAdapterFactory impleme
      * @param point Annotated element
      * @param wrapperType Non-primitive wrapper class for primitive class
      * @param callback Inject callback
+     * @param result
      */
-    private void injectPrimitiveInitialValue(AnnotatedElement point, Type wrapperType, InjectCallback callback) {
+    private void injectPrimitiveInitialValue(AnnotatedElement point, Type wrapperType, InjectCallback callback, Result<?> result) {
         Type primitiveType = mapWrapperClasses(wrapperType);
         Object value = null;
         if (primitiveType == int.class) {
@@ -707,7 +719,7 @@ public class ModelAdapterFactory impleme
             value = Character.valueOf('\u0000');
         }
         if (value != null) {
-            callback.inject(point, value);
+            callback.inject(point, value, result);
         };
     }
     
@@ -783,7 +795,7 @@ public class ModelAdapterFactory impleme
         return true;
     }
 
-    private void invokePostConstruct(Object object) throws Exception {
+    private void invokePostConstruct(Object object) throws InvocationTargetException, IllegalAccessException {
         Class<?> clazz = object.getClass();
         List<Method> postConstructMethods = new ArrayList<Method>();
         while (clazz != null) {
@@ -827,9 +839,9 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private static boolean setField(Field field, Object createdObject, Object value) {
+    private boolean setField(Field field, Object createdObject, Object value, Result<?> result) {
         if (value != null) {
-            value = adaptIfNecessary(value, field.getType(), field.getGenericType());
+            value = adaptIfNecessary(value, field.getType(), field.getGenericType(), result);
             // value may now be null due to the adaptation done above
             if (value == null) {
                 return false;
@@ -854,9 +866,9 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private static boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
+    private boolean setMethod(Method method, Map<Method, Object> methods, Object value, Result<?> result) {
         if (value != null) {
-            value = adaptIfNecessary(value, method.getReturnType(), method.getGenericReturnType());
+            value = adaptIfNecessary(value, method.getReturnType(), method.getGenericReturnType(), result);
             // value may now be null due to the adaptation done above
             if (value == null) {
                 return false;
@@ -868,9 +880,9 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private static boolean setConstructorParameter(ConstructorParameter constructorParameter, List<Object> parameterValues, Object value) {
+    private boolean setConstructorParameter(ConstructorParameter constructorParameter, List<Object> parameterValues, Object value, Result<?> result) {
         if (value != null && constructorParameter.getType() instanceof Class<?>) {
-            value = adaptIfNecessary(value, (Class<?>) constructorParameter.getType(), constructorParameter.getGenericType());
+            value = adaptIfNecessary(value, (Class<?>) constructorParameter.getType(), constructorParameter.getGenericType(), result);
             // value may now be null due to the adaptation done above
             if (value == null) {
                 return false;
@@ -882,10 +894,18 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private static Object adaptIfNecessary(Object value, Class<?> type, Type genericType) {
+    private Object adaptIfNecessary(Object value, Class<?> type, Type genericType, Result<?> parentResult) {
         if (!isAcceptableType(type, genericType, value)) {
             Class<?> declaredType = type;
-            if (value instanceof Adaptable) {
+            if (isModelClass(type) && canCreateFromAdaptable(type, value)) {
+                Result<?> result = internalCreateModel(value, type);
+                if (result.getModel() == null) {
+                    parentResult.appendFailures(result);
+                    value = null;
+                } else {
+                    value = result.getModel();
+                }
+            } else if (value instanceof Adaptable) {
                 value = ((Adaptable) value).adaptTo(type);
             } else if (genericType instanceof ParameterizedType) {
                 ParameterizedType parameterizedType = (ParameterizedType) genericType;

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java Fri Oct  3 18:09:32 2014
@@ -19,6 +19,8 @@
 package org.apache.sling.models.impl;
 
 import java.lang.reflect.AnnotatedElement;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.sling.models.factory.InvalidAdaptableException;
@@ -33,9 +35,9 @@ public class Result<ModelType> {
                 "Failure calling post-construct method"), NO_MODEL_ANNOTATION(
                 "Provided Adapter class does not have a Model annotation"), NO_USABLE_CONSTRUCTOR(
                 "Unable to find a useable constructor"), OTHER("Unclassified problem"), MISSING_METHODS(
-                "Required methods %s on model interface %s were not able to be injected."), MISSING_FIELDS(
-                "Required fields %s on model interface %s were not able to be injected."), MISSING_CONSTRUCTOR_PARAMS(
-                "Required constructor parameteres %s on model interface %s were not able to be injected.");
+                "Required methods %s on model %s were not able to be injected."), MISSING_FIELDS(
+                "Required fields %s on model %s were not able to be injected."), MISSING_CONSTRUCTOR_PARAMS(
+                "Required constructor parameteres %s on model %s were not able to be injected.");
 
         private String message;
 
@@ -43,98 +45,146 @@ public class Result<ModelType> {
             this.message = msg;
         }
     }
+    
+    private static class Failure {
+        private FailureType failureType;
 
-    private Exception failureException;
+        private Throwable failureException;
 
-    private String failureMessage;
+        private String failureMessage;
 
-    private FailureType failureType;
+        private Set<? extends AnnotatedElement> missingElements;
 
-    private ModelType model;
+        private Class<?> clazz;
 
-    private Class<? extends ModelType> type;
+        private String getMessage() {
+            if (failureMessage != null) {
+                return failureMessage;
+            } else if (failureType != null) {
+                return failureType.message;
+            } else {
+                return null;
+            }
+        }
 
-    private Set<? extends AnnotatedElement> missingElements;
+        public void log(Logger log) {
 
-    public ModelType getModel() {
-        return model;
-    }
-
-    public void logFailure(Logger log) {
-        if (failureType != null) {
-            switch (failureType) {
-            case MISSING_CONSTRUCTOR_PARAMS:
-            case MISSING_FIELDS:
-            case MISSING_METHODS:
-                log.error(String.format(failureType.message, missingElements, type));
-                break;
-            default:
-                log.error(getMessage(), failureException);
-                break;
+            if (failureType != null) {
+                switch (failureType) {
+                case MISSING_CONSTRUCTOR_PARAMS:
+                case MISSING_FIELDS:
+                case MISSING_METHODS:
+                    log.error(String.format(failureType.message, missingElements, clazz));
+                    break;
+                default:
+                    log.error(getMessage(), failureException);
+                    break;
+                }
             }
         }
-    }
 
-    public void setFailure(FailureType type) {
-        this.failureType = type;
+        public void throwException() {
+            RuntimeException e = null;
+            if (failureType != null) {
+                final String msg = getMessage();
+                switch (failureType) {
+                case ADAPTABLE_DOES_NOT_MATCH:
+                    e = new InvalidAdaptableException(msg);
+                    break;
+                case FAILED_CALLING_POST_CONSTRUCT:
+                case NO_MODEL_ANNOTATION:
+                case NO_USABLE_CONSTRUCTOR:
+                    e = new InvalidModelException(msg);
+                    break;
+                case MISSING_CONSTRUCTOR_PARAMS:
+                case MISSING_FIELDS:
+                case MISSING_METHODS:
+                    e = new MissingElementsException(failureType.message, missingElements, clazz);
+                    break;
+                default:
+                    e = new RuntimeException(msg);
+                    break;
+                }
+            }
+            if (e != null) {
+                if (failureException != null) {
+                    e.initCause(failureException);
+                }
+                throw e;
+            }
+        }
+        
     }
 
-    public void setFailure(FailureType type, Exception e) {
-        this.failureType = type;
-        this.failureException = e;
-    }
+    private ModelType model;
+    
+    private List<Failure> failures = new ArrayList<Failure>();
 
-    public void setFailure(FailureType type, Set<? extends AnnotatedElement> requiredElements) {
-        this.failureType = type;
-        this.missingElements = requiredElements;
+    public ModelType getModel() {
+        return model;
     }
 
-    public void setFailure(FailureType type, String msg) {
-        this.failureType = type;
-        this.failureMessage = msg;
+    public void logFailures(Logger log) {
+        for (Failure failure : failures) {
+            failure.log(log);
+        }
     }
 
-    public void setFailure(FailureType type, String msg, Exception e) {
-        this.failureType = type;
-        this.failureMessage = msg;
-        this.failureException = e;
+    public void addFailure(FailureType type, Class<?> clazz) {
+        Failure f = new Failure();
+        f.failureType = type;
+        f.clazz = clazz;
+        failures.add(f);
+    }
+
+    public void addFailure(FailureType type, Throwable e) {
+        Failure f = new Failure();
+        f.failureType = type;
+        f.failureException = e;
+        failures.add(f);
+    }
+
+    public void addFailure(FailureType type, Set<? extends AnnotatedElement> requiredElements, Class<?> clazz) {
+        Failure f = new Failure();
+        f.failureType = type;
+        f.missingElements = requiredElements;
+        f.clazz = clazz;
+        failures.add(f);
+    }
+
+    public void addFailure(FailureType type, String msg) {
+        Failure f = new Failure();
+        f.failureType = type;
+        f.failureMessage = msg;
+        failures.add(f);
+    }
+
+    public void addFailure(FailureType type, String msg, Throwable e) {
+        Failure f = new Failure();
+        f.failureType = type;
+        f.failureMessage = msg;
+        f.failureException = e;
+        failures.add(f);
     }
 
     public void setModel(ModelType model) {
         this.model = model;
     }
-
+/*
     public void setType(Class<? extends ModelType> type) {
         this.type = type;
     }
-
-    public void throwException() {
-        if (failureType != null) {
-            final String msg = getMessage();
-            switch (failureType) {
-            case ADAPTABLE_DOES_NOT_MATCH:
-                throw new InvalidAdaptableException(msg);
-            case FAILED_CALLING_POST_CONSTRUCT:
-            case NO_MODEL_ANNOTATION:
-            case NO_USABLE_CONSTRUCTOR:
-                throw new InvalidModelException(msg);
-            case MISSING_CONSTRUCTOR_PARAMS:
-            case MISSING_FIELDS:
-            case MISSING_METHODS:
-                throw new MissingElementsException(failureType.message, missingElements, type);
-            case OTHER:
-                throw new RuntimeException(msg);
-            }
+*/
+    public void throwException(Logger log) {
+        for (int i = 0; i < failures.size() - 1; i++) {
+            failures.get(i).log(log);
+        }
+        if (failures.size() >= 1) {
+            failures.get(failures.size() - 1).throwException();
         }
     }
 
-    private String getMessage() {
-        if (failureMessage != null) {
-            return failureMessage;
-        } else if (failureType != null) {
-            return failureType.message;
-        } else {
-            return null;
-        }
+    public void appendFailures(Result<?> result) {
+        failures.addAll(result.failures);
     }
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java Fri Oct  3 18:09:32 2014
@@ -22,6 +22,8 @@ import static org.mockito.Mockito.*;
 import java.util.Hashtable;
 
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.factory.InvalidModelException;
+import org.apache.sling.models.testmodels.classes.FailingPostConstuctModel;
 import org.apache.sling.models.testmodels.classes.SubClass;
 import org.apache.sling.models.testmodels.classes.SubClassOverriddenPostConstruct;
 import org.junit.Before;
@@ -41,33 +43,49 @@ public class PostConstructTest {
     @Mock
     private BundleContext bundleContext;
 
+    @Mock
+    private Resource resource;
+
+    ModelAdapterFactory factory = new ModelAdapterFactory();
+
     @Before
     public void setup() {
         when(componentCtx.getBundleContext()).thenReturn(bundleContext);
         when(componentCtx.getProperties()).thenReturn(new Hashtable<String, Object>());
+        factory.activate(componentCtx);
+        // no injectors are necessary
     }
 
     @Test
     public void testClassOrder() {
-        Resource r = mock(Resource.class);
-        ModelAdapterFactory factory = new ModelAdapterFactory();
-        factory.activate(componentCtx);
-        // no injectors are necessary
-
-        SubClass sc = factory.getAdapter(r, SubClass.class);
+        SubClass sc = factory.getAdapter(resource, SubClass.class);
         assertTrue(sc.getPostConstructCalledTimestampInSub() > sc.getPostConstructCalledTimestampInSuper());
         assertTrue(sc.getPostConstructCalledTimestampInSuper() > 0);
     }
-    
+
     @Test
     public void testOverriddenPostConstruct() {
-        Resource r = mock(Resource.class);
-        ModelAdapterFactory factory = new ModelAdapterFactory();
-        factory.activate(componentCtx);
-        // no injectors are necessary
-
-        SubClassOverriddenPostConstruct sc = factory.getAdapter(r, SubClassOverriddenPostConstruct.class);
+        SubClassOverriddenPostConstruct sc = factory.getAdapter(resource, SubClassOverriddenPostConstruct.class);
         assertEquals("Post construct not called exactly one time in sub class!", 1, sc.getPostConstructorCalledCounter());
         assertEquals("Post construct was called on super class although overridden in sub class", 0, sc.getPostConstructCalledTimestampInSuper());
     }
+
+    @Test
+    public void testPostConstructMethodWhichThrowsException() {
+        FailingPostConstuctModel model = factory.getAdapter(resource, FailingPostConstuctModel.class);
+        assertNull(model);
+    }
+
+    @Test
+    public void testPostConstructMethodWhichThrowsExceptionThrowingException() {
+        boolean thrown = false;
+        try {
+            factory.createModel(resource, FailingPostConstuctModel.class);
+        } catch (InvalidModelException e) {
+            assertTrue(e.getMessage().contains("post-construct"));
+            assertEquals("FAIL", e.getCause().getMessage());
+            thrown = true;
+        }
+        assertTrue(thrown);
+    }
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java?rev=1629277&r1=1629276&r2=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java Fri Oct  3 18:09:32 2014
@@ -19,6 +19,7 @@ package org.apache.sling.models.impl;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
+import java.lang.reflect.Field;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -29,6 +30,7 @@ import org.apache.commons.lang.RandomStr
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.factory.MissingElementsException;
 import org.apache.sling.models.impl.injectors.ChildResourceInjector;
 import org.apache.sling.models.impl.injectors.ValueMapInjector;
 import org.apache.sling.models.testmodels.classes.ArrayPrimitivesModel;
@@ -180,6 +182,29 @@ public class ResourceModelClassesTest {
     }
 
     @Test
+    public void testRequiredPropertyModelWithException() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("first", "first-value");
+        map.put("third", "third-value");
+        ValueMap vm = spy(new ValueMapDecorator(map));
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        boolean thrown = false;
+        try {
+            factory.createModel(res, ResourceModelWithRequiredField.class);
+        } catch (MissingElementsException e) {
+            assertEquals(ResourceModelWithRequiredField.class, e.getType());
+            assertEquals("required", ((Field) e.getMissingElements().iterator().next()).getName());
+            thrown = true;
+        }
+        assertTrue(thrown);
+
+        verify(vm).get("required", String.class);
+    }
+
+    @Test
     public void testRequiredPropertyModelOptionalStrategyAvailable() {
         Map<String, Object> map = new HashMap<String, Object>();
         map.put("first", "first-value");

Copied: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/FailingPostConstuctModel.java (from r1629167, sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java)
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/FailingPostConstuctModel.java?p2=sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/FailingPostConstuctModel.java&p1=sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java&r1=1629167&r2=1629277&rev=1629277&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/FailingPostConstuctModel.java Fri Oct  3 18:09:32 2014
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.models.factory;
+package org.apache.sling.models.testmodels.classes;
 
-/**
- * Exception which is triggered whenever a Sling Model could not be
- * instantiated because it could not be adapted from the given adaptable.
- * 
- * @see ModelFactory
- *
- */
-public class InvalidAdaptableException extends RuntimeException {
-    private static final long serialVersionUID = -1209301268928038702L;
+import javax.annotation.PostConstruct;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
 
-    public InvalidAdaptableException(String message) {
-        super(message);
+@Model(adaptables=Resource.class)
+public class FailingPostConstuctModel {
+
+    @PostConstruct
+    protected void pc() throws Exception {
+        throw new Exception("FAIL");
     }
+
 }