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:54:43 UTC

[sling-org-apache-sling-models-impl] 01/10: SLING-3313 - adding initial version of Sling Models (nee YAMF)

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.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git

commit 58786a9c52601c84b140b31cbe5a8f4cc196ed02
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Wed Jan 22 17:36:40 2014 +0000

    SLING-3313 - adding initial version of Sling Models (nee YAMF)
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1560437 13f79535-47bb-0310-9956-ffa450edef68
---
 README.txt                                         |  24 +
 pom.xml                                            | 142 +++++
 .../sling/models/impl/ModelAdapterFactory.java     | 615 +++++++++++++++++++++
 .../models/impl/ModelPackageBundleListener.java    | 130 +++++
 .../models/impl/injectors/BindingsInjector.java    |  75 +++
 .../impl/injectors/ChildResourceInjector.java      |  46 ++
 .../models/impl/injectors/OSGiServiceInjector.java | 194 +++++++
 .../impl/injectors/RequestAttributeInjector.java   |  60 ++
 .../models/impl/injectors/ValueMapInjector.java    |  65 +++
 .../org/apache/sling/models/impl/DefaultTest.java  |  70 +++
 .../sling/models/impl/MultipleInjectorTest.java    | 128 +++++
 .../sling/models/impl/OSGiInjectionTest.java       | 209 +++++++
 .../sling/models/impl/PostConstructTest.java       |  57 ++
 .../sling/models/impl/RequestInjectionTest.java    |  76 +++
 .../models/impl/ResourceModelClassesTest.java      | 163 ++++++
 .../models/impl/ResourceModelInterfacesTest.java   |  96 ++++
 .../java/org/apache/sling/models/impl/ViaTest.java |  78 +++
 .../models/testmodels/classes/ArrayOSGiModel.java  |  35 ++
 .../models/testmodels/classes/BindingsModel.java   |  37 ++
 .../models/testmodels/classes/ChildModel.java      |  33 ++
 .../testmodels/classes/CollectionOSGiModel.java    |  37 ++
 .../testmodels/classes/DefaultStringModel.java     |  43 ++
 .../models/testmodels/classes/ListOSGiModel.java   |  37 ++
 .../testmodels/classes/RequestOSGiModel.java       |  35 ++
 .../classes/ResourceModelWithRequiredField.java    |  34 ++
 .../models/testmodels/classes/SetOSGiModel.java    |  37 ++
 .../models/testmodels/classes/SimpleOSGiModel.java |  35 ++
 .../testmodels/classes/SimplePropertyModel.java    |  71 +++
 .../sling/models/testmodels/classes/SubClass.java  |  43 ++
 .../models/testmodels/classes/SuperClass.java      |  34 ++
 .../sling/models/testmodels/classes/ViaModel.java  |  36 ++
 .../testmodels/interfaces/ChildResourceModel.java  |  30 +
 .../testmodels/interfaces/ChildValueMapModel.java  |  31 ++
 .../models/testmodels/interfaces/ParentModel.java  |  30 +
 .../interfaces/ResourceModelWithRequiredField.java |  30 +
 .../testmodels/interfaces/ServiceInterface.java    |  21 +
 .../testmodels/interfaces/SimplePropertyModel.java |  43 ++
 37 files changed, 2960 insertions(+)

diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..657f1c7
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,24 @@
+Apache Sling Models Implementation
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/settings
+
+See the Subversion documentation for other source control features.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..079b51c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>18</version>
+        <relativePath>../../../../parent/pom.xml</relativePath>
+    </parent>
+    <groupId>org.apache.sling</groupId>
+    <artifactId>org.apache.sling.models.impl</artifactId>
+    <packaging>bundle</packaging>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>Sling Models Implementation</name>
+    <description>Sling Models Implementation</description>
+    <properties>
+        <sling.java.version>6</sling.java.version>
+    </properties>
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl</connection>
+        <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl</url>
+    </scm>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Embed-Dependency>
+                            *;scope=compile,
+                            org.osgi.compendium;inline="org/osgi/util/tracker/*"</Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.models.api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>4.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+            <version>1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.9.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.8.3</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
new file mode 100644
index 0000000..39bce98
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -0,0 +1,615 @@
+/*
+ * 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.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+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;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.commons.osgi.ServiceUtil;
+import org.apache.sling.models.annotations.Default;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+import org.apache.sling.models.annotations.Via;
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.DisposalCallback;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+public class ModelAdapterFactory implements AdapterFactory, Runnable {
+
+    public static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry {
+
+        private List<DisposalCallback> callbacks = new ArrayList<DisposalCallback>();
+
+        @Override
+        public void addDisposalCallback(DisposalCallback callback) {
+            callbacks.add(callback);
+        }
+
+        private void lock() {
+            callbacks = Collections.unmodifiableList(callbacks);
+        }
+
+        private void onDisposed() {
+            for (DisposalCallback callback : callbacks) {
+                callback.onDisposed();
+            }
+        }
+
+    }
+
+    private ReferenceQueue<Object> queue;
+
+    private ConcurrentMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl> disposalCallbacks;
+
+    public 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();
+        if (ref != null) {
+            log.info("calling disposal for " + ref.toString());
+            DisposalCallbackRegistryImpl registry = disposalCallbacks.remove(ref);
+            registry.onDisposed();
+        }
+    }
+
+    private static final Logger log = LoggerFactory.getLogger(ModelAdapterFactory.class);
+
+    @Reference(name = "injector", referenceInterface = Injector.class,
+            cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+    private final Map<Object, Injector> injectors = new TreeMap<Object, Injector>();
+
+    private volatile Injector[] sortedInjectors = new Injector[0];
+
+    private ModelPackageBundleListener listener;
+
+    private ServiceRegistration jobRegistration;
+
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+        Model modelAnnotation = type.getAnnotation(Model.class);
+        if (modelAnnotation == null) {
+            return null;
+        }
+        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);
+            if (handler != null) {
+                return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type },
+                        handler);
+            } else {
+                return null;
+            }
+        } else {
+            try {
+                return createObject(adaptable, type);
+            } catch (Exception e) {
+                log.error("unable to create object", e);
+                return null;
+            }
+        }
+    }
+
+    private Set<Field> collectInjectableFields(Class<?> type) {
+        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);
+                }
+            }
+            type = type.getSuperclass();
+        }
+        return result;
+    }
+
+    private Set<Method> collectInjectableMethods(Class<?> type) {
+        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);
+                }
+            }
+            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);
+
+        DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+
+        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();
+                        }
+                    }
+                }
+            }
+        }
+
+        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 (injectableMethods.isEmpty()) {
+            return handler;
+        } else {
+            Set<Method> requiredMethods = new HashSet<Method>();
+            for (Method method : injectableMethods) {
+                if (method.getAnnotation(Optional.class) == null) {
+                    requiredMethods.add(method);
+                }
+            }
+
+            if (!requiredMethods.isEmpty()) {
+                log.warn("Required methods {} on model class {} were not able to be injected.", requiredMethods,
+                        type);
+                return null;
+            } else {
+                return handler;
+            }
+        }
+    }
+
+    private DisposalCallbackRegistryImpl createAndRegisterCallbackRegistry(Object object) {
+        PhantomReference<Object> reference = new PhantomReference<Object>(object, queue);
+        DisposalCallbackRegistryImpl registry = new DisposalCallbackRegistryImpl();
+        disposalCallbacks.put(reference, registry);
+        return registry;
+    }
+
+    private String getSource(AnnotatedElement element) {
+        Source source = element.getAnnotation(Source.class);
+        if (source != null) {
+            return source.value();
+        } else {
+            for (Annotation ann : element.getAnnotations()) {
+                source = ann.annotationType().getAnnotation(Source.class);
+                if (source != null) {
+                    return source.value();
+                }
+            }
+        }
+        return null;
+    }
+
+    private <AdapterType> AdapterType createObject(Object adaptable, Class<AdapterType> type)
+            throws InstantiationException, IllegalAccessException {
+        Set<Field> injectableFields = collectInjectableFields(type);
+
+        AdapterType object = type.newInstance();
+
+        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();
+                        }
+                    }
+                }
+            }
+        }
+
+        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();
+                }
+            }
+        }
+
+        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);
+                }
+            }
+
+            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;
+                }
+            }
+        }
+    }
+
+    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 == Long.TYPE) {
+                    return defaultAnnotation.longValues();
+                }
+                if (componentType == Boolean.TYPE) {
+                    return defaultAnnotation.booleanValues();
+                }
+                if (componentType == Short.TYPE) {
+                    return defaultAnnotation.shortValues();
+                }
+                if (componentType == Float.TYPE) {
+                    return defaultAnnotation.floatValues();
+                }
+                if (componentType == Double.TYPE) {
+                    return defaultAnnotation.doubleValues();
+                }
+
+                log.warn("Default values for {} are not supported", componentType);
+                return null;
+            } else {
+                if (injectedClass == String.class) {
+                    return defaultAnnotation.values()[0];
+                }
+                if (injectedClass == Integer.TYPE) {
+                    return defaultAnnotation.intValues()[0];
+                }
+                if (injectedClass == Long.TYPE) {
+                    return defaultAnnotation.longValues()[0];
+                }
+                if (injectedClass == Boolean.TYPE) {
+                    return defaultAnnotation.booleanValues()[0];
+                }
+                if (injectedClass == Short.TYPE) {
+                    return defaultAnnotation.shortValues()[0];
+                }
+                if (injectedClass == Float.TYPE) {
+                    return defaultAnnotation.floatValues()[0];
+                }
+                if (injectedClass == Double.TYPE) {
+                    return defaultAnnotation.doubleValues()[0];
+                }
+
+                log.warn("Default values for {} are not supported", injectedClass);
+                return null;
+            }
+        } else {
+            log.warn("Cannot provide default for {}", type);
+            return null;
+        }
+    }
+
+    private Object getAdaptable(Object adaptable, AnnotatedElement point) {
+        Via viaAnnotation = point.getAnnotation(Via.class);
+        if (viaAnnotation == null) {
+            return adaptable;
+        }
+        String viaPropertyName = viaAnnotation.value();
+        try {
+            return PropertyUtils.getProperty(adaptable, viaPropertyName);
+        } catch (Exception e) {
+            log.error("Unable to execution projection " + viaPropertyName, e);
+            return null;
+        }
+    }
+
+    private String getName(Field field) {
+        Named named = field.getAnnotation(Named.class);
+        if (named != null) {
+            return named.value();
+        }
+        return field.getName();
+    }
+
+    private String getName(Method method) {
+        Named named = method.getAnnotation(Named.class);
+        if (named != null) {
+            return named.value();
+        }
+        String methodName = method.getName();
+        if (methodName.startsWith("get")) {
+            return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
+        } else if (methodName.startsWith("is")) {
+            return methodName.substring(2, 3).toLowerCase() + methodName.substring(3);
+        } else {
+            return methodName;
+        }
+    }
+
+    private void invokePostConstruct(Object object) throws Exception {
+        Class<?> clazz = object.getClass();
+        List<Method> postConstructMethods = new ArrayList<Method>();
+        while (clazz != null) {
+            Method[] methods = clazz.getDeclaredMethods();
+            for (Method method : methods) {
+                if (method.isAnnotationPresent(PostConstruct.class)) {
+                    postConstructMethods.add(method);
+                }
+            }
+            clazz = clazz.getSuperclass();
+        }
+        Collections.reverse(postConstructMethods);
+        for (Method method : postConstructMethods) {
+            boolean accessible = method.isAccessible();
+            try {
+                if (!accessible) {
+                    method.setAccessible(true);
+                }
+                method.invoke(object);
+            } finally {
+                if (!accessible) {
+                    method.setAccessible(false);
+                }
+            }
+        }
+    }
+
+    private Type mapPrimitiveClasses(Type type) {
+        if (type == Integer.TYPE) {
+            return Integer.class;
+        }
+        if (type == Long.TYPE) {
+            return Long.class;
+        }
+        if (type == Boolean.TYPE) {
+            return Boolean.class;
+        }
+        if (type == Double.TYPE) {
+            return Double.class;
+        }
+        if (type == Float.TYPE) {
+            return Float.class;
+        }
+        if (type == Short.TYPE) {
+            return Short.class;
+        }
+        if (type == Character.TYPE) {
+            return Character.class;
+        }
+
+        return type;
+    }
+
+    private boolean setField(Field field, Object createdObject, Object value) {
+        if (value != null) {
+            if (!isAcceptableType(field.getClass(), value) && value instanceof Adaptable) {
+                value = ((Adaptable) value).adaptTo(field.getClass());
+                if (value == null) {
+                    return false;
+                }
+            }
+            boolean accessible = field.isAccessible();
+            try {
+                if (!accessible) {
+                    field.setAccessible(true);
+                }
+                field.set(createdObject, value);
+                return true;
+            } catch (Exception e) {
+                log.error("unable to inject field", e);
+                return false;
+            } finally {
+                if (!accessible) {
+                    field.setAccessible(false);
+                }
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private 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());
+                if (value == null) {
+                    return false;
+                }
+            }
+            methods.put(method, value);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isAcceptableType(Class<?> type, Object value) {
+        if (type.isInstance(value)) {
+            return true;
+        }
+
+        if (type == Integer.TYPE) {
+            return Integer.class.isInstance(value);
+        }
+        if (type == Long.TYPE) {
+            return Long.class.isInstance(value);
+        }
+        if (type == Boolean.TYPE) {
+            return Boolean.class.isInstance(value);
+        }
+        if (type == Double.TYPE) {
+            return Double.class.isInstance(value);
+        }
+        if (type == Float.TYPE) {
+            return Float.class.isInstance(value);
+        }
+        if (type == Short.TYPE) {
+            return Short.class.isInstance(value);
+        }
+        if (type == Character.TYPE) {
+            return Character.class.isInstance(value);
+        }
+
+        return false;
+    }
+
+    @Activate
+    protected void activate(final ComponentContext ctx) {
+        this.queue = new ReferenceQueue<Object>();
+        this.disposalCallbacks = new ConcurrentHashMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl>();
+        Hashtable<Object, Object> properties = new Hashtable<Object, Object>();
+        properties.put("scheduler.concurrent", false);
+        properties.put("scheduler.period", Long.valueOf(30));
+
+        this.jobRegistration = ctx.getBundleContext().registerService(Runnable.class.getName(), this,
+                properties);
+
+        this.listener = new ModelPackageBundleListener(ctx.getBundleContext(), this);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        this.listener.unregisterAll();
+        if (jobRegistration != null) {
+            jobRegistration.unregister();
+            jobRegistration = null;
+        }
+    }
+
+    protected void bindInjector(final Injector injector, final Map<String, Object> props) {
+        synchronized (injectors) {
+            injectors.put(ServiceUtil.getComparableForServiceRanking(props), injector);
+            sortedInjectors = injectors.values().toArray(new Injector[injectors.size()]);
+        }
+    }
+
+    protected void unbindInjector(final Injector injector, final Map<String, Object> props) {
+        synchronized (injectors) {
+            injectors.remove(ServiceUtil.getComparableForServiceRanking(props));
+            sortedInjectors = injectors.values().toArray(new Injector[injectors.size()]);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java b/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java
new file mode 100644
index 0000000..ba45ca0
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/ModelPackageBundleListener.java
@@ -0,0 +1,130 @@
+/*
+ * 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.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.models.annotations.Model;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ModelPackageBundleListener implements BundleTrackerCustomizer {
+
+    private static final String HEADER = "Sling-Model-Packages";
+
+    private static final Logger log = LoggerFactory.getLogger(ModelPackageBundleListener.class);
+    
+    private final BundleContext bundleContext;
+
+    private final BundleTracker bundleTracker;
+
+    private final AdapterFactory factory;
+    
+    public ModelPackageBundleListener(BundleContext bundleContext, AdapterFactory factory) {
+        this.bundleContext = bundleContext;
+        this.factory = factory;
+        this.bundleTracker = new BundleTracker(bundleContext, Bundle.ACTIVE, this);
+        this.bundleTracker.open();
+    }
+    
+    @Override
+    public Object addingBundle(Bundle bundle, BundleEvent event) {
+List<ServiceRegistration> regs = new ArrayList<ServiceRegistration>();
+        
+        Dictionary<?, ?> headers = bundle.getHeaders();
+        String packageList = PropertiesUtil.toString(headers.get(HEADER), null);
+        if (packageList != null) {
+
+            packageList = StringUtils.deleteWhitespace(packageList);
+            String[] packages = packageList.split(",");
+            for (String singlePackage : packages) {
+                @SuppressWarnings("unchecked")
+                Enumeration<URL> classUrls = bundle.findEntries("/" + singlePackage.replace('.', '/'), "*.class",
+                        true);
+                while (classUrls.hasMoreElements()) {
+                    URL url = classUrls.nextElement();
+                    String className = toClassName(url);
+                    try {
+                        Class<?> clazz = bundle.loadClass(className);
+                        Model annotation = clazz.getAnnotation(Model.class);
+                        if (annotation != null) {
+                            Class<?>[] adaptables = annotation.adaptables();
+                            String[] classNames = toStringArray(adaptables);
+                            Dictionary<String, Object> registrationProps = new Hashtable<String, Object>();
+                            registrationProps.put(AdapterFactory.ADAPTER_CLASSES, className);
+                            registrationProps.put(AdapterFactory.ADAPTABLE_CLASSES, classNames);
+                            ServiceRegistration reg = bundleContext.registerService(AdapterFactory.SERVICE_NAME,
+                                    factory, registrationProps);
+                            regs.add(reg);
+                        }
+                    } catch (ClassNotFoundException e) {
+                        log.warn("Unable to load class", e);
+                    }
+
+                }
+            }
+        }
+        return regs.toArray(new ServiceRegistration[0]);
+    }
+
+    @Override
+    public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+    }
+
+    @Override
+    public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+        if (object instanceof ServiceRegistration[]) {
+            for (ServiceRegistration reg : (ServiceRegistration[]) object) {
+                reg.unregister();
+            }
+        }
+    }
+
+    public synchronized void unregisterAll() {
+        this.bundleTracker.close();
+    }
+
+    /** Convert class URL to class name */
+    private String toClassName(URL url) {
+        final String f = url.getFile();
+        final String cn = f.substring(1, f.length() - ".class".length());
+        return cn.replace('/', '.');
+    }
+
+    private String[] toStringArray(Class<?>[] classes) {
+        String[] arr = new String[classes.length];
+        for (int i = 0; i < classes.length; i++) {
+            arr[i] = classes[i].getName();
+        }
+        return arr;
+    }
+
+}
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
new file mode 100644
index 0000000..2a6fcc3
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
@@ -0,0 +1,75 @@
+/*
+ * 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 javax.servlet.ServletRequest;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service
+public class BindingsInjector implements Injector {
+
+    private static final Logger log = LoggerFactory.getLogger(BindingsInjector.class);
+
+    @Override
+    public String getName() {
+        return "script-bindings";
+    }
+
+    private static Object getValue(SlingBindings bindings, String name, Class<?> type) {
+        Object value = bindings.get(name);
+        if (type.isInstance(value)) {
+            return value;
+        } else {
+            return null;
+        }
+    }
+
+    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+        SlingBindings bindings = getBindings(adaptable);
+        if (bindings == null) {
+            return null;
+        }
+        if (type instanceof Class<?>) {
+            return getValue(bindings, name, (Class<?>) type);
+        } else {
+            log.debug("BindingsInjector doesn't support non-class type {}", type);
+            return null;
+        }
+
+    }
+
+    private SlingBindings getBindings(Object adaptable) {
+        if (adaptable instanceof ServletRequest) {
+            ServletRequest request = (ServletRequest) adaptable;
+            return (SlingBindings) request.getAttribute(SlingBindings.class.getName());
+        } else {
+            return null;
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..6afec71
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
@@ -0,0 +1,46 @@
+/*
+ * 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.Service;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+
+@Component
+@Service
+public class ChildResourceInjector implements Injector {
+
+    @Override
+    public String getName() {
+        return "child-resources";
+    }
+
+    @Override
+    public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+        if (adaptable instanceof Resource) {
+            return ((Resource) adaptable).getChild(name);
+        } 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
new file mode 100644
index 0000000..f19c9ed
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
@@ -0,0 +1,194 @@
+/*
+ * 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.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+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.spi.DisposalCallback;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service
+public class OSGiServiceInjector implements Injector {
+
+    private static final Logger log = LoggerFactory.getLogger(OSGiServiceInjector.class);
+
+    private BundleContext bundleContext;
+
+    @Override
+    public String getName() {
+        return "osgi-services";
+    }
+
+    @Activate
+    public void activate(ComponentContext ctx) {
+        this.bundleContext = ctx.getBundleContext();
+    }
+
+    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+        Filter filter = element.getAnnotation(Filter.class);
+        String filterString = null;
+        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) {
+        SlingScriptHelper helper = getScriptHelper(adaptable);
+
+        if (helper != null) {
+            T[] services = helper.getServices(type, filter);
+            if (services == null || services.length == 0) {
+                return null;
+            } else {
+                return services[0];
+            }
+        } else {
+            try {
+                ServiceReference[] refs = bundleContext.getServiceReferences(type.getName(), filter);
+                if (refs == null || refs.length == 0) {
+                    return null;
+                } else {
+                    callbackRegistry.addDisposalCallback(new Callback(refs, bundleContext));
+                    return bundleContext.getService(refs[0]);
+                }
+            } catch (InvalidSyntaxException e) {
+                log.error("invalid filter expression", e);
+                return null;
+            }
+        }
+    }
+
+    private <T> Object[] getServices(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) {
+        SlingScriptHelper helper = getScriptHelper(adaptable);
+
+        if (helper != null) {
+            T[] services = helper.getServices(type, filter);
+            return services;
+        } else {
+            try {
+                ServiceReference[] refs = bundleContext.getServiceReferences(type.getName(), filter);
+                if (refs == null || refs.length == 0) {
+                    return null;
+                } else {
+                    callbackRegistry.addDisposalCallback(new Callback(refs, bundleContext));
+                    List<Object> services = new ArrayList<Object>();
+                    for (ServiceReference ref : refs) {
+                        Object service = bundleContext.getService(ref);
+                        if (service != null) {
+                            services.add(service);
+                        }
+                    }
+                    return services.toArray();
+                }
+            } catch (InvalidSyntaxException e) {
+                log.error("invalid filter expression", e);
+                return null;
+            }
+        }
+    }
+
+    private SlingScriptHelper getScriptHelper(Object adaptable) {
+        if (adaptable instanceof ServletRequest) {
+            ServletRequest request = (ServletRequest) adaptable;
+            SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
+            if (bindings != null) {
+                return bindings.getSling();
+            } else {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    private Object getValue(Object adaptable, Type type, String filterString, DisposalCallbackRegistry callbackRegistry) {
+        if (type instanceof Class) {
+            Class<?> injectedClass = (Class<?>) type;
+            if (injectedClass.isArray()) {
+                Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString, callbackRegistry);
+                Object arr = Array.newInstance(injectedClass.getComponentType(), services.length);
+                for (int i = 0; i < services.length; i++) {
+                    Array.set(arr, i, services[i]);
+                }
+                return arr;
+            } else {
+                return getService(adaptable, injectedClass, filterString, callbackRegistry);
+            }
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType ptype = (ParameterizedType) type;
+            if (ptype.getActualTypeArguments().length != 1) {
+                return null;
+            }
+            Class<?> collectionType = (Class<?>) ptype.getRawType();
+            if (!(collectionType.equals(Collection.class) || collectionType.equals(List.class))) {
+                return null;
+            }
+
+            Class<?> serviceType = (Class<?>) ptype.getActualTypeArguments()[0];
+            Object[] services = getServices(adaptable, serviceType, filterString, callbackRegistry);
+            return Arrays.asList(services);
+        } else {
+            log.warn("Cannot handle type {}", type);
+            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) {
+                for (ServiceReference ref : refs) {
+                    context.ungetService(ref);
+                }
+            }
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..ccd7138
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
@@ -0,0 +1,60 @@
+/*
+ * 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 javax.servlet.ServletRequest;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service
+public class RequestAttributeInjector implements Injector {
+
+    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) {
+        if (!(adaptable instanceof ServletRequest)) {
+            return null;
+        } else if (declaredType instanceof Class<?>) {
+            Class<?> clazz = (Class<?>) declaredType;
+            Object attribute = ((ServletRequest)adaptable).getAttribute(name);
+            if (clazz.isInstance(attribute)) {
+                return attribute;
+            } else {
+                return null;
+            }
+        } else {
+            log.debug("BindingsInjector doesn't support non-class type {}", declaredType);
+            return null;
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..f81fb35
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
@@ -0,0 +1,65 @@
+/*
+ * 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.Service;
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.spi.DisposalCallbackRegistry;
+import org.apache.sling.models.spi.Injector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service
+public class ValueMapInjector implements 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) {
+        ValueMap map = getMap(adaptable);
+        if (map == null) {
+            return null;
+        } else if (type instanceof Class<?>) {
+            return map.get(name, (Class<?>) type);
+        } else {
+            log.debug("ValueMapInjector doesn't support non-class types {}", type);
+            return null;
+        }
+    }
+
+    private ValueMap getMap(Object adaptable) {
+        if (adaptable instanceof ValueMap) {
+            return (ValueMap) adaptable;
+        } else if (adaptable instanceof Adaptable) {
+            ValueMap map = ((Adaptable) adaptable).adaptTo(ValueMap.class);
+            return map;
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/DefaultTest.java b/src/test/java/org/apache/sling/models/impl/DefaultTest.java
new file mode 100644
index 0000000..a0ca9e7
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/DefaultTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.testmodels.classes.DefaultStringModel;
+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.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DefaultTest {
+
+    @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);
+        factory.bindInjector(new ValueMapInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+    }
+
+    @Test
+    public void testDefaultStringValue() {
+        ValueMap vm = new ValueMapDecorator(Collections.<String, Object>emptyMap());
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        DefaultStringModel model = factory.getAdapter(res, DefaultStringModel.class);
+        assertNotNull(model);
+        assertEquals("firstDefault", model.getFirstProperty());
+        assertEquals(2, model.getSecondProperty().length);
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
new file mode 100644
index 0000000..087c075
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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 javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.impl.injectors.BindingsInjector;
+import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MultipleInjectorTest {
+
+    @Spy
+    private BindingsInjector bindingsInjector;
+
+    @Spy
+    private RequestAttributeInjector attributesInjector;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    private ModelAdapterFactory factory;
+
+    private SlingBindings bindings;
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+        bindings = new SlingBindings();
+
+        factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+        factory.bindInjector(bindingsInjector, Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 2L));
+        factory.bindInjector(attributesInjector,
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 1L));
+
+        when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+    }
+
+    @Test
+    public void testInjectorOrder() {
+        String bindingsValue = "bindings value";
+        bindings.put("firstAttribute", bindingsValue);
+
+        String attributeValue = "attribute value";
+        when(request.getAttribute("firstAttribute")).thenReturn(attributeValue);
+
+        ForTwoInjectors obj = factory.getAdapter(request, ForTwoInjectors.class);
+
+        assertNotNull(obj);
+        assertEquals(obj.firstAttribute, bindingsValue);
+
+        verifyNoMoreInteractions(attributesInjector);
+    }
+
+    @Test
+    public void testInjectorOrderWithSource() {
+        String bindingsValue = "bindings value";
+        bindings.put("firstAttribute", bindingsValue);
+
+        String attributeValue = "attribute value";
+        when(request.getAttribute("firstAttribute")).thenReturn(attributeValue);
+
+        ForTwoInjectorsWithSource obj = factory.getAdapter(request, ForTwoInjectorsWithSource.class);
+
+        assertNotNull(obj);
+        assertEquals(obj.firstAttribute, attributeValue);
+
+        verify(bindingsInjector).getName();
+        verifyNoMoreInteractions(bindingsInjector);
+    }
+
+    @Model(adaptables = SlingHttpServletRequest.class)
+    public static class ForTwoInjectors {
+
+        @Inject
+        private String firstAttribute;
+
+    }
+
+    @Model(adaptables = SlingHttpServletRequest.class)
+    public static class ForTwoInjectorsWithSource {
+
+        @Inject
+        @Source("request-attributes")
+        private String firstAttribute;
+
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java
new file mode 100644
index 0000000..a7989ee
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/OSGiInjectionTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.Dictionary;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.impl.injectors.OSGiServiceInjector;
+import org.apache.sling.models.testmodels.classes.ArrayOSGiModel;
+import org.apache.sling.models.testmodels.classes.CollectionOSGiModel;
+import org.apache.sling.models.testmodels.classes.ListOSGiModel;
+import org.apache.sling.models.testmodels.classes.RequestOSGiModel;
+import org.apache.sling.models.testmodels.classes.SetOSGiModel;
+import org.apache.sling.models.testmodels.classes.SimpleOSGiModel;
+import org.apache.sling.models.testmodels.interfaces.ServiceInterface;
+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.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OSGiInjectionTest {
+    private ModelAdapterFactory factory;
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    @Mock
+    private SlingScriptHelper helper;
+
+    private SlingBindings bindings = new SlingBindings();
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+        factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+
+        OSGiServiceInjector injectorFactory = new OSGiServiceInjector();
+        injectorFactory.activate(componentCtx);
+        factory.bindInjector(injectorFactory, Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+
+        bindings.setSling(helper);
+    }
+
+    @Test
+    public void testSimpleOSGiModel() 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);
+
+        SimpleOSGiModel model = factory.getAdapter(res, SimpleOSGiModel.class);
+        assertNotNull(model);
+        assertNotNull(model.getService());
+        assertEquals(service, model.getService());
+
+        verifyNoMoreInteractions(res);
+    }
+
+    @Test
+    public void testRequestOSGiModel() throws Exception {
+        ServiceInterface service = mock(ServiceInterface.class);
+
+        SlingHttpServletRequest request = mock(SlingHttpServletRequest.class);
+        when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+        when(helper.getServices(ServiceInterface.class, null)).thenReturn(new ServiceInterface[] { service });
+
+        RequestOSGiModel model = factory.getAdapter(request, RequestOSGiModel.class);
+        assertNotNull(model);
+        assertNotNull(model.getService());
+        assertEquals(service, model.getService());
+
+        verify(bundleContext).registerService(eq(Runnable.class.getName()), eq(factory), any(Dictionary.class));
+        verify(bundleContext).addBundleListener(any(BundleListener.class));
+        verify(bundleContext).getBundles();
+        verifyNoMoreInteractions(bundleContext);
+    }
+
+    @Test
+    public void testListOSGiModel() 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);
+
+        ListOSGiModel model = factory.getAdapter(res, 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);
+    }
+
+    @Test
+    public void testArrayOSGiModel() 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);
+
+        ArrayOSGiModel model = factory.getAdapter(res, ArrayOSGiModel.class);
+        assertNotNull(model);
+        assertNotNull(model.getServices());
+        assertEquals(2, model.getServices().length);
+        assertEquals(service1, model.getServices()[0]);
+        assertEquals(service2, model.getServices()[1]);
+
+        verifyNoMoreInteractions(res);
+    }
+
+    @Test
+    public void testCollectionOSGiModel() 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);
+
+        CollectionOSGiModel model = factory.getAdapter(res, CollectionOSGiModel.class);
+        assertNotNull(model);
+        assertNotNull(model.getServices());
+        assertEquals(2, model.getServices().size());
+
+        assertTrue(model.getServices().contains(service1));
+        assertTrue(model.getServices().contains(service2));
+
+        verifyNoMoreInteractions(res);
+    }
+
+    @Test
+    public void testSetOSGiModel() 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);
+
+        SetOSGiModel model = factory.getAdapter(res, SetOSGiModel.class);
+        assertNull(model);
+
+        verify(bundleContext).registerService(eq(Runnable.class.getName()), eq(factory), any(Dictionary.class));
+        verify(bundleContext).addBundleListener(any(BundleListener.class));
+        verify(bundleContext).getBundles();
+        verifyNoMoreInteractions(res, bundleContext);
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/PostConstructTest.java b/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
new file mode 100644
index 0000000..7b59be4
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/PostConstructTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.testmodels.classes.SubClass;
+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 PostConstructTest {
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+    }
+
+    @Test
+    public void testClassOrder() {
+        Resource r = mock(Resource.class);
+        ModelAdapterFactory factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+        // no injectors are necessary
+        
+        SubClass sc = factory.getAdapter(r, SubClass.class);
+        assertTrue(sc.getPostConstructCalledTimestampInSub() > sc.getPostConstructCalledTimestampInSuper());
+        assertTrue(sc.getPostConstructCalledTimestampInSuper() > 0);
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java
new file mode 100644
index 0000000..8a48ef0
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/RequestInjectionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.models.impl.injectors.BindingsInjector;
+import org.apache.sling.models.testmodels.classes.BindingsModel;
+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.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RequestInjectionTest {
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    private ModelAdapterFactory factory;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Mock
+    private SlingScriptHelper sling;
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+
+        SlingBindings bindings = new SlingBindings();
+        bindings.setSling(sling);
+        when(request.getAttribute(SlingBindings.class.getName())).thenReturn(bindings);
+
+        factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+        factory.bindInjector(new BindingsInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+    }
+
+    @Test
+    public void testNamedInjection() {
+        BindingsModel model = factory.getAdapter(request, BindingsModel.class);
+        assertNotNull(model.getSling());
+        assertEquals(sling, model.getSling());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
new file mode 100644
index 0000000..46e825a
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/ResourceModelClassesTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.commons.lang.RandomStringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.ChildResourceInjector;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.testmodels.classes.ChildModel;
+import org.apache.sling.models.testmodels.classes.ResourceModelWithRequiredField;
+import org.apache.sling.models.testmodels.classes.SimplePropertyModel;
+import org.apache.sling.models.testmodels.interfaces.ChildResourceModel;
+import org.apache.sling.models.testmodels.interfaces.ChildValueMapModel;
+import org.apache.sling.models.testmodels.interfaces.ParentModel;
+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.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ResourceModelClassesTest {
+
+    @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);
+        factory.bindInjector(new ValueMapInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 1L));
+        factory.bindInjector(new ChildResourceInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+    }
+
+    @Test
+    public void testSimplePropertyModel() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("first", "first-value");
+        map.put("third", "third-value");
+        map.put("intProperty", new Integer(3));
+        ValueMap vm = new ValueMapDecorator(map);
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        SimplePropertyModel model = factory.getAdapter(res, SimplePropertyModel.class);
+        assertNotNull(model);
+        assertEquals("first-value", model.getFirst());
+        assertNull(model.getSecond());
+        assertEquals("third-value", model.getThirdProperty());
+        assertEquals(3, model.getIntProperty());
+        assertTrue(model.isPostConstructCalled());
+    }
+
+    @Test
+    public void testRequiredPropertyModel() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("first", "first-value");
+        map.put("third", "third-value");
+        ValueMap vm = spy(new ValueMapDecorator(map));
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        ResourceModelWithRequiredField model = factory.getAdapter(res, ResourceModelWithRequiredField.class);
+        assertNull(model);
+
+        verify(vm).get("required", String.class);
+    }
+
+    @Test
+    public void testChildResource() {
+        Resource child = mock(Resource.class);
+
+        Resource res = mock(Resource.class);
+        when(res.getChild("firstChild")).thenReturn(child);
+
+        ChildResourceModel model = factory.getAdapter(res, ChildResourceModel.class);
+        assertNotNull(model);
+        assertEquals(child, model.getFirstChild());
+    }
+
+    @Test
+    public void testChildValueMap() {
+        ValueMap map = ValueMapDecorator.EMPTY;
+
+        Resource child = mock(Resource.class);
+        when(child.adaptTo(ValueMap.class)).thenReturn(map);
+
+        Resource res = mock(Resource.class);
+        when(res.getChild("firstChild")).thenReturn(child);
+
+        ChildValueMapModel model = factory.getAdapter(res, ChildValueMapModel.class);
+        assertNotNull(model);
+        assertEquals(map, model.getFirstChild());
+    }
+
+    @Test
+    public void testChildModel() {
+        Object value = RandomStringUtils.randomAlphabetic(10);
+        Map<String, Object> props = Collections.singletonMap("property", value);
+        ValueMap map = new ValueMapDecorator(props);
+
+        final Resource child = mock(Resource.class);
+        when(child.adaptTo(ValueMap.class)).thenReturn(map);
+        when(child.adaptTo(ChildModel.class)).thenAnswer(new Answer<ChildModel>() {
+
+            @Override
+            public ChildModel answer(InvocationOnMock invocation) throws Throwable {
+                return factory.getAdapter(child, ChildModel.class);
+            }
+
+        });
+
+        Resource res = mock(Resource.class);
+        when(res.getChild("firstChild")).thenReturn(child);
+
+        ParentModel model = factory.getAdapter(res, ParentModel.class);
+        assertNotNull(model);
+        
+        ChildModel childModel = model.getFirstChild();
+        assertNotNull(childModel);
+        assertEquals(value, childModel.getProperty());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java b/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java
new file mode 100644
index 0000000..6d1b0c8
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/ResourceModelInterfacesTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.testmodels.classes.ResourceModelWithRequiredField;
+import org.apache.sling.models.testmodels.interfaces.SimplePropertyModel;
+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.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ResourceModelInterfacesTest {
+
+    @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);
+        factory.bindInjector(new ValueMapInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+    }
+
+    @Test
+    public void testSimplePropertyModel() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("first", "first-value");
+        map.put("third", "third-value");
+        map.put("fourth", true);
+        ValueMap vm = new ValueMapDecorator(map);
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        SimplePropertyModel model = factory.getAdapter(res, SimplePropertyModel.class);
+        assertNotNull(model);
+        assertEquals("first-value", model.getFirst());
+        assertNull(model.getSecond());
+        assertEquals("third-value", model.getThirdProperty());
+        assertTrue(model.isFourth());
+    }
+
+    @Test
+    public void testRequiredPropertyModel() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("first", "first-value");
+        map.put("third", "third-value");
+        ValueMap vm = spy(new ValueMapDecorator(map));
+
+        Resource res = mock(Resource.class);
+        when(res.adaptTo(ValueMap.class)).thenReturn(vm);
+
+        ResourceModelWithRequiredField model = factory.getAdapter(res, ResourceModelWithRequiredField.class);
+        assertNull(model);
+
+        verify(vm).get("required", String.class);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/impl/ViaTest.java b/src/test/java/org/apache/sling/models/impl/ViaTest.java
new file mode 100644
index 0000000..0485030
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/ViaTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+
+import org.apache.commons.lang.RandomStringUtils;
+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.wrappers.ValueMapDecorator;
+import org.apache.sling.models.impl.injectors.ValueMapInjector;
+import org.apache.sling.models.testmodels.classes.ViaModel;
+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.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ViaTest {
+
+    @Mock
+    private Resource resource;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+    
+    private ModelAdapterFactory factory;
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+        when(request.getResource()).thenReturn(resource);
+        factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+        factory.bindInjector(new ValueMapInjector(),
+                Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L));
+    }
+
+    @Test
+    public void testProjectionToResource() {
+        String value = RandomStringUtils.randomAlphanumeric(10);
+        ValueMap map = new ValueMapDecorator(Collections.<String, Object> singletonMap("firstProperty", value));
+        when(resource.adaptTo(ValueMap.class)).thenReturn(map);
+        
+        ViaModel model = factory.getAdapter(request, ViaModel.class);
+        assertNotNull(model);
+        assertEquals(value, model.getFirstProperty());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.java
new file mode 100644
index 0000000..3e31a97
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ArrayOSGiModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.testmodels.classes;
+
+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)
+public class ArrayOSGiModel {
+    
+    @Inject
+    private ServiceInterface[] services;
+    
+    public ServiceInterface[] getServices() {
+        return services;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.java
new file mode 100644
index 0000000..342b9cf
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/BindingsModel.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.testmodels.classes;
+
+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)
+public class BindingsModel {
+    
+    @Inject
+    @Named("sling")
+    private SlingScriptHelper sling;
+    
+    public SlingScriptHelper getSling() {
+        return sling;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java
new file mode 100644
index 0000000..071e37f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ChildModel.java
@@ -0,0 +1,33 @@
+/*
+ * 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 javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+public class ChildModel {
+
+    @Inject
+    private String property;
+    
+    public String getProperty() {
+        return property;
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.java
new file mode 100644
index 0000000..115b5e4
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/CollectionOSGiModel.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.testmodels.classes;
+
+import java.util.Collection;
+
+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)
+public class CollectionOSGiModel {
+    
+    @Inject
+    private Collection<ServiceInterface> services;
+    
+    public Collection<ServiceInterface> getServices() {
+        return services;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java
new file mode 100644
index 0000000..fecfcb0
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/DefaultStringModel.java
@@ -0,0 +1,43 @@
+/*
+ * 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 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)
+public class DefaultStringModel {
+
+    @Inject
+    @Default(values = "firstDefault")
+    private String firstProperty;
+
+    @Inject
+    @Default(values = { "firstDefault", "secondDefault" })
+    private String[] secondProperty;
+
+    public String getFirstProperty() {
+        return firstProperty;
+    }
+
+    public String[] getSecondProperty() {
+        return secondProperty;
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.java
new file mode 100644
index 0000000..33b2ed4
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ListOSGiModel.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.testmodels.classes;
+
+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)
+public class ListOSGiModel {
+    
+    @Inject
+    private List<ServiceInterface> services;
+    
+    public List<ServiceInterface> getServices() {
+        return services;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.java
new file mode 100644
index 0000000..253e9ef
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/RequestOSGiModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.testmodels.classes;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.testmodels.interfaces.ServiceInterface;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+public class RequestOSGiModel {
+    
+    @Inject
+    private ServiceInterface service;
+    
+    public ServiceInterface getService() {
+        return service;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.java b/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.java
new file mode 100644
index 0000000..8ebc5f6
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ResourceModelWithRequiredField.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.testmodels.classes;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+public class ResourceModelWithRequiredField {
+    
+    @Inject
+    private String required;
+    
+    public String getRequired() {
+        return required;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.java
new file mode 100644
index 0000000..9e185c6
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SetOSGiModel.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.testmodels.classes;
+
+import java.util.Set;
+
+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)
+public class SetOSGiModel {
+    
+    @Inject
+    private Set<ServiceInterface> services;
+    
+    public Set<ServiceInterface> getServices() {
+        return services;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.java
new file mode 100644
index 0000000..1fc3d62
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SimpleOSGiModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.testmodels.classes;
+
+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)
+public class SimpleOSGiModel {
+    
+    @Inject
+    private ServiceInterface service;
+    
+    public ServiceInterface getService() {
+        return service;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java
new file mode 100644
index 0000000..ddd3b2f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SimplePropertyModel.java
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+
+@Model(adaptables = Resource.class)
+public class SimplePropertyModel {
+
+    @Inject
+    private String first;
+
+    @Inject
+    @Optional
+    private String second;
+
+    @Inject
+    @Named("third")
+    private String thirdProperty;
+
+    @Inject
+    private int intProperty;
+
+    private boolean postConstructCalled;
+
+    public int getIntProperty() {
+        return intProperty;
+    }
+
+    public String getFirst() {
+        return first;
+    }
+
+    public String getSecond() {
+        return second;
+    }
+
+    public String getThirdProperty() {
+        return thirdProperty;
+    }
+
+    @PostConstruct
+    protected void postConstruct() {
+        postConstructCalled = true;
+    }
+
+    public boolean isPostConstructCalled() {
+        return postConstructCalled;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java b/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java
new file mode 100644
index 0000000..997cf74
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SubClass.java
@@ -0,0 +1,43 @@
+/*
+ * 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 javax.annotation.PostConstruct;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables=Resource.class)
+public class SubClass extends SuperClass {
+
+    private long postConstructCalledTimestampInSub;
+
+    public long getPostConstructCalledTimestampInSub() {
+        return postConstructCalledTimestampInSub;
+    }
+    
+    @PostConstruct
+    protected void pc2() {
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        postConstructCalledTimestampInSub = System.currentTimeMillis();
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.java b/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.java
new file mode 100644
index 0000000..a7047ce
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/SuperClass.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.testmodels.classes;
+
+import javax.annotation.PostConstruct;
+
+public class SuperClass {
+
+    private long postConstructCalledTimestampInSuper;
+
+    public long getPostConstructCalledTimestampInSuper() {
+        return postConstructCalledTimestampInSuper;
+    }
+
+    @PostConstruct
+    protected void pc() {
+        postConstructCalledTimestampInSuper = System.currentTimeMillis();
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.java
new file mode 100644
index 0000000..5e5054e
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/ViaModel.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 javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Via;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+public class ViaModel {
+
+    @Inject
+    @Via("resource")
+    private String firstProperty;
+
+    public String getFirstProperty() {
+        return firstProperty;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.java
new file mode 100644
index 0000000..b2ce65c
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildResourceModel.java
@@ -0,0 +1,30 @@
+/*
+ * 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.interfaces;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+public interface ChildResourceModel {
+
+    @Inject
+    public Resource getFirstChild();
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java
new file mode 100644
index 0000000..e7021d7
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ChildValueMapModel.java
@@ -0,0 +1,31 @@
+/*
+ * 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.interfaces;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+public interface ChildValueMapModel {
+
+    @Inject
+    public ValueMap getFirstChild();
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.java
new file mode 100644
index 0000000..e0ec29d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ParentModel.java
@@ -0,0 +1,30 @@
+/*
+ * 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.interfaces;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.testmodels.classes.ChildModel;
+
+@Model(adaptables = Resource.class)
+public interface ParentModel {
+
+    @Inject
+    public ChildModel getFirstChild();
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.java
new file mode 100644
index 0000000..dd68f78
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ResourceModelWithRequiredField.java
@@ -0,0 +1,30 @@
+/*
+ * 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.interfaces;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = Resource.class)
+public interface ResourceModelWithRequiredField {
+
+    @Inject
+    public String getRequired();
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java
new file mode 100644
index 0000000..73c9b41
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/ServiceInterface.java
@@ -0,0 +1,21 @@
+/*
+ * 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.interfaces;
+
+public interface ServiceInterface {
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java b/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java
new file mode 100644
index 0000000..cdff785
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/interfaces/SimplePropertyModel.java
@@ -0,0 +1,43 @@
+/*
+ * 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.interfaces;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+
+@Model(adaptables = Resource.class)
+public interface SimplePropertyModel {
+
+    @Inject
+    public String getFirst();
+
+    @Inject
+    @Optional
+    public String getSecond();
+
+    @Inject
+    @Named("third")
+    public String getThirdProperty();
+    
+    @Inject
+    public boolean isFourth();
+
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.