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:17 UTC
[sling-org-apache-sling-models-impl] 04/16: SLING-3499 - adding
support for custom annotation per injector (thanks Konrad Windszus for the
patch!)
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.0.6
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit 189f1d3262130da997743732e1b84b1e89cd63e3
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Wed Jun 4 19:38:55 2014 +0000
SLING-3499 - adding support for custom annotation per injector (thanks Konrad Windszus for the patch!)
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1600469 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 2 +-
.../sling/models/impl/ModelAdapterFactory.java | 424 +++++++++++----------
.../models/impl/injectors/BindingsInjector.java | 43 ++-
.../impl/injectors/ChildResourceInjector.java | 60 ++-
.../models/impl/injectors/OSGiServiceInjector.java | 64 +++-
.../impl/injectors/RequestAttributeInjector.java | 48 ++-
.../models/impl/injectors/ValueMapInjector.java | 62 ++-
.../sling/models/impl/CustomInjectorTest.java | 90 +++++
.../impl/InjectorSpecificAnnotationTest.java | 180 +++++++++
.../sling/models/impl/MultipleInjectorTest.java | 1 +
.../sling/models/impl/PostConstructTest.java | 2 +-
.../models/impl/injector/CustomAnnotation.java | 32 ++
.../impl/injector/CustomAnnotationInjector.java | 66 ++++
.../models/impl/injector/SimpleInjector.java} | 21 +-
.../classes/InjectorSpecificAnnotationModel.java | 82 ++++
15 files changed, 941 insertions(+), 236 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2197578..dd759b7 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.0</version>
+ <version>1.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
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 21ddc76..483215a 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -34,7 +34,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -65,6 +64,9 @@ 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.Injector;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
@@ -177,8 +179,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
if (type.isInterface()) {
InvocationHandler handler = createInvocationHandler(adaptable, type);
if (handler != null) {
- return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type },
- handler);
+ return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
} else {
return null;
}
@@ -196,12 +197,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
Set<Field> result = new HashSet<Field>();
while (type != null) {
Field[] fields = type.getDeclaredFields();
- for (Field field : fields) {
- Inject injection = field.getAnnotation(Inject.class);
- if (injection != null) {
- result.add(field);
- }
- }
+ addAnnotated(fields, result);
type = type.getSuperclass();
}
return result;
@@ -211,76 +207,124 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
Set<Method> result = new HashSet<Method>();
while (type != null) {
Method[] methods = type.getDeclaredMethods();
- for (Method method : methods) {
- Inject injection = method.getAnnotation(Inject.class);
- if (injection != null) {
- result.add(method);
- }
- }
+ addAnnotated(methods, result);
type = type.getSuperclass();
}
return result;
}
- private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type) {
- Set<Method> injectableMethods = collectInjectableMethods(type);
- Map<Method, Object> methods = new HashMap<Method, Object>();
- MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods);
+ private <T extends AnnotatedElement> void addAnnotated(T[] elements, Set<T> set) {
+ for (T element : elements) {
+ Inject injection = getAnnotation(element, Inject.class);
+ if (injection != null) {
+ set.add(element);
+ } else {
+ InjectAnnotation modelInject = getAnnotation(element, InjectAnnotation.class);
+ if (modelInject != null) {
+ set.add(element);
+ }
+ }
+ }
+ }
- DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+ private static interface InjectCallback {
+ /**
+ * Is called each time when the given value should be injected into the given element
+ * @param element
+ * @param value
+ * @return true if injection was successful otherwise false
+ */
+ public boolean inject(AnnotatedElement element, Object value);
+ }
+
+ private static class SetFieldCallback implements InjectCallback {
+
+ private final Object object;
+
+ private SetFieldCallback(Object object) {
+ this.object = object;
+ }
+
+ @Override
+ public boolean inject(AnnotatedElement element, Object value) {
+ return setField((Field) element, object, value);
+ }
+ }
+
+ private static class SetMethodsCallback implements InjectCallback {
+
+ private final Map<Method, Object> methods;
+
+ private SetMethodsCallback( Map<Method, Object> methods) {
+ this.methods = methods;
+ }
+
+ @Override
+ public boolean inject(AnnotatedElement element, Object value) {
+ return setMethod((Method) element, methods, value);
+ }
+ }
+
+ private boolean injectFieldOrMethod(final AnnotatedElement element, final Object adaptable, final Type type,
+ final DisposalCallbackRegistry registry, InjectCallback callback) {
+
+ InjectAnnotationProcessor annotationProcessor = null;
+ String source = getSource(element);
+ boolean wasInjectionSuccessful = false;
+ // find the right injector
for (Injector injector : sortedInjectors) {
- Iterator<Method> it = injectableMethods.iterator();
- while (it.hasNext()) {
- Method method = it.next();
- String source = getSource(method);
- if (source == null || source.equals(injector.getName())) {
- String name = getName(method);
- Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
- Object injectionAdaptable = getAdaptable(adaptable, method);
- if (injectionAdaptable != null) {
- Object value = injector.getValue(injectionAdaptable, name, returnType, method, registry);
- if (setMethod(method, methods, value)) {
- it.remove();
- }
+ if (source == null || source.equals(injector.getName())) {
+ // get annotation processor
+ if (injector instanceof InjectAnnotationProcessorFactory) {
+ annotationProcessor = ((InjectAnnotationProcessorFactory) injector).createAnnotationProcessor(adaptable,
+ element);
+ }
+
+ 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 injection failed, use default
+ if (!wasInjectionSuccessful) {
+ wasInjectionSuccessful = injectDefaultValue(element, type, annotationProcessor, callback);
+ }
- registry.seal();
-
- Iterator<Method> it = injectableMethods.iterator();
- while (it.hasNext()) {
- Method method = it.next();
- Default defaultAnnotation = method.getAnnotation(Default.class);
- if (defaultAnnotation != null) {
- Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
- Object value = getDefaultValue(defaultAnnotation, returnType);
- if (setMethod(method, methods, value)) {
- it.remove();
- }
- }
+ // if default is not set, check if mandatory
+ if (!wasInjectionSuccessful && !isOptional(element, annotationProcessor)) {
+ return false;
}
+ return true;
+ }
- if (injectableMethods.isEmpty()) {
- return handler;
- } else {
- Set<Method> requiredMethods = new HashSet<Method>();
- for (Method method : injectableMethods) {
- if (method.getAnnotation(Optional.class) == null) {
- requiredMethods.add(method);
- }
- }
+ private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type) {
+ Set<Method> injectableMethods = collectInjectableMethods(type);
+ final Map<Method, Object> methods = new HashMap<Method, Object>();
+ SetMethodsCallback callback = new SetMethodsCallback(methods);
+ MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods);
- if (!requiredMethods.isEmpty()) {
- log.warn("Required methods {} on model class {} were not able to be injected.", requiredMethods,
- type);
- return null;
- } else {
- return handler;
+ DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+ Set<Method> requiredMethods = new HashSet<Method>();
+
+ for (Method method : injectableMethods) {
+ Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
+ if (!injectFieldOrMethod(method, adaptable, returnType, registry, callback)) {
+ requiredMethods.add(method);
}
}
+ registry.seal();
+ if (!requiredMethods.isEmpty()) {
+ log.warn("Required methods {} on model interface {} were not able to be injected.", requiredMethods, type);
+ return null;
+ }
+ return handler;
}
private DisposalCallbackRegistryImpl createAndRegisterCallbackRegistry(Object object) {
@@ -367,160 +411,133 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
} else {
object = constructorToUse.newInstance();
}
+ InjectCallback callback = new SetFieldCallback(object);
DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(object);
- for (Injector injector : sortedInjectors) {
- Iterator<Field> it = injectableFields.iterator();
- while (it.hasNext()) {
- Field field = it.next();
- String source = getSource(field);
- if (source == null || source.equals(injector.getName())) {
- String name = getName(field);
- Type fieldType = mapPrimitiveClasses(field.getGenericType());
- Object injectionAdaptable = getAdaptable(adaptable, field);
- if (injectionAdaptable != null) {
- Object value = injector.getValue(injectionAdaptable, name, fieldType, field, registry);
- if (setField(field, object, value)) {
- it.remove();
- }
- }
- }
+ Set<Field> requiredFields = new HashSet<Field>();
+
+ for (Field field : injectableFields) {
+ Type fieldType = mapPrimitiveClasses(field.getGenericType());
+ if (!injectFieldOrMethod(field, adaptable, fieldType, registry, callback)) {
+ requiredFields.add(field);
}
}
registry.seal();
+ if (!requiredFields.isEmpty()) {
+ log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields, type);
+ return null;
+ }
+ try {
+ invokePostConstruct(object);
+ return object;
+ } catch (Exception e) {
+ log.error("Unable to invoke post construct method.", e);
+ return null;
+ }
- Iterator<Field> it = injectableFields.iterator();
- while (it.hasNext()) {
- Field field = it.next();
- Default defaultAnnotation = field.getAnnotation(Default.class);
- if (defaultAnnotation != null) {
- Type fieldType = mapPrimitiveClasses(field.getGenericType());
- Object value = getDefaultValue(defaultAnnotation, fieldType);
- if (setField(field, object, value)) {
- it.remove();
- }
+ }
+
+ private boolean isOptional(AnnotatedElement point, InjectAnnotationProcessor annotationProcessor) {
+ if (annotationProcessor != null) {
+ Boolean isOptional = annotationProcessor.isOptional();
+ if (isOptional != null) {
+ return isOptional.booleanValue();
}
}
+ return (point.getAnnotation(Optional.class) != null);
+ }
- if (injectableFields.isEmpty()) {
- try {
- invokePostConstruct(object);
- return object;
- } catch (Exception e) {
- log.error("Unable to invoke post construct method.", e);
- return null;
- }
- } else {
- Set<Field> requiredFields = new HashSet<Field>();
- for (Field field : injectableFields) {
- if (field.getAnnotation(Optional.class) == null) {
- requiredFields.add(field);
- }
- }
+ private boolean injectDefaultValue(AnnotatedElement point, Type type, InjectAnnotationProcessor processor,
+ InjectCallback callback) {
- if (!requiredFields.isEmpty()) {
- log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields,
- type);
- return null;
- } else {
- try {
- invokePostConstruct(object);
- return object;
- } catch (Exception e) {
- log.error("Unable to invoke post construct method.", e);
- return null;
- }
+ if (processor != null) {
+ if (processor.hasDefault()) {
+ return callback.inject(point, processor.getDefault());
}
}
- }
+ Default defaultAnnotation = point.getAnnotation(Default.class);
+ if (defaultAnnotation == null) {
+ return false;
+ }
+
+ type = mapPrimitiveClasses(type);
+ Object value = null;
- private Object getDefaultValue(Default defaultAnnotation, Type type) {
if (type instanceof Class) {
Class<?> injectedClass = (Class<?>) type;
if (injectedClass.isArray()) {
Class<?> componentType = injectedClass.getComponentType();
if (componentType == String.class) {
- return defaultAnnotation.values();
- }
- if (componentType == Integer.TYPE) {
- return defaultAnnotation.intValues();
- }
- if (componentType == Integer.class) {
- return ArrayUtils.toObject(defaultAnnotation.intValues());
- }
- if (componentType == Long.TYPE) {
- return defaultAnnotation.longValues();
- }
- if (componentType == Long.class) {
- return ArrayUtils.toObject(defaultAnnotation.longValues());
- }
- if (componentType == Boolean.TYPE) {
- return defaultAnnotation.booleanValues();
- }
- if (componentType == Boolean.class) {
- return ArrayUtils.toObject(defaultAnnotation.booleanValues());
- }
- if (componentType == Short.TYPE) {
- return defaultAnnotation.shortValues();
- }
- if (componentType == Short.class) {
- return ArrayUtils.toObject(defaultAnnotation.shortValues());
- }
- if (componentType == Float.TYPE) {
- return defaultAnnotation.floatValues();
- }
- if (componentType == Float.class) {
- return ArrayUtils.toObject(defaultAnnotation.floatValues());
- }
- if (componentType == Double.TYPE) {
- return defaultAnnotation.doubleValues();
- }
- if (componentType == Double.class) {
- return ArrayUtils.toObject(defaultAnnotation.doubleValues());
+ value = defaultAnnotation.values();
+ } else if (componentType == Integer.TYPE) {
+ value = defaultAnnotation.intValues();
+ } else if (componentType == Integer.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.intValues());
+ } else if (componentType == Long.TYPE) {
+ value = defaultAnnotation.longValues();
+ } else if (componentType == Long.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.longValues());
+ } else if (componentType == Boolean.TYPE) {
+ value = defaultAnnotation.booleanValues();
+ } else if (componentType == Boolean.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.booleanValues());
+ } else if (componentType == Short.TYPE) {
+ value = defaultAnnotation.shortValues();
+ } else if (componentType == Short.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.shortValues());
+ } else if (componentType == Float.TYPE) {
+ value = defaultAnnotation.floatValues();
+ } else if (componentType == Float.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.floatValues());
+ } else if (componentType == Double.TYPE) {
+ value = defaultAnnotation.doubleValues();
+ } else if (componentType == Double.class) {
+ value = ArrayUtils.toObject(defaultAnnotation.doubleValues());
+ } else {
+ log.warn("Default values for {} are not supported", componentType);
+ return false;
}
-
- log.warn("Default values for {} are not supported", componentType);
- return null;
} else {
if (injectedClass == String.class) {
- return defaultAnnotation.values()[0];
- }
- if (injectedClass == Integer.class) {
- return defaultAnnotation.intValues()[0];
- }
- if (injectedClass == Long.class) {
- return defaultAnnotation.longValues()[0];
- }
- if (injectedClass == Boolean.class) {
- return defaultAnnotation.booleanValues()[0];
- }
- if (injectedClass == Short.class) {
- return defaultAnnotation.shortValues()[0];
- }
- if (injectedClass == Float.class) {
- return defaultAnnotation.floatValues()[0];
- }
- if (injectedClass == Double.class) {
- return defaultAnnotation.doubleValues()[0];
+ value = defaultAnnotation.values()[0];
+ } else if (injectedClass == Integer.class) {
+ value = defaultAnnotation.intValues()[0];
+ } else if (injectedClass == Long.class) {
+ value = defaultAnnotation.longValues()[0];
+ } else if (injectedClass == Boolean.class) {
+ value = defaultAnnotation.booleanValues()[0];
+ } else if (injectedClass == Short.class) {
+ value = defaultAnnotation.shortValues()[0];
+ } else if (injectedClass == Float.class) {
+ value = defaultAnnotation.floatValues()[0];
+ } else if (injectedClass == Double.class) {
+ value = defaultAnnotation.doubleValues()[0];
+ } else {
+ log.warn("Default values for {} are not supported", injectedClass);
+ return false;
}
-
- log.warn("Default values for {} are not supported", injectedClass);
- return null;
}
} else {
log.warn("Cannot provide default for {}", type);
- return null;
+ return false;
}
+ return callback.inject(point, value);
}
- private Object getAdaptable(Object adaptable, AnnotatedElement point) {
- Via viaAnnotation = point.getAnnotation(Via.class);
- if (viaAnnotation == null) {
- return adaptable;
+ private Object getAdaptable(Object adaptable, AnnotatedElement point, InjectAnnotationProcessor processor) {
+ String viaPropertyName = null;
+ if (processor != null) {
+ viaPropertyName = processor.getVia();
+ }
+ if (viaPropertyName == null) {
+ Via viaAnnotation = point.getAnnotation(Via.class);
+ if (viaAnnotation == null) {
+ return adaptable;
+ }
+ viaPropertyName = viaAnnotation.value();
}
- String viaPropertyName = viaAnnotation.value();
try {
return PropertyUtils.getProperty(adaptable, viaPropertyName);
} catch (Exception e) {
@@ -529,19 +546,33 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
- private String getName(Field field) {
- Named named = field.getAnnotation(Named.class);
+ private String getName(AnnotatedElement element, InjectAnnotationProcessor processor) {
+ // try to get the name from injector-specific annotation
+ if (processor != null) {
+ String name = processor.getName();
+ if (name != null) {
+ return name;
+ }
+ }
+ // alternative for name attribute
+ Named named = element.getAnnotation(Named.class);
if (named != null) {
return named.value();
}
+ if (element instanceof Method) {
+ return getNameFromMethod((Method) element);
+ } else if (element instanceof Field) {
+ return getNameFromField((Field) element);
+ } else {
+ throw new IllegalArgumentException("The given element must be either method or field but is " + element);
+ }
+ }
+
+ private String getNameFromField(Field field) {
return field.getName();
}
- private String getName(Method method) {
- Named named = method.getAnnotation(Named.class);
- if (named != null) {
- return named.value();
- }
+ private String getNameFromMethod(Method method) {
String methodName = method.getName();
if (methodName.startsWith("get")) {
return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
@@ -606,7 +637,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
return type;
}
- private boolean setField(Field field, Object createdObject, Object value) {
+ private static boolean setField(Field field, Object createdObject, Object value) {
if (value != null) {
if (!isAcceptableType(field.getType(), value) && value instanceof Adaptable) {
value = ((Adaptable) value).adaptTo(field.getType());
@@ -634,7 +665,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
- private boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
+ private static boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
if (value != null) {
if (!isAcceptableType(method.getReturnType(), value) && value instanceof Adaptable) {
value = ((Adaptable) value).adaptTo(method.getReturnType());
@@ -649,7 +680,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
}
}
- private boolean isAcceptableType(Class<?> type, Object value) {
+ private static boolean isAcceptableType(Class<?> type, Object value) {
if (type.isInstance(value)) {
return true;
}
@@ -690,8 +721,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
properties.put("scheduler.concurrent", false);
properties.put("scheduler.period", Long.valueOf(30));
- this.jobRegistration = bundleContext.registerService(Runnable.class.getName(), this,
- properties);
+ this.jobRegistration = bundleContext.registerService(Runnable.class.getName(), this, properties);
this.listener = new ModelPackageBundleListener(ctx.getBundleContext(), this);
@@ -703,7 +733,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable {
printerProps.put("felix.webconsole.configprinter.modes", "always");
this.configPrinterRegistration = bundleContext.registerService(Object.class.getName(),
- new ModelConfigurationPrinter(this), printerProps);
+ new ModelConfigurationPrinter(this), printerProps);
}
@Deactivate
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
index aa5ed5c..e2140e2 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
@@ -25,8 +25,12 @@ 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.api.scripting.SlingBindings;
+import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,7 +38,7 @@ import org.slf4j.LoggerFactory;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 1000)
-public class BindingsInjector implements Injector {
+public class BindingsInjector implements Injector, InjectAnnotationProcessorFactory {
private static final Logger log = LoggerFactory.getLogger(BindingsInjector.class);
@@ -52,7 +56,8 @@ public class BindingsInjector implements Injector {
}
}
- public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+ public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
SlingBindings bindings = getBindings(adaptable);
if (bindings == null) {
return null;
@@ -75,4 +80,38 @@ public class BindingsInjector implements Injector {
}
}
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ ScriptVariable annotation = element.getAnnotation(ScriptVariable.class);
+ if (annotation != null) {
+ return new ScriptVariableAnnotationProcessor(annotation);
+ }
+ return null;
+ }
+
+ private static class ScriptVariableAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final ScriptVariable annotation;
+
+ public ScriptVariableAnnotationProcessor(ScriptVariable annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+
+ @Override
+ public String getName() {
+ // since null is not allowed as default value in annotations, the empty string means, the default should be
+ // used!
+ if (annotation.name().isEmpty()) {
+ return null;
+ }
+ return annotation.name();
+ }
+ }
+
}
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
index 194db83..500473b 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
@@ -19,18 +19,24 @@ package org.apache.sling.models.impl.injectors;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
+import org.apache.commons.lang.StringUtils;
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.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
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;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 3000)
-public class ChildResourceInjector implements Injector {
+public class ChildResourceInjector implements Injector, InjectAnnotationProcessorFactory {
@Override
public String getName() {
@@ -38,7 +44,8 @@ public class ChildResourceInjector implements Injector {
}
@Override
- public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+ public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
if (adaptable instanceof Resource) {
return ((Resource) adaptable).getChild(name);
} else {
@@ -46,4 +53,53 @@ public class ChildResourceInjector implements Injector {
}
}
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ ChildResource annotation = element.getAnnotation(ChildResource.class);
+ if (annotation != null) {
+ return new ChildResourceAnnotationProcessor(annotation, adaptable);
+ }
+ return null;
+ }
+
+ private static class ChildResourceAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final ChildResource annotation;
+ private final Object adaptable;
+
+ public ChildResourceAnnotationProcessor(ChildResource annotation, Object adaptable) {
+ this.annotation = annotation;
+ this.adaptable = adaptable;
+ }
+
+ @Override
+ public String getName() {
+ // since null is not allowed as default value in annotations, the empty string means, the default should be
+ // used!
+ if (annotation.name().isEmpty()) {
+ return null;
+ }
+ return annotation.name();
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+
+ @Override
+ public String getVia() {
+ if (StringUtils.isNotBlank(annotation.via())) {
+ return annotation.via();
+ }
+ // automatically go via resource, if this is the httprequest
+ if (adaptable instanceof SlingHttpServletRequest) {
+ return "resource";
+ } else {
+ return null;
+ }
+ }
+ }
+
}
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 59a9636..fb85dc0 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
@@ -27,6 +27,7 @@ import java.util.List;
import javax.servlet.ServletRequest;
+import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
@@ -34,9 +35,13 @@ import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
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.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.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
@@ -48,7 +53,7 @@ import org.slf4j.LoggerFactory;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 5000)
-public class OSGiServiceInjector implements Injector {
+public class OSGiServiceInjector implements Injector, InjectAnnotationProcessorFactory {
private static final Logger log = LoggerFactory.getLogger(OSGiServiceInjector.class);
@@ -64,17 +69,25 @@ public class OSGiServiceInjector implements Injector {
this.bundleContext = ctx.getBundleContext();
}
- public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
- Filter filter = element.getAnnotation(Filter.class);
+ public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
+ OSGiService annotation = element.getAnnotation(OSGiService.class);
String filterString = null;
- if (filter != null) {
- filterString = filter.value();
+ if (annotation != null) {
+ if (StringUtils.isNotBlank(annotation.filter())) {
+ filterString = annotation.filter();
+ }
+ } else {
+ Filter filter = element.getAnnotation(Filter.class);
+ if (filter != null) {
+ filterString = filter.value();
+ }
}
-
return getValue(adaptable, type, filterString, callbackRegistry);
}
- private <T> Object getService(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) {
+ private <T> Object getService(Object adaptable, Class<T> type, String filter,
+ DisposalCallbackRegistry callbackRegistry) {
SlingScriptHelper helper = getScriptHelper(adaptable);
if (helper != null) {
@@ -100,7 +113,8 @@ public class OSGiServiceInjector implements Injector {
}
}
- private <T> Object[] getServices(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) {
+ private <T> Object[] getServices(Object adaptable, Class<T> type, String filter,
+ DisposalCallbackRegistry callbackRegistry) {
SlingScriptHelper helper = getScriptHelper(adaptable);
if (helper != null) {
@@ -147,7 +161,8 @@ public class OSGiServiceInjector implements Injector {
if (type instanceof Class) {
Class<?> injectedClass = (Class<?>) type;
if (injectedClass.isArray()) {
- Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString, callbackRegistry);
+ Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString,
+ callbackRegistry);
if (services == null) {
return null;
}
@@ -180,16 +195,16 @@ public class OSGiServiceInjector implements Injector {
return null;
}
}
-
+
private static class Callback implements DisposalCallback {
private final ServiceReference[] refs;
private final BundleContext context;
-
+
public Callback(ServiceReference[] refs, BundleContext context) {
this.refs = refs;
this.context = context;
}
-
+
@Override
public void onDisposed() {
if (refs != null) {
@@ -200,4 +215,29 @@ public class OSGiServiceInjector implements Injector {
}
}
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ OSGiService annotation = element.getAnnotation(OSGiService.class);
+ if (annotation != null) {
+ return new OSGiServiceAnnotationProcessor(annotation);
+ }
+ return null;
+ }
+
+ private static class OSGiServiceAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final OSGiService annotation;
+
+ public OSGiServiceAnnotationProcessor(OSGiService annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+ }
+
+
}
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
index fb73063..23c3907 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
@@ -24,8 +24,12 @@ import javax.servlet.ServletRequest;
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.RequestAttribute;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,22 +37,23 @@ import org.slf4j.LoggerFactory;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 4000)
-public class RequestAttributeInjector implements Injector {
+public class RequestAttributeInjector implements Injector, InjectAnnotationProcessorFactory {
private static final Logger log = LoggerFactory.getLogger(RequestAttributeInjector.class);
-
+
@Override
public String getName() {
return "request-attributes";
}
@Override
- public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+ public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
if (!(adaptable instanceof ServletRequest)) {
return null;
} else if (declaredType instanceof Class<?>) {
Class<?> clazz = (Class<?>) declaredType;
- Object attribute = ((ServletRequest)adaptable).getAttribute(name);
+ Object attribute = ((ServletRequest) adaptable).getAttribute(name);
if (clazz.isInstance(attribute)) {
return attribute;
} else {
@@ -60,4 +65,39 @@ public class RequestAttributeInjector implements Injector {
}
}
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ RequestAttribute annotation = element.getAnnotation(RequestAttribute.class);
+ if (annotation != null) {
+ return new RequestAttributeAnnotationProcessor(annotation);
+ }
+ return null;
+ }
+
+ private static class RequestAttributeAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final RequestAttribute annotation;
+
+ public RequestAttributeAnnotationProcessor(RequestAttribute annotation) {
+ this.annotation = annotation;
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+
+ @Override
+ public String getName() {
+ // since null is not allowed as default value in annotations, the empty string means, the default should be
+ // used!
+ if (annotation.name().isEmpty()) {
+ return null;
+ }
+ return annotation.name();
+ }
+ }
+
+
}
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
index f6bea43..b5a6a2d 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
@@ -19,13 +19,19 @@ package org.apache.sling.models.impl.injectors;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
+import org.apache.commons.lang.StringUtils;
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.api.SlingHttpServletRequest;
import org.apache.sling.api.adapter.Adaptable;
import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,16 +39,17 @@ import org.slf4j.LoggerFactory;
@Component
@Service
@Property(name = Constants.SERVICE_RANKING, intValue = 2000)
-public class ValueMapInjector implements Injector {
+public class ValueMapInjector implements InjectAnnotationProcessorFactory, Injector {
private static final Logger log = LoggerFactory.getLogger(ValueMapInjector.class);
-
+
@Override
public String getName() {
return "valuemap";
}
- public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+ public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
ValueMap map = getMap(adaptable);
if (map == null) {
return null;
@@ -65,4 +72,53 @@ public class ValueMapInjector implements Injector {
}
}
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ // check if the element has the expected annotation
+ ValueMapValue annotation = element.getAnnotation(ValueMapValue.class);
+ if (annotation != null) {
+ return new ValueAnnotationProcessor(annotation, adaptable);
+ }
+ return null;
+ }
+
+ private static class ValueAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+ private final ValueMapValue annotation;
+
+ private final Object adaptable;
+
+ public ValueAnnotationProcessor(ValueMapValue annotation, Object adaptable) {
+ this.annotation = annotation;
+ this.adaptable = adaptable;
+ }
+
+ @Override
+ public String getName() {
+ // since null is not allowed as default value in annotations, the empty string means, the default should be
+ // used!
+ if (annotation.name().isEmpty()) {
+ return null;
+ }
+ return annotation.name();
+ }
+
+ @Override
+ public String getVia() {
+ if (StringUtils.isNotBlank(annotation.via())) {
+ return annotation.via();
+ }
+ // automatically go via resource, if this is the httprequest
+ if (adaptable instanceof SlingHttpServletRequest) {
+ return "resource";
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Boolean isOptional() {
+ return annotation.optional();
+ }
+ }
}
diff --git a/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java b/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java
new file mode 100644
index 0000000..2db9d7d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.*;
+import static org.mockito.Mockito.*;
+
+import javax.inject.Inject;
+
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.impl.injector.CustomAnnotation;
+import org.apache.sling.models.impl.injector.CustomAnnotationInjector;
+import org.apache.sling.models.impl.injector.SimpleInjector;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CustomInjectorTest {
+
+ @Mock
+ private ComponentContext componentCtx;
+
+ @Mock
+ private BundleContext bundleContext;
+
+ private ModelAdapterFactory factory;
+
+ @Before
+ public void setup() {
+ when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+
+ factory = new ModelAdapterFactory();
+ factory.activate(componentCtx);
+ }
+
+ @Test
+ public void testInjectorWhichDoesNotImplementAnnotationProcessor() {
+ factory.bindInjector(new SimpleInjector(), new ServicePropertiesMap(1, 1));
+
+ TestModel model = factory.getAdapter(new Object(), TestModel.class);
+ assertNotNull(model);
+ assertEquals("test string", model.getTestString());
+ }
+
+ @Test
+ public void testInjectorWithCustomAnnotation() {
+ factory.bindInjector(new SimpleInjector(), new ServicePropertiesMap(1, 1));
+ factory.bindInjector(new CustomAnnotationInjector(), new ServicePropertiesMap(1, 1));
+
+ CustomAnnotationModel model = factory.getAdapter(new Object(), CustomAnnotationModel.class);
+ assertNotNull(model);
+ assertEquals("default value", model.getDefaultString());
+ assertEquals("custom value", model.getCustomString());
+ }
+
+ @Model(adaptables = Object.class)
+ public interface TestModel {
+ @Inject
+ String getTestString();
+ }
+
+ @Model(adaptables = Object.class)
+ public interface CustomAnnotationModel {
+ @CustomAnnotation
+ String getDefaultString();
+
+ @Inject
+ String getCustomString();
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java b/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
new file mode 100644
index 0000000..6fe6a2b
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.BindingsInjector;
+import org.apache.sling.models.impl.injectors.ChildResourceInjector;
+import org.apache.sling.models.impl.injectors.OSGiServiceInjector;
+import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.testmodels.classes.InjectorSpecificAnnotationModel;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RunWith(MockitoJUnitRunner.class)
+public class InjectorSpecificAnnotationTest {
+
+ @Mock
+ private ComponentContext componentCtx;
+
+ @Mock
+ private BundleContext bundleContext;
+
+ @Mock
+ private SlingHttpServletRequest request;
+
+ @Mock
+ private Logger log;
+
+ private ModelAdapterFactory factory;
+
+ private OSGiServiceInjector osgiInjector;
+
+ @Before
+ public void setup() {
+ when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+ factory = new ModelAdapterFactory();
+ factory.activate(componentCtx);
+
+ osgiInjector = new OSGiServiceInjector();
+ osgiInjector.activate(componentCtx);
+
+ factory.bindInjector(new BindingsInjector(),
+ Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 1L));
+ factory.bindInjector(new ValueMapInjector(),
+ Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 2L));
+ factory.bindInjector(new ChildResourceInjector(),
+ Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 3L));
+ factory.bindInjector(new RequestAttributeInjector(),
+ Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 4L));
+ factory.bindInjector(osgiInjector, Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 5L));
+ SlingBindings bindings = new SlingBindings();
+ bindings.setLog(log);
+ Mockito.when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+ }
+
+ @Test
+ public void testSimpleValueModel() {
+ 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);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals("first-value", model.getFirst());
+ assertEquals("second-value", model.getSecond());
+ }
+
+ @Test
+ public void testOrderForValueAnnotation() {
+ // 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);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals("first-value", model.getFirst());
+ assertEquals(logFromValueMap, model.getLog());
+ }
+
+ @Test
+ public void testOSGiService() 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);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(log, model.getService());
+ }
+
+ @Test
+ public void testScriptVariable() throws InvalidSyntaxException {
+ SlingBindings bindings = new SlingBindings();
+ SlingScriptHelper helper = mock(SlingScriptHelper.class);
+ bindings.setSling(helper);
+ when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(helper, model.getHelper());
+ }
+
+ @Test
+ public void testRequestAttribute() throws InvalidSyntaxException {
+ Object attribute = new Object();
+ when(request.getAttribute("attribute")).thenReturn(attribute);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(attribute, model.getRequestAttribute());
+ }
+
+ @Test
+ public void testChildResource() {
+ Resource res = mock(Resource.class);
+ Resource child = mock(Resource.class);
+ when(res.getChild("child1")).thenReturn(child);
+ when(request.getResource()).thenReturn(res);
+
+ InjectorSpecificAnnotationModel model = factory.getAdapter(request, InjectorSpecificAnnotationModel.class);
+ assertNotNull("Could not instanciate model", model);
+ assertEquals(child, model.getChildResource());
+ }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
index b973952..605d750 100644
--- a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
+++ b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
@@ -88,6 +88,7 @@ public class MultipleInjectorTest {
assertEquals(obj.firstAttribute, bindingsValue);
verifyNoMoreInteractions(attributesInjector);
+ verify(bindingsInjector).createAnnotationProcessor(any(), any(AnnotatedElement.class));
verify(bindingsInjector).getValue(eq(request), eq("firstAttribute"), eq(String.class), any(AnnotatedElement.class), any(DisposalCallbackRegistry.class));
verifyNoMoreInteractions(bindingsInjector);
}
diff --git a/src/test/java/org/apache/sling/models/impl/PostConstructTest.java b/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
index 7b59be4..7f54f12 100644
--- a/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
+++ b/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
@@ -49,7 +49,7 @@ public class PostConstructTest {
ModelAdapterFactory factory = new ModelAdapterFactory();
factory.activate(componentCtx);
// no injectors are necessary
-
+
SubClass sc = factory.getAdapter(r, SubClass.class);
assertTrue(sc.getPostConstructCalledTimestampInSub() > sc.getPostConstructCalledTimestampInSuper());
assertTrue(sc.getPostConstructCalledTimestampInSuper() > 0);
diff --git a/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotation.java b/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotation.java
new file mode 100644
index 0000000..a2c9c55
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotation.java
@@ -0,0 +1,32 @@
+/*
+ * 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.injector;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+public @interface CustomAnnotation {
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotationInjector.java b/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotationInjector.java
new file mode 100644
index 0000000..053c1e6
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotationInjector.java
@@ -0,0 +1,66 @@
+/*
+ * 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.injector;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
+
+public class CustomAnnotationInjector implements InjectAnnotationProcessorFactory {
+
+ @Override
+ public String getName() {
+ return "with-annotation";
+ }
+
+ @Override
+ public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
+ if (name.equals("customString")) {
+ return "custom value";
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+ if (element.isAnnotationPresent(CustomAnnotation.class)) {
+ return new Processor();
+ } else {
+ return null;
+ }
+ }
+
+ private class Processor extends AbstractInjectAnnotationProcessor {
+
+ @Override
+ public boolean hasDefault() {
+ return true;
+ }
+
+ @Override
+ public Object getDefault() {
+ return "default value";
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java b/src/test/java/org/apache/sling/models/impl/injector/SimpleInjector.java
similarity index 64%
copy from src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
copy to src/test/java/org/apache/sling/models/impl/injector/SimpleInjector.java
index 194db83..316d179 100644
--- a/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
+++ b/src/test/java/org/apache/sling/models/impl/injector/SimpleInjector.java
@@ -14,33 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sling.models.impl.injectors;
+package org.apache.sling.models.impl.injector;
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.api.resource.Resource;
import org.apache.sling.models.spi.DisposalCallbackRegistry;
import org.apache.sling.models.spi.Injector;
-import org.osgi.framework.Constants;
-@Component
-@Service
-@Property(name = Constants.SERVICE_RANKING, intValue = 3000)
-public class ChildResourceInjector implements Injector {
+public class SimpleInjector implements Injector {
@Override
public String getName() {
- return "child-resources";
+ return "test";
}
@Override
- public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
- if (adaptable instanceof Resource) {
- return ((Resource) adaptable).getChild(name);
+ public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+ DisposalCallbackRegistry callbackRegistry) {
+ if (name.equals("testString") && declaredType.equals(String.class)) {
+ return "test string";
} else {
return null;
}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/InjectorSpecificAnnotationModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/InjectorSpecificAnnotationModel.java
new file mode 100644
index 0000000..ecbd32d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/InjectorSpecificAnnotationModel.java
@@ -0,0 +1,82 @@
+/*
+ * 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.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)
+public class InjectorSpecificAnnotationModel {
+
+ @ValueMapValue(optional = true)
+ private String first;
+
+ @ValueMapValue(name = "second", optional = true)
+ private String secondWithOtherName;
+
+ @ValueMapValue(optional = true)
+ private Logger log;
+
+ @ScriptVariable(optional = true, name = "sling")
+ private SlingScriptHelper helper;
+
+ @RequestAttribute(optional = true, name = "attribute")
+ private Object requestAttribute;
+
+ @OSGiService(optional = true)
+ private Logger service;
+
+ @ChildResource(optional = true, name = "child1")
+ private Resource 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;
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.