You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:55:35 UTC
[sling-org-apache-sling-models-impl] 05/24: SLING-3716 SLING-3718 -
adding support for constructor injection and self injection based on a
patch from Stefan Seifert
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.models.impl-1.1.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit 76f3fae7933d72c0cbb4cd8f9e96b0c681dd94c0
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Tue Aug 19 22:51:36 2014 +0000
SLING-3716 SLING-3718 - adding support for constructor injection and self injection based on a patch from Stefan Seifert
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1619007 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 2 +-
.../sling/models/impl/ConstructorParameter.java | 87 ++++++
.../models/impl/MapBackedInvocationHandler.java | 37 +++
.../sling/models/impl/ModelAdapterFactory.java | 304 ++++++++++++++-------
.../impl/ParameterCountInjectComparator.java | 63 +++++
.../models/impl/injectors/OSGiServiceInjector.java | 3 +-
.../sling/models/impl/injectors/SelfInjector.java | 87 ++++++
.../apache/sling/models/impl/ConstructorTest.java | 39 ++-
.../org/apache/sling/models/impl/DefaultTest.java | 62 ++++-
.../impl/InjectorSpecificAnnotationTest.java | 106 ++++++-
.../sling/models/impl/OSGiInjectionTest.java | 62 ++++-
.../sling/models/impl/RequestInjectionTest.java | 11 +-
.../sling/models/impl/SelfDependencyTest.java | 95 +++++++
.../impl/injectors/BindingsInjectorTest.java | 98 +++++++
.../injectors/RequestAttributeInjectorTest.java | 86 ++++++
.../models/impl/injectors/SelfInjectorTest.java | 86 ++++++
.../impl/injectors/ValueMapInjectorTest.java | 86 ++++++
.../classes/DirectCyclicSelfDependencyModel.java | 36 +++
.../IndirectCyclicSelfDependencyModelA.java | 36 +++
.../IndirectCyclicSelfDependencyModelB.java | 36 +++
.../testmodels/classes/SelfDependencyModelA.java | 36 +++
.../testmodels/classes/SelfDependencyModelB.java | 36 +++
.../constructorinjection/BindingsModel.java | 41 +++
.../DefaultPrimitivesModel.java | 62 +++++
.../constructorinjection/DefaultStringModel.java | 48 ++++
.../constructorinjection/DefaultWrappersModel.java | 63 +++++
.../InjectorSpecificAnnotationModel.java | 91 ++++++
.../constructorinjection/ListOSGiModel.java | 42 +++
.../classes/constructorinjection/NoNameModel.java | 42 +++
.../constructorinjection/SimpleOSGiModel.java | 40 +++
.../WithThreeConstructorsOneInjectModel.java | 59 ++++
31 files changed, 1850 insertions(+), 132 deletions(-)
diff --git a/pom.xml b/pom.xml
index 94c9d3c..426fb1d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,7 +63,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.models.api</artifactId>
- <version>1.0.2</version>
+ <version>1.0.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/sling/models/impl/ConstructorParameter.java b/src/main/java/org/apache/sling/models/impl/ConstructorParameter.java
new file mode 100644
index 0000000..eab70ae
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/ConstructorParameter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+
+/**
+ * Constructor parameters aren't normally accessible using the
+ * AnnotatedElement. This class acts as a facade to ease
+ * compatibility with field and method injection.
+ */
+class ConstructorParameter implements AnnotatedElement {
+
+ private final Annotation[] annotations;
+ private final Class<?> type;
+ private final Type genericType;
+ private final int parameterIndex;
+
+ ConstructorParameter(Annotation[] annotations, Class<?> type, Type genericType, int parameterIndex) {
+ this.annotations = annotations;
+ this.type = type;
+ this.genericType = genericType;
+ this.parameterIndex = parameterIndex;
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> paramClass) {
+ return getAnnotation(paramClass) != null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> paramClass) {
+ for (Annotation annotation : this.annotations) {
+ if (paramClass.isInstance(annotation)) {
+ return (T)annotation;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Annotation[] getAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return annotations;
+ }
+
+ public Class<?> getType() {
+ return this.type;
+ }
+
+ public Type getGenericType() {
+ return this.genericType;
+ }
+
+ public int getParameterIndex() {
+ return this.parameterIndex;
+ }
+
+ @Override
+ public String toString() {
+ return "Parameter" + this.parameterIndex + "[" + this.genericType.toString() + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/impl/MapBackedInvocationHandler.java b/src/main/java/org/apache/sling/models/impl/MapBackedInvocationHandler.java
new file mode 100644
index 0000000..c486781
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/MapBackedInvocationHandler.java
@@ -0,0 +1,37 @@
+/*
+ * 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.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+class MapBackedInvocationHandler implements InvocationHandler {
+
+ private Map<Method, Object> methods;
+
+ public MapBackedInvocationHandler(Map<Method, Object> methods) {
+ this.methods = methods;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return methods.get(method);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
index 8532de4..b77cce1 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -32,7 +32,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
@@ -69,6 +68,7 @@ import org.apache.sling.models.annotations.Source;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.spi.DisposalCallback;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.AcceptsNullName;
import org.apache.sling.models.spi.Injector;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
@@ -83,23 +83,6 @@ import org.slf4j.LoggerFactory;
@Component
public class ModelAdapterFactory implements AdapterFactory, Runnable {
- /**
- * Comparator which sorts constructors by the number of parameters
- * in reverse order (most params to least params).
- */
- public class ParameterCountComparator implements Comparator<Constructor<?>> {
-
- @Override
- public int compare(Constructor<?> o1, Constructor<?> o2) {
- return compare(o2.getParameterTypes().length, o1.getParameterTypes().length);
- }
-
- public int compare(int x, int y) {
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
- }
-
- }
-
private static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry {
private List<DisposalCallback> callbacks = new ArrayList<DisposalCallback>();
@@ -125,20 +108,6 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
private ConcurrentMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl> disposalCallbacks;
- private static class MapBackedInvocationHandler implements InvocationHandler {
-
- private Map<Method, Object> methods;
-
- public MapBackedInvocationHandler(Map<Method, Object> methods) {
- this.methods = methods;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return methods.get(method);
- }
-
- }
-
@Override
public void run() {
java.lang.ref.Reference<? extends Object> ref = queue.poll();
@@ -169,39 +138,72 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
private ServiceRegistration jobRegistration;
private ServiceRegistration configPrinterRegistration;
+
+ // Use threadlocal to count recursive invocations and break recursing if a max. limit is reached (to avoid cyclic dependencies)
+ private static final int MAX_RECURSIVE_INOVCATIONS = 20;
- @SuppressWarnings("unchecked")
- public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
- Model modelAnnotation = type.getAnnotation(Model.class);
- if (modelAnnotation == null) {
- return null;
+ private static class ThreadInvocationCounter {
+ private int count;
+ public boolean isMaximumReached() {
+ return count >= MAX_RECURSIVE_INOVCATIONS;
}
- boolean isAdaptable = false;
-
- Class<?>[] declaredAdaptable = modelAnnotation.adaptables();
- for (Class<?> clazz : declaredAdaptable) {
- if (clazz.isInstance(adaptable)) {
- isAdaptable = true;
- }
+ public void increase() {
+ this.count++;
}
- if (!isAdaptable) {
- return null;
+ public void decrease() {
+ this.count--;
}
+ }
- if (type.isInterface()) {
- InvocationHandler handler = createInvocationHandler(adaptable, type, modelAnnotation);
- if (handler != null) {
- return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
- } else {
+ private static final ThreadLocal<ThreadInvocationCounter> INVOCATION_COUNT_THREADLOCAL = new ThreadLocal<ModelAdapterFactory.ThreadInvocationCounter>() {
+ @Override
+ protected ThreadInvocationCounter initialValue() {
+ return new ThreadInvocationCounter();
+ }
+ };
+
+ @SuppressWarnings("unchecked")
+ public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+ if (INVOCATION_COUNT_THREADLOCAL.get().isMaximumReached()) {
+ log.error("Adapting {} to {} failed, too much recursive invocations (>={}).",
+ new Object[] { adaptable, type, MAX_RECURSIVE_INOVCATIONS });
+ return null;
+ };
+ INVOCATION_COUNT_THREADLOCAL.get().increase();
+ try {
+ Model modelAnnotation = type.getAnnotation(Model.class);
+ if (modelAnnotation == null) {
return null;
}
- } else {
- try {
- return createObject(adaptable, type, modelAnnotation);
- } catch (Exception e) {
- log.error("unable to create object", e);
+ boolean isAdaptable = false;
+
+ Class<?>[] declaredAdaptable = modelAnnotation.adaptables();
+ for (Class<?> clazz : declaredAdaptable) {
+ if (clazz.isInstance(adaptable)) {
+ isAdaptable = true;
+ }
+ }
+ if (!isAdaptable) {
return null;
}
+
+ if (type.isInterface()) {
+ InvocationHandler handler = createInvocationHandler(adaptable, type, modelAnnotation);
+ if (handler != null) {
+ return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
+ } else {
+ return null;
+ }
+ } else {
+ try {
+ return createObject(adaptable, type, modelAnnotation);
+ } catch (Exception e) {
+ log.error("unable to create object", e);
+ return null;
+ }
+ }
+ } finally {
+ INVOCATION_COUNT_THREADLOCAL.get().decrease();
}
}
@@ -276,14 +278,28 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
return setMethod((Method) element, methods, value);
}
}
+
+ private static class SetConstructorParameterCallback implements InjectCallback {
+
+ private final List<Object> parameterValues;
- private boolean injectFieldOrMethod(final AnnotatedElement element, final Object adaptable, final Type type,
+ private SetConstructorParameterCallback(List<Object> parameterValues) {
+ this.parameterValues = parameterValues;
+ }
+
+ @Override
+ public boolean inject(AnnotatedElement element, Object value) {
+ return setConstructorParameter((ConstructorParameter)element, parameterValues, value);
+ }
+ }
+
+ private boolean injectElement(final AnnotatedElement element, final Object adaptable, final Type type,
final Model modelAnnotation, final DisposalCallbackRegistry registry, InjectCallback callback) {
InjectAnnotationProcessor annotationProcessor = null;
String source = getSource(element);
boolean wasInjectionSuccessful = false;
-
+
// find an appropriate annotation processor
for (InjectAnnotationProcessorFactory factory : sortedInjectAnnotationProcessorFactories) {
annotationProcessor = factory.createAnnotationProcessor(adaptable, element);
@@ -292,16 +308,19 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
+ 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())) {
- String name = getName(element, annotationProcessor);
- Object injectionAdaptable = getAdaptable(adaptable, element, annotationProcessor);
- if (injectionAdaptable != null) {
- Object value = injector.getValue(injectionAdaptable, name, type, element, registry);
- if (callback.inject(element, value)) {
- wasInjectionSuccessful = true;
- break;
+ if (name != null || injector instanceof AcceptsNullName) {
+ if (injectionAdaptable != null) {
+ Object value = injector.getValue(injectionAdaptable, name, type, element, registry);
+ if (callback.inject(element, value)) {
+ wasInjectionSuccessful = true;
+ break;
+ }
}
}
}
@@ -324,12 +343,13 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
SetMethodsCallback callback = new SetMethodsCallback(methods);
MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods);
- DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+ DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl();
+ registerCallbackRegistry(handler, registry);
Set<Method> requiredMethods = new HashSet<Method>();
for (Method method : injectableMethods) {
Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
- if (!injectFieldOrMethod(method, adaptable, returnType, modelAnnotation, registry, callback)) {
+ if (!injectElement(method, adaptable, returnType, modelAnnotation, registry, callback)) {
requiredMethods.add(method);
}
}
@@ -341,11 +361,9 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
return handler;
}
- private DisposalCallbackRegistryImpl createAndRegisterCallbackRegistry(Object object) {
+ private void registerCallbackRegistry(Object object, DisposalCallbackRegistryImpl registry) {
PhantomReference<Object> reference = new PhantomReference<Object>(object, queue);
- DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl();
disposalCallbacks.put(reference, registry);
- return registry;
}
private String getSource(AnnotatedElement element) {
@@ -380,60 +398,52 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
return null;
}
- @SuppressWarnings("unchecked")
private <AdapterType> AdapterType createObject(Object adaptable, Class<AdapterType> type, Model modelAnnotation)
throws InstantiationException, InvocationTargetException, IllegalAccessException {
- Set<Field> injectableFields = collectInjectableFields(type);
+ DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl();
- Constructor<?>[] constructors = type.getConstructors();
- if (constructors.length == 0) {
- log.warn("Model class {} does not have a public constructor.", type.getName());
+ Constructor<AdapterType> constructorToUse = getBestMatchingConstructor(adaptable, type);
+ if (constructorToUse == null) {
+ log.warn("Model class {} does not have a usable constructor", type.getName());
return null;
}
- // sort the constructor list in order from most params to least params
- Arrays.sort(constructors, new ParameterCountComparator());
-
- Constructor<AdapterType> constructorToUse = null;
- boolean constructorHasParam = false;
- for (Constructor<?> constructor : constructors) {
- final Class<?>[] paramTypes = constructor.getParameterTypes();
- if (paramTypes.length == 1) {
- Class<?> paramType = constructor.getParameterTypes()[0];
- if (paramType.isInstance(adaptable)) {
- constructorToUse = (Constructor<AdapterType>) constructor;
- constructorHasParam = true;
- break;
- }
- }
-
- if (constructor.getParameterTypes().length == 0) {
- constructorToUse = (Constructor<AdapterType>) constructor;
- constructorHasParam = false;
- break;
+ final AdapterType object;
+ if (constructorToUse.getParameterTypes().length == 0) {
+ // no parameters for constructor injection? instantiate it right away
+ object = constructorToUse.newInstance();
+ } else {
+ // 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);
+ } catch (InstantiationException ex) {
+ registry.onDisposed();
+ throw ex;
+ } catch (InvocationTargetException ex) {
+ registry.onDisposed();
+ throw ex;
+ } catch (IllegalAccessException ex) {
+ registry.onDisposed();
+ throw ex;
}
}
- if (constructorToUse == null) {
- log.warn("Model class {} does not have a usable constructor", type.getName());
+ if (object == null) {
+ registry.onDisposed();
return null;
}
- final AdapterType object;
- if (constructorHasParam) {
- object = constructorToUse.newInstance(adaptable);
- } else {
- object = constructorToUse.newInstance();
- }
- InjectCallback callback = new SetFieldCallback(object);
+ registerCallbackRegistry(object, registry);
- DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(object);
+ InjectCallback callback = new SetFieldCallback(object);
Set<Field> requiredFields = new HashSet<Field>();
+ Set<Field> injectableFields = collectInjectableFields(type);
for (Field field : injectableFields) {
Type fieldType = mapPrimitiveClasses(field.getGenericType());
- if (!injectFieldOrMethod(field, adaptable, fieldType, modelAnnotation, registry, callback)) {
+ if (!injectElement(field, adaptable, fieldType, modelAnnotation, registry, callback)) {
requiredFields.add(field);
}
}
@@ -453,6 +463,64 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
+ /**
+ * Gets best matching constructor for constructor injection - or default constructor if none is found.
+ * @param adaptable Adaptable instance
+ * @param type Model type
+ * @return Constructor or null if none found
+ */
+ @SuppressWarnings("unchecked")
+ private <AdapterType> Constructor<AdapterType> getBestMatchingConstructor(Object adaptable, Class<AdapterType> type) {
+ Constructor<?>[] constructors = type.getConstructors();
+
+ // sort the constructor list in order from most params to least params, and constructors with @Inject annotation first
+ Arrays.sort(constructors, new ParameterCountInjectComparator());
+
+ 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;
+ }
+ // compatibility mode for sling models implementation <= 1.0.6:
+ // support constructor without @Inject if it has exactly one parameter matching the adaptable class
+ final Class<?>[] paramTypes = constructor.getParameterTypes();
+ if (paramTypes.length == 1) {
+ Class<?> paramType = constructor.getParameterTypes()[0];
+ if (paramType.isInstance(adaptable)) {
+ return (Constructor<AdapterType>) constructor;
+ }
+ }
+ // if no constructor for injection found use public constructor without any params
+ if (constructor.getParameterTypes().length == 0) {
+ return (Constructor<AdapterType>) constructor;
+ }
+ }
+ return null;
+ }
+
+ private <AdapterType> AdapterType newInstanceWithConstructorInjection(Constructor<AdapterType> constructor, Object adaptable,
+ Class<AdapterType> type, Model modelAnnotation, DisposalCallbackRegistry registry)
+ throws InstantiationException, InvocationTargetException, IllegalAccessException {
+ Set<ConstructorParameter> requiredParameters = new HashSet<ConstructorParameter>();
+ Type[] parameterTypes = constructor.getGenericParameterTypes();
+ List<Object> paramValues = new ArrayList<Object>(Arrays.asList(new Object[parameterTypes.length]));
+ InjectCallback callback = new SetConstructorParameterCallback(paramValues);
+
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Type genericType = mapPrimitiveClasses(parameterTypes[i]);
+ ConstructorParameter constructorParameter = new ConstructorParameter(
+ constructor.getParameterAnnotations()[i], constructor.getParameterTypes()[i], genericType, i);
+ if (!injectElement(constructorParameter, adaptable, genericType, modelAnnotation, registry, callback)) {
+ requiredParameters.add(constructorParameter);
+ }
+ }
+ if (!requiredParameters.isEmpty()) {
+ log.warn("Required constructor parameters {} on model class {} were not able to be injected.", requiredParameters, type);
+ return null;
+ }
+ return constructor.newInstance(paramValues.toArray(new Object[paramValues.size()]));
+ }
+
private boolean isOptional(AnnotatedElement point, Model modelAnnotation, InjectAnnotationProcessor annotationProcessor) {
if (annotationProcessor != null) {
Boolean isOptional = annotationProcessor.isOptional();
@@ -582,6 +650,9 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
return getNameFromMethod((Method) element);
} else if (element instanceof Field) {
return getNameFromField((Field) element);
+ } else if (element instanceof ConstructorParameter) {
+ // implicit name not supported for constructor parameters - but do not throw exception because class-based injection is still possible
+ return null;
} else {
throw new IllegalArgumentException("The given element must be either method or field but is " + element);
}
@@ -702,6 +773,27 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
+ private static boolean setConstructorParameter(ConstructorParameter constructorParameter, List<Object> parameterValues, Object value) {
+ if (value != null && constructorParameter.getType() instanceof Class<?>) {
+ Class<?> requestedType = (Class<?>)constructorParameter.getType();
+ if (!isAcceptableType(requestedType, constructorParameter.getGenericType(), value)) {
+ if (value instanceof Adaptable) {
+ value = ((Adaptable) value).adaptTo(requestedType);
+ if (value == null) {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ parameterValues.set(constructorParameter.getParameterIndex(), value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private static boolean isAcceptableType(Class<?> type, Type genericType, Object value) {
if (type.isInstance(value)) {
if ((type == Collection.class || type == List.class) && genericType instanceof ParameterizedType &&
diff --git a/src/main/java/org/apache/sling/models/impl/ParameterCountInjectComparator.java b/src/main/java/org/apache/sling/models/impl/ParameterCountInjectComparator.java
new file mode 100644
index 0000000..afb8410
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/ParameterCountInjectComparator.java
@@ -0,0 +1,63 @@
+/*
+ * 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.Constructor;
+import java.util.Comparator;
+
+import javax.inject.Inject;
+
+/**
+ * Comparator which sorts constructors by the number of parameters
+ * in reverse order (most params to least params).
+ * If two constructors have the same number of parameters constructors with
+ * @Inject annotation are sorted first.
+ */
+class ParameterCountInjectComparator implements Comparator<Constructor<?>> {
+
+ @Override
+ public int compare(Constructor<?> o1, Constructor<?> o2) {
+ int result = compareParameterCount(o2.getParameterTypes().length, o1.getParameterTypes().length);
+ if (result==0) {
+ return compareInjectAnnotation(o1, o2);
+ }
+ else {
+ return result;
+ }
+ }
+
+ private int compareParameterCount(int x, int y) {
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
+ }
+
+ private int compareInjectAnnotation(Constructor<?> o1, Constructor<?> o2) {
+ boolean hasInject1 = o1.isAnnotationPresent(Inject.class);
+ boolean hasInject2 = o2.isAnnotationPresent(Inject.class);
+ if (hasInject1 && !hasInject2) {
+ return -1;
+ }
+ else if (hasInject2 && !hasInject1) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
index fb85dc0..0d1b4e8 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
@@ -38,6 +38,7 @@ import org.apache.sling.models.annotations.Filter;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.spi.DisposalCallback;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.AcceptsNullName;
import org.apache.sling.models.spi.Injector;
import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
@@ -53,7 +54,7 @@ import org.slf4j.LoggerFactory;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 5000)
-public class OSGiServiceInjector implements Injector, InjectAnnotationProcessorFactory {
+public class OSGiServiceInjector implements Injector, InjectAnnotationProcessorFactory, AcceptsNullName {
private static final Logger log = LoggerFactory.getLogger(OSGiServiceInjector.class);
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/SelfInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/SelfInjector.java
new file mode 100644
index 0000000..5787b46
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/SelfInjector.java
@@ -0,0 +1,87 @@
+/*
+ * 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.injectors;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.AcceptsNullName;
+import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
+import org.osgi.framework.Constants;
+
+/**
+ * Injects the adaptable object itself.
+ */
+@Component
+@Service
+@Property(name = Constants.SERVICE_RANKING, intValue = 100)
+public class SelfInjector implements Injector, InjectAnnotationProcessorFactory, AcceptsNullName {
+
+ @Override
+ public String getName() {
+ return "self";
+ }
+
+ public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
+ // if the @Self annotation is present return the adaptable to be inserted directly or to be adapted from
+ if (element.isAnnotationPresent(Self.class)) {
+ return adaptable;
+ }
+ // otherwise apply class-based injection only if class matches or is a superclass
+ else if (type instanceof Class<?>) {
+ Class<?> requestedClass = (Class<?>)type;
+ if (requestedClass.isAssignableFrom(adaptable.getClass())) {
+ return adaptable;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ Self annotation = element.getAnnotation(Self.class);
+ if (annotation != null) {
+ return new SelfAnnotationProcessor(annotation);
+ }
+ return null;
+ }
+
+ private static class SelfAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final Self annotation;
+
+ public SelfAnnotationProcessor(Self annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/ConstructorTest.java b/src/test/java/org/apache/sling/models/impl/ConstructorTest.java
index 0f3da57..6720a16 100644
--- a/src/test/java/org/apache/sling/models/impl/ConstructorTest.java
+++ b/src/test/java/org/apache/sling/models/impl/ConstructorTest.java
@@ -21,11 +21,14 @@ import static org.mockito.Mockito.*;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
+import org.apache.sling.models.impl.injectors.SelfInjector;
import org.apache.sling.models.testmodels.classes.InvalidConstructorModel;
import org.apache.sling.models.testmodels.classes.SuperclassConstructorModel;
import org.apache.sling.models.testmodels.classes.WithOneConstructorModel;
import org.apache.sling.models.testmodels.classes.WithThreeConstructorsModel;
import org.apache.sling.models.testmodels.classes.WithTwoConstructorsModel;
+import org.apache.sling.models.testmodels.classes.constructorinjection.NoNameModel;
+import org.apache.sling.models.testmodels.classes.constructorinjection.WithThreeConstructorsOneInjectModel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,6 +38,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
public class ConstructorTest {
@Mock
@@ -48,15 +52,21 @@ public class ConstructorTest {
@Mock
private SlingHttpServletRequest request;
+ private static final int INT_VALUE = 42;
+
+ private static final String STRING_VALUE = "myValue";
+
@Before
public void setup() {
when(componentCtx.getBundleContext()).thenReturn(bundleContext);
- when(request.getAttribute("attribute")).thenReturn(42);
+ when(request.getAttribute("attribute")).thenReturn(INT_VALUE);
+ when(request.getAttribute("attribute2")).thenReturn(STRING_VALUE);
factory = new ModelAdapterFactory();
factory.activate(componentCtx);
factory.bindInjector(new RequestAttributeInjector(), new ServicePropertiesMap(1, 1));
+ factory.bindInjector(new SelfInjector(), new ServicePropertiesMap(2, 2));
}
@Test
@@ -64,7 +74,7 @@ public class ConstructorTest {
WithOneConstructorModel model = factory.getAdapter(request, WithOneConstructorModel.class);
assertNotNull(model);
assertEquals(request, model.getRequest());
- assertEquals(42, model.getAttribute());
+ assertEquals(INT_VALUE, model.getAttribute());
}
@Test
@@ -72,7 +82,7 @@ public class ConstructorTest {
WithThreeConstructorsModel model = factory.getAdapter(request, WithThreeConstructorsModel.class);
assertNotNull(model);
assertEquals(request, model.getRequest());
- assertEquals(42, model.getAttribute());
+ assertEquals(INT_VALUE, model.getAttribute());
}
@Test
@@ -80,7 +90,7 @@ public class ConstructorTest {
WithTwoConstructorsModel model = factory.getAdapter(request, WithTwoConstructorsModel.class);
assertNotNull(model);
assertEquals(request, model.getRequest());
- assertEquals(42, model.getAttribute());
+ assertEquals(INT_VALUE, model.getAttribute());
}
@Test
@@ -88,7 +98,7 @@ public class ConstructorTest {
SuperclassConstructorModel model = factory.getAdapter(request, SuperclassConstructorModel.class);
assertNotNull(model);
assertEquals(request, model.getRequest());
- assertEquals(42, model.getAttribute());
+ assertEquals(INT_VALUE, model.getAttribute());
}
@Test
@@ -97,4 +107,23 @@ public class ConstructorTest {
assertNull(model);
}
+ /**
+ * 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.
+ */
+ @Test
+ public void testThreeConstructorsOneInjectInjection() {
+ WithThreeConstructorsOneInjectModel model = factory.getAdapter(request,
+ WithThreeConstructorsOneInjectModel.class);
+ assertNotNull(model);
+ assertNull(model.getRequest());
+ assertEquals(INT_VALUE, model.getAttribute());
+ assertEquals(STRING_VALUE, model.getAttribute2());
+ }
+
+ @Test
+ public void testNoNameModel() {
+ NoNameModel model = factory.getAdapter(request, NoNameModel.class);
+ assertNull(model);
+ }
}
diff --git a/src/test/java/org/apache/sling/models/impl/DefaultTest.java b/src/test/java/org/apache/sling/models/impl/DefaultTest.java
index aa6de10..01a333f 100644
--- a/src/test/java/org/apache/sling/models/impl/DefaultTest.java
+++ b/src/test/java/org/apache/sling/models/impl/DefaultTest.java
@@ -39,6 +39,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
public class DefaultTest {
@Mock
@@ -58,7 +59,7 @@ public class DefaultTest {
}
@Test
- public void testDefaultStringValue() {
+ public void testDefaultStringValueField() {
ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
Resource res = mock(Resource.class);
@@ -71,7 +72,7 @@ public class DefaultTest {
}
@Test
- public void testDefaultStringValueOnInterface() {
+ public void testDefaultStringValueOnInterfaceField() {
ValueMap vm = new ValueMapDecorator(Collections.<String, Object>singletonMap("first", "first value"));
Resource res = mock(Resource.class);
@@ -85,7 +86,7 @@ public class DefaultTest {
@Test
- public void testDefaultPrimitives() {
+ public void testDefaultPrimitivesField() {
ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
Resource res = mock(Resource.class);
@@ -103,7 +104,7 @@ public class DefaultTest {
}
@Test
- public void testDefaultWrappers() {
+ public void testDefaultWrappersField() {
ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
Resource res = mock(Resource.class);
@@ -119,4 +120,57 @@ public class DefaultTest {
assertEquals(Long.valueOf(1L), model.getLongWrapperProperty());
assertArrayEquals(new Long[] { Long.valueOf(1L), Long.valueOf(1L) }, model.getLongWrapperArrayProperty());
}
+
+ @Test
+ public void testDefaultStringValueConstructor() {
+ ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
+
+ Resource res = mock(Resource.class);
+ when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.DefaultStringModel model
+ = factory.getAdapter(res, org.apache.sling.models.testmodels.classes.constructorinjection.DefaultStringModel.class);
+ assertNotNull(model);
+ assertEquals("firstDefault", model.getFirstProperty());
+ assertEquals(2, model.getSecondProperty().length);
+ }
+
+ @Test
+ public void testDefaultPrimitivesConstructor() {
+ ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
+
+ Resource res = mock(Resource.class);
+ when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.DefaultPrimitivesModel model
+ = factory.getAdapter(res, org.apache.sling.models.testmodels.classes.constructorinjection.DefaultPrimitivesModel.class);
+ assertNotNull(model);
+
+ assertEquals(true, model.getBooleanProperty());
+ // we need to wait for JUnit 4.12 for this assertArrayEquals to be working on primitive boolean arrays, https://github.com/junit-team/junit/issues/86!
+ assertTrue(Arrays.equals(new boolean[] { true, true }, model.getBooleanArrayProperty()));
+
+ assertEquals(1L, model.getLongProperty());
+ assertArrayEquals(new long[] { 1L, 1L }, model.getLongArrayProperty());
+ }
+
+ @Test
+ public void testDefaultWrappersConstructor() {
+ ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
+
+ Resource res = mock(Resource.class);
+ when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.DefaultWrappersModel model
+ = factory.getAdapter(res, org.apache.sling.models.testmodels.classes.constructorinjection.DefaultWrappersModel.class);
+ assertNotNull(model);
+
+ assertEquals(Boolean.valueOf(true), model.getBooleanWrapperProperty());
+ // we need to wait for JUnit 4.12 for this assertArrayEquals to be working on primitive boolean arrays, https://github.com/junit-team/junit/issues/86!
+ assertTrue(Arrays.equals(new Boolean[] { Boolean.TRUE, Boolean.TRUE }, model.getBooleanWrapperArrayProperty()));
+
+ assertEquals(Long.valueOf(1L), model.getLongWrapperProperty());
+ assertArrayEquals(new Long[] { Long.valueOf(1L), Long.valueOf(1L) }, model.getLongWrapperArrayProperty());
+ }
+
}
diff --git a/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java b/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
index 8d8fea7..2a82d7d 100644
--- a/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
+++ b/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
@@ -50,6 +50,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
public class InjectorSpecificAnnotationTest {
@Mock
@@ -110,7 +111,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testSimpleValueModel() {
+ public void testSimpleValueModelField() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("first", "first-value");
map.put("second", "second-value");
@@ -127,7 +128,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testOrderForValueAnnotation() {
+ public void testOrderForValueAnnotationField() {
// make sure that that the correct injection is used
// make sure that log is adapted from value map
// and not coming from request attribute
@@ -149,7 +150,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testOSGiService() throws InvalidSyntaxException {
+ public void testOSGiServiceField() throws InvalidSyntaxException {
ServiceReference ref = mock(ServiceReference.class);
Logger log = mock(Logger.class);
when(bundleContext.getServiceReferences(Logger.class.getName(), null)).thenReturn(
@@ -162,7 +163,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testScriptVariable() throws InvalidSyntaxException {
+ public void testScriptVariableField() throws InvalidSyntaxException {
SlingBindings bindings = new SlingBindings();
SlingScriptHelper helper = mock(SlingScriptHelper.class);
bindings.setSling(helper);
@@ -174,7 +175,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testRequestAttribute() throws InvalidSyntaxException {
+ public void testRequestAttributeField() throws InvalidSyntaxException {
Object attribute = new Object();
when(request.getAttribute("attribute")).thenReturn(attribute);
@@ -184,7 +185,7 @@ public class InjectorSpecificAnnotationTest {
}
@Test
- public void testChildResource() {
+ public void testChildResourceField() {
Resource res = mock(Resource.class);
Resource child = mock(Resource.class);
when(res.getChild("child1")).thenReturn(child);
@@ -194,4 +195,97 @@ public class InjectorSpecificAnnotationTest {
assertNotNull("Could not instanciate model", model);
assertEquals(child, model.getChildResource());
}
+
+ @Test
+ public void testSimpleValueModelConstructor() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("first", "first-value");
+ map.put("second", "second-value");
+ ValueMap vm = new ValueMapDecorator(map);
+
+ Resource res = mock(Resource.class);
+ when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+ when(request.getResource()).thenReturn(res);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals("first-value", model.getFirst());
+ assertEquals("second-value", model.getSecond());
+ }
+
+ @Test
+ public void testOrderForValueAnnotationConstructor() {
+ // make sure that that the correct injection is used
+ // make sure that log is adapted from value map
+ // and not coming from request attribute
+ Logger logFromValueMap = LoggerFactory.getLogger(this.getClass());
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("first", "first-value");
+ map.put("log", logFromValueMap);
+ ValueMap vm = new ValueMapDecorator(map);
+
+ Resource res = mock(Resource.class);
+ when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+ when(request.getResource()).thenReturn(res);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals("first-value", model.getFirst());
+ assertEquals(logFromValueMap, model.getLog());
+ }
+
+ @Test
+ public void testOSGiServiceConstructor() throws InvalidSyntaxException {
+ ServiceReference ref = mock(ServiceReference.class);
+ Logger log = mock(Logger.class);
+ when(bundleContext.getServiceReferences(Logger.class.getName(), null)).thenReturn(
+ new ServiceReference[] { ref });
+ when(bundleContext.getService(ref)).thenReturn(log);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(log, model.getService());
+ }
+
+ @Test
+ public void testScriptVariableConstructor() throws InvalidSyntaxException {
+ SlingBindings bindings = new SlingBindings();
+ SlingScriptHelper helper = mock(SlingScriptHelper.class);
+ bindings.setSling(helper);
+ when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(helper, model.getHelper());
+ }
+
+ @Test
+ public void testRequestAttributeConstructor() throws InvalidSyntaxException {
+ Object attribute = new Object();
+ when(request.getAttribute("attribute")).thenReturn(attribute);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(attribute, model.getRequestAttribute());
+ }
+
+ @Test
+ public void testChildResourceConstructor() {
+ Resource res = mock(Resource.class);
+ Resource child = mock(Resource.class);
+ when(res.getChild("child1")).thenReturn(child);
+ when(request.getResource()).thenReturn(res);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(child, model.getChildResource());
+ }
+
}
diff --git a/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java
index 1058323..df09c52 100644
--- a/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java
+++ b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java
@@ -47,6 +47,7 @@ import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
public class OSGiInjectionTest {
private ModelAdapterFactory factory;
@@ -75,7 +76,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testSimpleOSGiModel() throws Exception {
+ public void testSimpleOSGiModelField() throws Exception {
ServiceReference ref = mock(ServiceReference.class);
ServiceInterface service = mock(ServiceInterface.class);
when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn(
@@ -93,7 +94,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testRequestOSGiModel() throws Exception {
+ public void testRequestOSGiModelField() throws Exception {
ServiceInterface service = mock(ServiceInterface.class);
SlingHttpServletRequest request = mock(SlingHttpServletRequest.class);
@@ -114,7 +115,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testListOSGiModel() throws Exception {
+ public void testListOSGiModelField() throws Exception {
ServiceReference ref1 = mock(ServiceReference.class);
ServiceInterface service1 = mock(ServiceInterface.class);
when(bundleContext.getService(ref1)).thenReturn(service1);
@@ -138,7 +139,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testArrayOSGiModel() throws Exception {
+ public void testArrayOSGiModelField() throws Exception {
ServiceReference ref1 = mock(ServiceReference.class);
ServiceInterface service1 = mock(ServiceInterface.class);
when(bundleContext.getService(ref1)).thenReturn(service1);
@@ -162,7 +163,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testOptionalArrayOSGiModel() throws Exception {
+ public void testOptionalArrayOSGiModelField() throws Exception {
Resource res = mock(Resource.class);
@@ -174,7 +175,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testOptionalListOSGiModel() throws Exception {
+ public void testOptionalListOSGiModelField() throws Exception {
Resource res = mock(Resource.class);
OptionalListOSGiModel model = factory.getAdapter(res, OptionalListOSGiModel.class);
@@ -185,7 +186,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testCollectionOSGiModel() throws Exception {
+ public void testCollectionOSGiModelField() throws Exception {
ServiceReference ref1 = mock(ServiceReference.class);
ServiceInterface service1 = mock(ServiceInterface.class);
when(bundleContext.getService(ref1)).thenReturn(service1);
@@ -210,7 +211,7 @@ public class OSGiInjectionTest {
}
@Test
- public void testSetOSGiModel() throws Exception {
+ public void testSetOSGiModelField() throws Exception {
ServiceReference ref1 = mock(ServiceReference.class);
ServiceInterface service1 = mock(ServiceInterface.class);
when(bundleContext.getService(ref1)).thenReturn(service1);
@@ -232,4 +233,49 @@ public class OSGiInjectionTest {
verify(bundleContext).getBundles();
verifyNoMoreInteractions(res, bundleContext);
}
+
+ @Test
+ public void testSimpleOSGiModelConstructor() throws Exception {
+ ServiceReference ref = mock(ServiceReference.class);
+ ServiceInterface service = mock(ServiceInterface.class);
+ when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn(
+ new ServiceReference[] { ref });
+ when(bundleContext.getService(ref)).thenReturn(service);
+
+ Resource res = mock(Resource.class);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.SimpleOSGiModel model
+ = factory.getAdapter(res, org.apache.sling.models.testmodels.classes.constructorinjection.SimpleOSGiModel.class);
+ assertNotNull(model);
+ assertNotNull(model.getService());
+ assertEquals(service, model.getService());
+
+ verifyNoMoreInteractions(res);
+ }
+
+ @Test
+ public void testListOSGiModelConstructor() throws Exception {
+ ServiceReference ref1 = mock(ServiceReference.class);
+ ServiceInterface service1 = mock(ServiceInterface.class);
+ when(bundleContext.getService(ref1)).thenReturn(service1);
+ ServiceReference ref2 = mock(ServiceReference.class);
+ ServiceInterface service2 = mock(ServiceInterface.class);
+ when(bundleContext.getService(ref2)).thenReturn(service2);
+
+ when(bundleContext.getServiceReferences(ServiceInterface.class.getName(), null)).thenReturn(
+ new ServiceReference[] { ref1, ref2 });
+
+ Resource res = mock(Resource.class);
+
+ org.apache.sling.models.testmodels.classes.constructorinjection.ListOSGiModel model
+ = factory.getAdapter(res, org.apache.sling.models.testmodels.classes.constructorinjection.ListOSGiModel.class);
+ assertNotNull(model);
+ assertNotNull(model.getServices());
+ assertEquals(2, model.getServices().size());
+ assertEquals(service1, model.getServices().get(0));
+ assertEquals(service2, model.getServices().get(1));
+
+ verifyNoMoreInteractions(res);
+ }
+
}
diff --git a/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java
index 9f495e3..91dd851 100644
--- a/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java
+++ b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java
@@ -33,6 +33,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
public class RequestInjectionTest {
@Mock
@@ -63,10 +64,18 @@ public class RequestInjectionTest {
}
@Test
- public void testNamedInjection() {
+ public void testNamedInjectionField() {
BindingsModel model = factory.getAdapter(request, BindingsModel.class);
assertNotNull(model.getSling());
assertEquals(sling, model.getSling());
}
+ @Test
+ public void testNamedInjectionConstructor() {
+ org.apache.sling.models.testmodels.classes.constructorinjection.BindingsModel model
+ = factory.getAdapter(request, org.apache.sling.models.testmodels.classes.constructorinjection.BindingsModel.class);
+ assertNotNull(model.getSling());
+ assertEquals(sling, model.getSling());
+ }
+
}
diff --git a/src/test/java/org/apache/sling/models/impl/SelfDependencyTest.java b/src/test/java/org/apache/sling/models/impl/SelfDependencyTest.java
new file mode 100644
index 0000000..e0e9bad
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/SelfDependencyTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.impl.injectors.SelfInjector;
+import org.apache.sling.models.testmodels.classes.DirectCyclicSelfDependencyModel;
+import org.apache.sling.models.testmodels.classes.IndirectCyclicSelfDependencyModelA;
+import org.apache.sling.models.testmodels.classes.SelfDependencyModelA;
+import org.apache.sling.models.testmodels.classes.SelfDependencyModelB;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
+public class SelfDependencyTest {
+
+ @Mock
+ private ComponentContext componentCtx;
+
+ @Mock
+ private BundleContext bundleContext;
+
+ private ModelAdapterFactory factory;
+
+ @Mock
+ private SlingHttpServletRequest request;
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setup() {
+ when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+ when(request.adaptTo(any(Class.class))).then(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Class<?> clazz = (Class<?>) invocation.getArguments()[0];
+ return factory.getAdapter(request, clazz);
+ }
+ });
+
+ factory = new ModelAdapterFactory();
+ factory.activate(componentCtx);
+ factory.bindInjector(new SelfInjector(), new ServicePropertiesMap(1, 1));
+ }
+
+ @Test
+ public void testChainedSelfDependency() {
+ SelfDependencyModelA objectA = factory.getAdapter(request, SelfDependencyModelA.class);
+ assertNotNull(objectA);
+ SelfDependencyModelB objectB = objectA.getDependencyB();
+ assertNotNull(objectB);
+ assertSame(request, objectB.getRequest());
+ }
+
+ @Test
+ public void testDirectCyclicSelfDependency() {
+ DirectCyclicSelfDependencyModel object = factory.getAdapter(request, DirectCyclicSelfDependencyModel.class);
+ assertNull(object);
+ }
+
+ @Test
+ public void testInddirectCyclicSelfDependency() {
+ IndirectCyclicSelfDependencyModelA object = factory.getAdapter(request,
+ IndirectCyclicSelfDependencyModelA.class);
+ assertNull(object);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/injectors/BindingsInjectorTest.java b/src/test/java/org/apache/sling/models/impl/injectors/BindingsInjectorTest.java
new file mode 100644
index 0000000..fa15446
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injectors/BindingsInjectorTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.injectors;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
+public class BindingsInjectorTest {
+
+ private BindingsInjector injector = new BindingsInjector();
+
+ @Mock
+ private ServletRequest request;
+ @Mock
+ private SlingBindings bindings;
+
+ private static final String STRING_PARAM = "param1";
+ private static final String INTEGER_PARAM = "param2";
+ private static final String CLASS_PARAM = "param3";
+ private static final String STRING_VALUE = "myValue";
+ private static final int INTEGER_VALUE = 42;
+ private static final ResourceResolver CLASS_INSTANCE = mock(ResourceResolver.class);
+
+ @Before
+ public void setUp() {
+ when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+ when(bindings.get(STRING_PARAM)).thenReturn(STRING_VALUE);
+ when(bindings.get(INTEGER_PARAM)).thenReturn(INTEGER_VALUE);
+ when(bindings.get(CLASS_PARAM)).thenReturn(CLASS_INSTANCE);
+ }
+
+ @Test
+ public void testStringParam() {
+ Object result = injector.getValue(request, STRING_PARAM, String.class, null, null);
+ assertEquals(STRING_VALUE, result);
+ }
+
+ @Test
+ public void testIntegerParam() {
+ Object result = injector.getValue(request, INTEGER_PARAM, Integer.class, null, null);
+ assertEquals(INTEGER_VALUE, result);
+ }
+
+ @Test
+ public void testClassInstance() {
+ Object result = injector.getValue(request, CLASS_PARAM, ResourceResolver.class, null, null);
+ assertSame(CLASS_INSTANCE, result);
+ }
+
+ @Test
+ public void testNonMatchingClassInstance() {
+ Object result = injector.getValue(request, CLASS_PARAM, Resource.class, null, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testNonRequestAdaptable() {
+ Object result = injector.getValue(mock(ResourceResolver.class), STRING_PARAM, String.class, null, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testRequestThatDoesNotContainBindings() {
+ when(request.getAttribute(SlingBindings.class.getName())).thenReturn(null);
+ Object result = injector.getValue(request, STRING_PARAM, String.class, null, null);
+ assertNull(result);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/injectors/RequestAttributeInjectorTest.java b/src/test/java/org/apache/sling/models/impl/injectors/RequestAttributeInjectorTest.java
new file mode 100644
index 0000000..75ac1ca
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injectors/RequestAttributeInjectorTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.injectors;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
+public class RequestAttributeInjectorTest {
+
+ private RequestAttributeInjector injector = new RequestAttributeInjector();
+
+ @Mock
+ private ServletRequest request;
+
+ private static final String STRING_PARAM = "param1";
+ private static final String INTEGER_PARAM = "param2";
+ private static final String CLASS_PARAM = "param3";
+ private static final String STRING_VALUE = "myValue";
+ private static final int INTEGER_VALUE = 42;
+ private static final ResourceResolver CLASS_INSTANCE = mock(ResourceResolver.class);
+
+ @Before
+ public void setUp() {
+ when(request.getAttribute(STRING_PARAM)).thenReturn(STRING_VALUE);
+ when(request.getAttribute(INTEGER_PARAM)).thenReturn(INTEGER_VALUE);
+ when(request.getAttribute(CLASS_PARAM)).thenReturn(CLASS_INSTANCE);
+ }
+
+ @Test
+ public void testStringParam() {
+ Object result = injector.getValue(request, STRING_PARAM, String.class, null, null);
+ assertEquals(STRING_VALUE, result);
+ }
+
+ @Test
+ public void testIntegerParam() {
+ Object result = injector.getValue(request, INTEGER_PARAM, Integer.class, null, null);
+ assertEquals(INTEGER_VALUE, result);
+ }
+
+ @Test
+ public void testClassInstance() {
+ Object result = injector.getValue(request, CLASS_PARAM, ResourceResolver.class, null, null);
+ assertSame(CLASS_INSTANCE, result);
+ }
+
+ @Test
+ public void testNonMatchingClassInstance() {
+ Object result = injector.getValue(request, CLASS_PARAM, Resource.class, null, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testNonRequestAdaptable() {
+ Object result = injector.getValue(mock(ResourceResolver.class), STRING_PARAM, String.class, null, null);
+ assertNull(result);
+ }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/injectors/SelfInjectorTest.java b/src/test/java/org/apache/sling/models/impl/injectors/SelfInjectorTest.java
new file mode 100644
index 0000000..3e7251a
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injectors/SelfInjectorTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.injectors;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.*;
+
+import java.lang.reflect.AnnotatedElement;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
+public class SelfInjectorTest {
+
+ private SelfInjector injector = new SelfInjector();
+
+ @Mock
+ private SlingHttpServletRequest request;
+ @Mock
+ private AnnotatedElement annotatedElement;
+
+ @Test
+ public void testMatchingClass() {
+ Object result = injector.getValue(request, "notRelevant", SlingHttpServletRequest.class, annotatedElement, null);
+ assertSame(request, result);
+ }
+
+ @Test
+ public void testMatchingSubClass() {
+ Object result = injector.getValue(request, "notRelevant", HttpServletRequest.class, annotatedElement, null);
+ assertSame(request, result);
+ }
+
+ @Test
+ public void testNotMatchingClass() {
+ Object result = injector.getValue(request, "notRelevant", ResourceResolver.class, annotatedElement, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testWithNullName() {
+ Object result = injector.getValue(request, null, SlingHttpServletRequest.class, annotatedElement, null);
+ assertSame(request, result);
+ }
+
+ @Test
+ public void testMatchingClassWithSelfAnnotation() {
+ when(annotatedElement.isAnnotationPresent(Self.class)).thenReturn(true);
+ Object result = injector.getValue(request, "notRelevant", SlingHttpServletRequest.class, annotatedElement, null);
+ assertSame(request, result);
+ }
+
+ @Test
+ public void testNotMatchingClassWithSelfAnnotation() {
+ when(annotatedElement.isAnnotationPresent(Self.class)).thenReturn(true);
+ Object result = injector.getValue(request, "notRelevant", ResourceResolver.class, annotatedElement, null);
+ assertSame(request, result);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/injectors/ValueMapInjectorTest.java b/src/test/java/org/apache/sling/models/impl/injectors/ValueMapInjectorTest.java
new file mode 100644
index 0000000..cf3583a
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injectors/ValueMapInjectorTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.injectors;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+@SuppressWarnings("javadoc")
+public class ValueMapInjectorTest {
+
+ private ValueMapInjector injector = new ValueMapInjector();
+
+ @Mock
+ private ValueMap valueMap;
+
+ private static final String STRING_PARAM = "param1";
+ private static final String INTEGER_PARAM = "param2";
+ private static final String CLASS_PARAM = "param3";
+ private static final String STRING_VALUE = "myValue";
+ private static final int INTEGER_VALUE = 42;
+ private static final ResourceResolver CLASS_INSTANCE = mock(ResourceResolver.class);
+
+ @Before
+ public void setUp() {
+ when(valueMap.get(STRING_PARAM, String.class)).thenReturn(STRING_VALUE);
+ when(valueMap.get(INTEGER_PARAM, Integer.class)).thenReturn(INTEGER_VALUE);
+ when(valueMap.get(CLASS_PARAM, ResourceResolver.class)).thenReturn(CLASS_INSTANCE);
+ }
+
+ @Test
+ public void testStringParam() {
+ Object result = injector.getValue(valueMap, STRING_PARAM, String.class, null, null);
+ assertEquals(STRING_VALUE, result);
+ }
+
+ @Test
+ public void testIntegerParam() {
+ Object result = injector.getValue(valueMap, INTEGER_PARAM, Integer.class, null, null);
+ assertEquals(INTEGER_VALUE, result);
+ }
+
+ @Test
+ public void testClassInstance() {
+ Object result = injector.getValue(valueMap, CLASS_PARAM, ResourceResolver.class, null, null);
+ assertSame(CLASS_INSTANCE, result);
+ }
+
+ @Test
+ public void testNonMatchingClassInstance() {
+ Object result = injector.getValue(valueMap, CLASS_PARAM, Resource.class, null, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testNonValueMapAdaptable() {
+ Object result = injector.getValue(mock(ResourceResolver.class), STRING_PARAM, String.class, null, null);
+ assertNull(result);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/DirectCyclicSelfDependencyModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/DirectCyclicSelfDependencyModel.java
new file mode 100644
index 0000000..c09603b
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/DirectCyclicSelfDependencyModel.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testmodels.classes;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+
+@Model(adaptables=SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class DirectCyclicSelfDependencyModel {
+
+ @Self
+ private DirectCyclicSelfDependencyModel modelSelf;
+
+ public DirectCyclicSelfDependencyModel getModelSelf() {
+ return modelSelf;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelA.java b/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelA.java
new file mode 100644
index 0000000..cd329a5
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelA.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testmodels.classes;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+
+@Model(adaptables=SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class IndirectCyclicSelfDependencyModelA {
+
+ @Self
+ private IndirectCyclicSelfDependencyModelB dependencyB;
+
+ public IndirectCyclicSelfDependencyModelB getDependencyB() {
+ return dependencyB;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelB.java b/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelB.java
new file mode 100644
index 0000000..68dfb15
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/IndirectCyclicSelfDependencyModelB.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testmodels.classes;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+
+@Model(adaptables=SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class IndirectCyclicSelfDependencyModelB {
+
+ @Self
+ private IndirectCyclicSelfDependencyModelA dependencyA;
+
+ public IndirectCyclicSelfDependencyModelA getDependencyA() {
+ return dependencyA;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelA.java b/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelA.java
new file mode 100644
index 0000000..6df4527
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelA.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testmodels.classes;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+
+@Model(adaptables=SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class SelfDependencyModelA {
+
+ @Self
+ private SelfDependencyModelB dependencyB;
+
+ public SelfDependencyModelB getDependencyB() {
+ return dependencyB;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelB.java b/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelB.java
new file mode 100644
index 0000000..6d7757c
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SelfDependencyModelB.java
@@ -0,0 +1,36 @@
+/*
+ * 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.testmodels.classes;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+
+@Model(adaptables=SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class SelfDependencyModelB {
+
+ @Self
+ private SlingHttpServletRequest request;
+
+ public SlingHttpServletRequest getRequest() {
+ return request;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/BindingsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/BindingsModel.java
new file mode 100644
index 0000000..595663a
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/BindingsModel.java
@@ -0,0 +1,41 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class BindingsModel {
+
+ private final SlingScriptHelper sling;
+
+ @Inject
+ public BindingsModel(@Named("sling") SlingScriptHelper sling) {
+ this.sling = sling;
+ }
+
+ public SlingScriptHelper getSling() {
+ return sling;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultPrimitivesModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultPrimitivesModel.java
new file mode 100644
index 0000000..6de8e7f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultPrimitivesModel.java
@@ -0,0 +1,62 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Default;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+@SuppressWarnings("javadoc")
+public class DefaultPrimitivesModel {
+
+ private final boolean booleanProperty;
+ private final boolean[] booleanArrayProperty;
+ private final long longProperty;
+ private final long[] longArrayProperty;
+
+ @Inject
+ public DefaultPrimitivesModel(
+ @Default(booleanValues = true) boolean booleanProperty,
+ @Default(booleanValues = { true, true }) boolean[] booleanArrayProperty,
+ @Default(longValues = 1L) long longProperty,
+ @Default(longValues = { 1L, 1L }) long[] longArrayProperty
+ ) {
+ this.booleanProperty = booleanProperty;
+ this.booleanArrayProperty = booleanArrayProperty;
+ this.longProperty = longProperty;
+ this.longArrayProperty = longArrayProperty;
+ }
+
+ public boolean getBooleanProperty() {
+ return booleanProperty;
+ }
+
+ public boolean[] getBooleanArrayProperty() {
+ return booleanArrayProperty;
+ }
+
+ public long getLongProperty() {
+ return longProperty;
+ }
+
+ public long[] getLongArrayProperty() {
+ return longArrayProperty;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultStringModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultStringModel.java
new file mode 100644
index 0000000..7c7accf
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultStringModel.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Default;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+@SuppressWarnings("javadoc")
+public class DefaultStringModel {
+
+ private final String firstProperty;
+ private final String[] secondProperty;
+
+ @Inject
+ public DefaultStringModel(
+ @Default(values = "firstDefault") String firstProperty,
+ @Default(values = { "firstDefault", "secondDefault" }) String[] secondProperty
+ ) {
+ this.firstProperty = firstProperty;
+ this.secondProperty = secondProperty;
+ }
+
+ public String getFirstProperty() {
+ return firstProperty;
+ }
+
+ public String[] getSecondProperty() {
+ return secondProperty;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultWrappersModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultWrappersModel.java
new file mode 100644
index 0000000..bc50f0d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/DefaultWrappersModel.java
@@ -0,0 +1,63 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Default;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+@SuppressWarnings("javadoc")
+public class DefaultWrappersModel {
+
+ private final Boolean booleanWrapperProperty;
+ private final Boolean[] booleanWrapperArrayProperty;
+ private final Long longWrapperProperty;
+ private final Long[] longWrapperArrayProperty;
+
+ @Inject
+ public DefaultWrappersModel(
+ @Default(booleanValues = true) Boolean booleanWrapperProperty,
+ @Default(booleanValues = { true, true }) Boolean[] booleanWrapperArrayProperty,
+ @Default(longValues = 1L) Long longWrapperProperty,
+ @Default(longValues = { 1L, 1L }) Long[] longWrapperArrayProperty
+ ) {
+ this.booleanWrapperProperty = booleanWrapperProperty;
+ this.booleanWrapperArrayProperty = booleanWrapperArrayProperty;
+ this.longWrapperProperty = longWrapperProperty;
+ this.longWrapperArrayProperty = longWrapperArrayProperty;
+ }
+
+ public Boolean getBooleanWrapperProperty() {
+ return booleanWrapperProperty;
+ }
+
+ public Boolean[] getBooleanWrapperArrayProperty() {
+ return booleanWrapperArrayProperty;
+ }
+
+ public Long getLongWrapperProperty() {
+ return longWrapperProperty;
+ }
+
+ public Long[] getLongWrapperArrayProperty() {
+ return longWrapperArrayProperty;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/InjectorSpecificAnnotationModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/InjectorSpecificAnnotationModel.java
new file mode 100644
index 0000000..2a6873f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/InjectorSpecificAnnotationModel.java
@@ -0,0 +1,91 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.ChildResource;
+import org.apache.sling.models.annotations.injectorspecific.OSGiService;
+import org.apache.sling.models.annotations.injectorspecific.RequestAttribute;
+import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
+import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
+import org.slf4j.Logger;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class InjectorSpecificAnnotationModel {
+
+ private final String first;
+ private final String secondWithOtherName;
+ private final Logger log;
+ private final SlingScriptHelper helper;
+ private final Object requestAttribute;
+ private final Logger service;
+ private final Resource childResource;
+
+ @Inject
+ public InjectorSpecificAnnotationModel(
+ @ValueMapValue(name = "first", optional = true) String first,
+ @ValueMapValue(name = "second", optional = true) String secondWithOtherName,
+ @ValueMapValue(name = "log", optional = true) Logger log,
+ @ScriptVariable(optional = true, name = "sling") SlingScriptHelper helper,
+ @RequestAttribute(optional = true, name = "attribute") Object requestAttribute,
+ @OSGiService(optional = true) Logger service,
+ @ChildResource(optional = true, name = "child1") Resource childResource
+ ) {
+ this.first = first;
+ this.secondWithOtherName = secondWithOtherName;
+ this.log = log;
+ this.helper = helper;
+ this.requestAttribute = requestAttribute;
+ this.service = service;
+ this.childResource = childResource;
+ }
+
+ public String getFirst() {
+ return first;
+ }
+
+ public String getSecond() {
+ return secondWithOtherName;
+ }
+
+ public Logger getLog() {
+ return log;
+ }
+
+ public Logger getService() {
+ return service;
+ }
+
+ public SlingScriptHelper getHelper() {
+ return helper;
+ }
+
+ public Object getRequestAttribute() {
+ return requestAttribute;
+ }
+
+ public Resource getChildResource() {
+ return childResource;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/ListOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/ListOSGiModel.java
new file mode 100644
index 0000000..66e8e2f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/ListOSGiModel.java
@@ -0,0 +1,42 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.testmodels.interfaces.ServiceInterface;
+
+@Model(adaptables = Resource.class)
+@SuppressWarnings("javadoc")
+public class ListOSGiModel {
+
+ private final List<ServiceInterface> services;
+
+ @Inject
+ public ListOSGiModel(List<ServiceInterface> services) {
+ this.services = services;
+ }
+
+ public List<ServiceInterface> getServices() {
+ return services;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/NoNameModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/NoNameModel.java
new file mode 100644
index 0000000..78e2928
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/NoNameModel.java
@@ -0,0 +1,42 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class NoNameModel {
+
+ private final SlingScriptHelper sling;
+
+ @Inject
+ public NoNameModel(SlingScriptHelper sling) {
+ this.sling = sling;
+ }
+
+ public SlingScriptHelper getSling() {
+ return sling;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/SimpleOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/SimpleOSGiModel.java
new file mode 100644
index 0000000..d48c2c6
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/SimpleOSGiModel.java
@@ -0,0 +1,40 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.testmodels.interfaces.ServiceInterface;
+
+@Model(adaptables = Resource.class)
+@SuppressWarnings("javadoc")
+public class SimpleOSGiModel {
+
+ private final ServiceInterface service;
+
+ @Inject
+ public SimpleOSGiModel(ServiceInterface service) {
+ this.service = service;
+ }
+
+ public ServiceInterface getService() {
+ return service;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/WithThreeConstructorsOneInjectModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/WithThreeConstructorsOneInjectModel.java
new file mode 100644
index 0000000..2e97072
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorinjection/WithThreeConstructorsOneInjectModel.java
@@ -0,0 +1,59 @@
+/*
+ * 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.testmodels.classes.constructorinjection;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+@SuppressWarnings("javadoc")
+public class WithThreeConstructorsOneInjectModel {
+
+ private SlingHttpServletRequest request;
+
+ @Inject
+ private int attribute;
+
+ private String attribute2;
+
+ public WithThreeConstructorsOneInjectModel(SlingHttpServletRequest request) {
+ this.request = request;
+ }
+
+ public WithThreeConstructorsOneInjectModel() {
+ }
+
+ @Inject
+ public WithThreeConstructorsOneInjectModel(@Named("attribute2") String attribute2) {
+ this.attribute2 = attribute2;
+ }
+
+ public int getAttribute() {
+ return attribute;
+ }
+
+ public String getAttribute2() {
+ return attribute2;
+ }
+
+ public SlingHttpServletRequest getRequest() {
+ return request;
+ }
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.