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 {
}