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/01 22:04:25 UTC

svn commit: r1628824 - 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/

Author: justin
Date: Wed Oct  1 20:04:24 2014
New Revision: 1628824

URL: http://svn.apache.org/r1628824
Log:
SLING-3709 - introduce new ModelFactory service interface which throws various exceptions based on failure conditions. Thanks to Konrad Windszus for the original patch!

Added:
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/
    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/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java
Modified:
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ConstructorTest.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InvalidAdaptationsTest.java

Added: 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=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidAdaptableException.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.models.factory;
+
+/**
+ * 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;
+
+    public InvalidAdaptableException(String message) {
+        super(message);
+    }
+}

Added: 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=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/InvalidModelException.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.models.factory;
+
+/**
+ * Exception which is triggered when the Model could not be instantiated due to
+ * model class is not having a model annotation, some reflection error, invalid constructors or 
+ * some exception within the post construct method was triggered.
+ * 
+ * @see ModelFactory
+ */
+public class InvalidModelException extends RuntimeException {
+
+    private static final long serialVersionUID = 4323592065808565135L;
+
+    public InvalidModelException(String message) {
+        super(message);
+    }
+}

Added: 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=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/MissingElementsException.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.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
+ * could not be injected).
+ * 
+ * @see ModelFactory
+ *
+ */
+public 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;
+    }
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java?rev=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/ModelFactory.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.models.factory;
+
+/**
+ * The ModelFactory instantiates Sling Model classes similar to adaptTo but is allowed to throw an exception in case
+ * instantiation fails for some reason.
+ *
+ */
+public interface ModelFactory {
+    /**
+     * Instantiates the given Sling Model class from the given adaptable
+     * @param adaptable the adaptable to use to instantiate the Sling Model Class
+     * @param type the class to instantiate
+     * @return a new instance for the required model (never null)
+     * @throws MissingElementsException in case no injector was able to inject some required values with the given types
+     * @throws InvalidAdaptableException in case the given class cannot be instantiated from the given adaptable (different adaptable on the model annotation)
+     * @throws InvalidModelException in case the model could not be instanciated because model annotation was missing, reflection failed, no valid constructor was found or post-construct has thrown an error
+     */
+    public <ModelType> ModelType createModel(Object adaptable, Class<ModelType> type) throws MissingElementsException,
+            InvalidAdaptableException, InvalidModelException;
+
+    /**
+     * 
+     * @param modelClass the class to check
+     * @param adaptable the adaptable to check
+     * @return false in case the given class can not be adapted from the given adaptable
+     * @throws InvalidModelException in case the given class does not have a model annotation
+     */
+    public boolean canCreateFromAdaptable(Class<?> modelClass, Object adaptable) throws InvalidModelException;
+
+    /**
+     * 
+     * @param modelClass the class to check
+     * @return false in case the given class has no model annotation
+     * 
+     * @see org.apache.sling.models.annotations.Model
+     */
+    public boolean isModelClass(Class<?> modelClass);
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java?rev=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/factory/package-info.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+@Version("1.0.0")
+package org.apache.sling.models.factory;
+
+import aQute.bnd.annotation.Version;
\ No newline at end of file

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=1628824&r1=1628823&r2=1628824&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 Wed Oct  1 20:04:24 2014
@@ -58,6 +58,7 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.adapter.Adaptable;
 import org.apache.sling.api.adapter.AdapterFactory;
 import org.apache.sling.commons.osgi.PropertiesUtil;
@@ -69,6 +70,11 @@ import org.apache.sling.models.annotatio
 import org.apache.sling.models.annotations.Required;
 import org.apache.sling.models.annotations.Source;
 import org.apache.sling.models.annotations.Via;
+import org.apache.sling.models.factory.InvalidAdaptableException;
+import org.apache.sling.models.factory.InvalidModelException;
+import org.apache.sling.models.factory.ModelFactory;
+import org.apache.sling.models.factory.MissingElementsException;
+import org.apache.sling.models.impl.Result.FailureType;
 import org.apache.sling.models.spi.AcceptsNullName;
 import org.apache.sling.models.spi.DisposalCallback;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
@@ -84,8 +90,9 @@ import org.osgi.service.component.Compon
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@Component(metatype = true)
-public class ModelAdapterFactory implements AdapterFactory, Runnable {
+@Component(metatype = true, immediate = true)
+@Service(value = ModelFactory.class)
+public class ModelAdapterFactory implements AdapterFactory, Runnable, ModelFactory {
 
     private static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry {
 
@@ -157,25 +164,64 @@ public class ModelAdapterFactory impleme
     // Use threadlocal to count recursive invocations and break recursing if a max. limit is reached (to avoid cyclic dependencies)
     private ThreadLocal<ThreadInvocationCounter> invocationCountThreadLocal;
 
-    @SuppressWarnings("unchecked")
     public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+        Result<AdapterType> result = internalCreateModel(adaptable, type);
+        result.logFailure(log);
+        return result.getModel();
+    }
+
+    @Override
+    public <ModelType> ModelType createModel(Object adaptable, Class<ModelType> type) throws MissingElementsException,
+            InvalidAdaptableException, InvalidModelException {
+        Result<ModelType> result = internalCreateModel(adaptable, type);
+        result.throwException();
+        return result.getModel();
+    }
+
+    @Override
+    public boolean canCreateFromAdaptable(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));
+        }
+
+        Class<?>[] declaredAdaptable = modelAnnotation.adaptables();
+        for (Class<?> clazz : declaredAdaptable) {
+            if (clazz.isInstance(adaptable)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isModelClass(Class<?> modelClass) {
+        return modelClass.getAnnotation(Model.class) != null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <ModelType> Result<ModelType> internalCreateModel(Object adaptable, Class<ModelType> type) {
+        Result<ModelType> result = new Result<ModelType>();
         ThreadInvocationCounter threadInvocationCounter = invocationCountThreadLocal.get();
         if (threadInvocationCounter.isMaximumReached()) {
-            log.error("Adapting {} to {} failed, too much recursive invocations (>={}).",
+            String msg = String.format("Adapting %s to %s failed, too much recursive invocations (>=%s).",
                     new Object[] { adaptable, type, threadInvocationCounter.maxRecursionDepth });
-            return null;
+            result.setFailure(FailureType.OTHER, msg);
+            return result;
         };
         threadInvocationCounter.increase();
         try {
             // check if a different implementation class was registered for this adapter type
             Class<?> implementationType = this.adapterImplementations.lookup(type, adaptable);
             if (implementationType != null) {
-                type = (Class<AdapterType>) implementationType;
+                type = (Class<ModelType>) implementationType;
             }
+            result.setType(type);
 
             Model modelAnnotation = type.getAnnotation(Model.class);
             if (modelAnnotation == null) {
-                return null;
+                result.setFailure(FailureType.NO_MODEL_ANNOTATION);
+                return result;
             }
             boolean isAdaptable = false;
 
@@ -186,24 +232,23 @@ public class ModelAdapterFactory impleme
                 }
             }
             if (!isAdaptable) {
-                return null;
-            }
-
-            if (type.isInterface()) {
-                InvocationHandler handler = createInvocationHandler(adaptable, type, modelAnnotation);
+                result.setFailure(FailureType.ADAPTABLE_DOES_NOT_MATCH);
+            } else if (type.isInterface()) {
+                InvocationHandler handler = createInvocationHandler(adaptable, type, modelAnnotation, result);
                 if (handler != null) {
-                    return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
-                } else {
-                    return null;
+                    ModelType model = (ModelType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
+                    result.setModel(model);
                 }
             } else {
                 try {
-                    return createObject(adaptable, type, modelAnnotation);
+                    ModelType model = createObject(adaptable, type, modelAnnotation, result);
+                    result.setModel(model);
+                    return result;
                 } catch (Exception e) {
-                    log.error("unable to create object", e);
-                    return null;
+                    result.setFailure(FailureType.OTHER, "Unable to create object", e);
                 }
             }
+            return result;
         } finally {
             threadInvocationCounter.decrease();
         }
@@ -314,11 +359,11 @@ public class ModelAdapterFactory impleme
         String name = getName(element, annotationProcessor);
         Object injectionAdaptable = getAdaptable(adaptable, element, annotationProcessor);
 
-        // find the right injector
-        for (Injector injector : sortedInjectors) {
-            if (source == null || source.equals(injector.getName())) {
-                if (name != null || injector instanceof AcceptsNullName) {
-                    if (injectionAdaptable != null) {
+        if (injectionAdaptable != null) {
+            // find the right injector
+            for (Injector injector : sortedInjectors) {
+                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)) {
                             wasInjectionSuccessful = true;
@@ -347,7 +392,7 @@ public class ModelAdapterFactory impleme
         return true;
     }
 
-    private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type, Model modelAnnotation) {
+    private <ModelType> InvocationHandler createInvocationHandler(final Object adaptable, final Class<ModelType> type, final Model modelAnnotation, final Result<ModelType> result) {
         Set<Method> injectableMethods = collectInjectableMethods(type);
         final Map<Method, Object> methods = new HashMap<Method, Object>();
         SetMethodsCallback callback = new SetMethodsCallback(methods);
@@ -370,7 +415,7 @@ public class ModelAdapterFactory impleme
         }
         registry.seal();
         if (!requiredMethods.isEmpty()) {
-            log.warn("Required methods {} on model interface {} were not able to be injected.", requiredMethods, type);
+            result.setFailure(FailureType.MISSING_METHODS, requiredMethods);
             return null;
         }
         return handler;
@@ -413,17 +458,17 @@ public class ModelAdapterFactory impleme
         return null;
     }
 
-    private <AdapterType> AdapterType createObject(Object adaptable, Class<AdapterType> type, Model modelAnnotation)
+    private <ModelType> ModelType createObject(final Object adaptable, final Class<ModelType> type, final Model modelAnnotation, final Result<ModelType> result)
             throws InstantiationException, InvocationTargetException, IllegalAccessException {
         DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl();
 
-        Constructor<AdapterType> constructorToUse = getBestMatchingConstructor(adaptable, type);
+        Constructor<ModelType> constructorToUse = getBestMatchingConstructor(adaptable, type);
         if (constructorToUse == null) {
-            log.warn("Model class {} does not have a usable constructor", type.getName());
+            result.setFailure(FailureType.NO_USABLE_CONSTRUCTOR);
             return null;
         }
 
-        final AdapterType object;
+        final ModelType object;
         if (constructorToUse.getParameterTypes().length == 0) {
             // no parameters for constructor injection? instantiate it right away
             object = constructorToUse.newInstance();
@@ -431,7 +476,11 @@ public class ModelAdapterFactory impleme
             // instantiate with constructor injection
             // if this fails, make sure resources that may be claimed by injectors are cleared up again
             try {
-                object = newInstanceWithConstructorInjection(constructorToUse, adaptable, type, modelAnnotation, registry);
+                object = newInstanceWithConstructorInjection(constructorToUse, adaptable, type, modelAnnotation, registry, result);
+                if (object == null) {
+                    registry.onDisposed();
+                    return null;
+                }
             } catch (InstantiationException ex) {
                 registry.onDisposed();
                 throw ex;
@@ -444,11 +493,6 @@ public class ModelAdapterFactory impleme
             }
         }
 
-        if (object == null) {
-            registry.onDisposed();
-            return null;
-        }
-
         registerCallbackRegistry(object, registry);
 
         InjectCallback callback = new SetFieldCallback(object);
@@ -465,14 +509,14 @@ public class ModelAdapterFactory impleme
 
         registry.seal();
         if (!requiredFields.isEmpty()) {
-            log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields, type);
+            result.setFailure(FailureType.MISSING_FIELDS, requiredFields);
             return null;
         }
         try {
             invokePostConstruct(object);
             return object;
         } catch (Exception e) {
-            log.error("Unable to invoke post construct method.", e);
+            result.setFailure(FailureType.FAILED_CALLING_POST_CONSTRUCT, e);
             return null;
         }
 
@@ -485,7 +529,7 @@ public class ModelAdapterFactory impleme
      * @return Constructor or null if none found
      */
     @SuppressWarnings("unchecked")
-    private <AdapterType> Constructor<AdapterType> getBestMatchingConstructor(Object adaptable, Class<AdapterType> type) {
+    private <ModelType> Constructor<ModelType> getBestMatchingConstructor(Object adaptable, Class<ModelType> type) {
         Constructor<?>[] constructors = type.getConstructors();
 
         // sort the constructor list in order from most params to least params, and constructors with @Inject annotation first
@@ -494,7 +538,7 @@ public class ModelAdapterFactory impleme
         for (Constructor<?> constructor : constructors) {
             // first try to find the constructor with most parameters and @Inject annotation
             if (constructor.isAnnotationPresent(Inject.class)) {
-                return (Constructor<AdapterType>) constructor;
+                return (Constructor<ModelType>) constructor;
             }
             // compatibility mode for sling models implementation <= 1.0.6:
             // support constructor without @Inject if it has exactly one parameter matching the adaptable class
@@ -502,19 +546,20 @@ public class ModelAdapterFactory impleme
             if (paramTypes.length == 1) {
                 Class<?> paramType = constructor.getParameterTypes()[0];
                 if (paramType.isInstance(adaptable)) {
-                    return (Constructor<AdapterType>) constructor;
+                    return (Constructor<ModelType>) constructor;
                 }
             }
             // if no constructor for injection found use public constructor without any params
             if (constructor.getParameterTypes().length == 0) {
-                return (Constructor<AdapterType>) constructor;
+                return (Constructor<ModelType>) constructor;
             }
         }
         return null;
     }
 
-    private <AdapterType> AdapterType newInstanceWithConstructorInjection(Constructor<AdapterType> constructor, Object adaptable,
-            Class<AdapterType> type, Model modelAnnotation, DisposalCallbackRegistry registry)
+    private <ModelType> ModelType newInstanceWithConstructorInjection(final Constructor<ModelType> constructor, final Object adaptable,
+            final Class<ModelType> type, final Model modelAnnotation, final DisposalCallbackRegistry registry,
+            final Result<ModelType> result)
             throws InstantiationException, InvocationTargetException, IllegalAccessException {
         Set<ConstructorParameter> requiredParameters = new HashSet<ConstructorParameter>();
         Type[] parameterTypes = constructor.getGenericParameterTypes();
@@ -535,7 +580,7 @@ public class ModelAdapterFactory impleme
             }
         }
         if (!requiredParameters.isEmpty()) {
-            log.warn("Required constructor parameters {} on model class {} were not able to be injected.", requiredParameters, type);
+            result.setFailure(FailureType.MISSING_CONSTRUCTOR_PARAMS, requiredParameters);
             return null;
         }
         return constructor.newInstance(paramValues.toArray(new Object[paramValues.size()]));

Added: 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=1628824&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java (added)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/Result.java Wed Oct  1 20:04:24 2014
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.models.impl;
+
+import java.lang.reflect.AnnotatedElement;
+import java.util.Set;
+
+import org.apache.sling.models.factory.InvalidAdaptableException;
+import org.apache.sling.models.factory.InvalidModelException;
+import org.apache.sling.models.factory.MissingElementsException;
+import org.slf4j.Logger;
+
+public class Result<ModelType> {
+
+    enum FailureType {
+        ADAPTABLE_DOES_NOT_MATCH("Adaptable is not acceptable for the model class"), FAILED_CALLING_POST_CONSTRUCT(
+                "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.");
+
+        private String message;
+
+        private FailureType(String msg) {
+            this.message = msg;
+        }
+    }
+
+    private Exception failureException;
+
+    private String failureMessage;
+
+    private FailureType failureType;
+
+    private ModelType model;
+
+    private Class<? extends ModelType> type;
+
+    private Set<? extends AnnotatedElement> missingElements;
+
+    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;
+            }
+        }
+    }
+
+    public void setFailure(FailureType type) {
+        this.failureType = type;
+    }
+
+    public void setFailure(FailureType type, Exception e) {
+        this.failureType = type;
+        this.failureException = e;
+    }
+
+    public void setFailure(FailureType type, Set<? extends AnnotatedElement> requiredElements) {
+        this.failureType = type;
+        this.missingElements = requiredElements;
+    }
+
+    public void setFailure(FailureType type, String msg) {
+        this.failureType = type;
+        this.failureMessage = msg;
+    }
+
+    public void setFailure(FailureType type, String msg, Exception e) {
+        this.failureType = type;
+        this.failureMessage = msg;
+        this.failureException = e;
+    }
+
+    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);
+            }
+        }
+    }
+
+    private String getMessage() {
+        if (failureMessage != null) {
+            return failureMessage;
+        } else if (failureType != null) {
+            return failureType.message;
+        } else {
+            return null;
+        }
+    }
+}

Modified: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ConstructorTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ConstructorTest.java?rev=1628824&r1=1628823&r2=1628824&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ConstructorTest.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/ConstructorTest.java Wed Oct  1 20:04:24 2014
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.*;
 import java.util.Hashtable;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.factory.InvalidModelException;
 import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
 import org.apache.sling.models.impl.injectors.SelfInjector;
 import org.apache.sling.models.testmodels.classes.InvalidConstructorModel;
@@ -109,6 +110,11 @@ public class ConstructorTest {
         assertNull(model);
     }
 
+    @Test(expected = InvalidModelException.class)
+    public void testInvalidConstructorInjectorException() {
+        factory.createModel(request, InvalidConstructorModel.class);
+    }
+
     /**
      * Test model object with three constructors, and make sure that one with @Inject is picked for instantiation.
      * Test mixing of constructor injection and field injection as well.

Modified: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InvalidAdaptationsTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InvalidAdaptationsTest.java?rev=1628824&r1=1628823&r2=1628824&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InvalidAdaptationsTest.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InvalidAdaptationsTest.java Wed Oct  1 20:04:24 2014
@@ -28,6 +28,8 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.factory.InvalidAdaptableException;
+import org.apache.sling.models.factory.InvalidModelException;
 import org.apache.sling.models.impl.injectors.ChildResourceInjector;
 import org.apache.sling.models.impl.injectors.ValueMapInjector;
 import org.junit.Before;
@@ -70,6 +72,16 @@ public class InvalidAdaptationsTest {
         assertNull(factory.getAdapter(res, NonModel.class));
     }
 
+    @Test(expected = InvalidModelException.class)
+    public void testNonModelClassException() {
+        Map<String, Object> emptyMap = Collections.<String, Object> emptyMap();
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(new ValueMapDecorator(emptyMap));
+
+        assertNull(factory.createModel(res, NonModel.class));
+    }
+
     @Test
     public void testWrongAdaptableClass() {
         Map<String, Object> emptyMap = Collections.<String, Object> emptyMap();
@@ -80,6 +92,16 @@ public class InvalidAdaptationsTest {
         assertNull(factory.getAdapter(res, RequestModel.class));
     }
 
+    @Test(expected = InvalidAdaptableException.class)
+    public void testWrongAdaptableClassException() {
+        Map<String, Object> emptyMap = Collections.<String, Object> emptyMap();
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(new ValueMapDecorator(emptyMap));
+
+        assertNull(factory.createModel(res, RequestModel.class));
+    }
+
     private class NonModel {
     }