You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ju...@apache.org on 2014/06/04 21:38:55 UTC

svn commit: r1600469 [1/2] - in /sling/trunk/bundles/extensions/models: api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ api/src/main/java/org/apache/sling/models/spi/injectorspecific/ impl/ impl/src/main/java/org/apache/sling/mo...

Author: justin
Date: Wed Jun  4 19:38:55 2014
New Revision: 1600469

URL: http://svn.apache.org/r1600469
Log:
SLING-3499 - adding support for custom annotation per injector (thanks Konrad Windszus for the patch!)

Added:
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ChildResource.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/OSGiService.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/RequestAttribute.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ScriptVariable.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ValueMapValue.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/package-info.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/AbstractInjectAnnotationProcessor.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotation.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessor.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessorFactory.java
    sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/package-info.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/InjectorSpecificAnnotationTest.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/injector/
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotation.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/injector/CustomAnnotationInjector.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/injector/SimpleInjector.java
      - copied, changed from r1599279, sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/testmodels/classes/InjectorSpecificAnnotationModel.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/InjectorSpecificAnnotationTest.java
    sling/trunk/bundles/extensions/models/integration-tests/src/main/java/org/apache/sling/models/it/models/SlingPropertyAnnotationTestModel.java
Modified:
    sling/trunk/bundles/extensions/models/impl/pom.xml
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
    sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/MultipleInjectorTest.java
    sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/PostConstructTest.java

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ChildResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ChildResource.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ChildResource.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ChildResource.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.annotations.injectorspecific;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods or fields to let Sling Models inject a child resource
+ *
+ */
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("child-resources")
+public @interface ChildResource {
+    /**
+     * Specifies the name of the child resource.
+     * If empty or not set, then the name is derived from the method or field.
+     */
+    public String name() default "";
+
+    /**
+     * If set to true, the model can be instantiated even if there is no child resource
+     * with that name available.
+     * Default = false.
+     */
+    public boolean optional() default false;
+
+    /**
+     * If set, then the child resource can be obtained via a projection of the given
+     * property of the adaptable.
+     */
+    public String via() default "";
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/OSGiService.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/OSGiService.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/OSGiService.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/OSGiService.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,51 @@
+/*
+ * 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.annotations.injectorspecific;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods or fields to let Sling Models inject an OSGi service
+ *
+ */
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("osgi-services")
+public @interface OSGiService {
+    /**
+     * specifies the RFC 1960-based filter string, which is evaluated when retrieving the service. If empty string or left out, then no filtering is being performed.
+     * 
+     * @see "Core Specification, section 5.5, for a description of the filter string
+     * @see <a href="http://www.ietf.org/rfc/rfc1960.txt">RFC 1960</a>
+     */
+    public String filter() default "";
+
+    /**
+     * If set to true, the model can be instantiated even if there is no OSGi service implementation available. Default
+     * = false.
+     */
+    public boolean optional() default false;
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/RequestAttribute.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/RequestAttribute.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/RequestAttribute.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/RequestAttribute.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,52 @@
+/*
+ * 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.annotations.injectorspecific;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods or fields to let Sling Models inject a
+ * request attribute.
+ *
+ */
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("request-attributes")
+public @interface RequestAttribute {
+
+    /**
+     * Specifies the name of the request attribute. If empty or not set, then the name
+     * is derived from the method or field.
+     */
+    public String name() default "";
+
+    /**
+     * If set to true, the model can be instantiated even if there is no request attribute
+     * with the given name found.
+     * Default = false.
+     */
+    public boolean optional() default false;
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ScriptVariable.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ScriptVariable.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ScriptVariable.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ScriptVariable.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,51 @@
+/*
+ * 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.annotations.injectorspecific;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods or fields to let Sling Models inject a
+ * script variable (from the {@link org.apache.sling.api.scripting.SlingBindings})
+ *
+ */
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("script-bindings")
+public @interface ScriptVariable {
+    /**
+     * Specifies the name of the script variable.
+     * If empty or not set, then the name is derived from the method or field.
+     */
+    public String name() default "";
+
+    /**
+     * If set to true, the model can be instantiated even if there is no
+     * scripting value with the specified name.
+     * Default = false.
+     */
+    public boolean optional() default false;
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ValueMapValue.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ValueMapValue.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ValueMapValue.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/ValueMapValue.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.annotations.injectorspecific;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.sling.models.annotations.Source;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+
+/**
+ * Annotation to be used on either methods or fields to let Sling Models inject a value from the ValueMap of the current resource.
+ *
+ */
+@Target({ METHOD, FIELD })
+@Retention(RUNTIME)
+@InjectAnnotation
+@Source("valuemap")
+public @interface ValueMapValue {
+    /**
+     * Specifies the name of the value from the value map to take.
+     * If empty, then the name is derived from the method or field.
+     */
+    String name() default "";
+
+    /**
+     * If set to true, the model can be instantiated even if that value is missing.
+     * Only considered if default is not set, because any default value implicitly
+     * sets optional to true
+     */
+    boolean optional() default false;
+
+    /**
+     * If set, then the child resource can be obtained via a projection of the given
+     * property of the adaptable.
+     */
+    String via() default "";
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/package-info.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/package-info.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/annotations/injectorspecific/package-info.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@Version("1.0.0")
+package org.apache.sling.models.annotations.injectorspecific;
+
+import aQute.bnd.annotation.Version;
\ No newline at end of file

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/AbstractInjectAnnotationProcessor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/AbstractInjectAnnotationProcessor.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/AbstractInjectAnnotationProcessor.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/AbstractInjectAnnotationProcessor.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,44 @@
+/*
+ * 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.spi.injectorspecific;
+
+/**
+ * Default implementation of ModelAnnotationProcessor.
+ *
+ */
+public class AbstractInjectAnnotationProcessor implements InjectAnnotationProcessor {
+
+    public String getName() {
+        return null;
+    }
+
+    public String getVia() {
+        return null;
+    }
+
+    public boolean hasDefault() {
+        return false;
+    }
+
+    public Object getDefault() {
+        return null;
+    }
+
+    public Boolean isOptional() {
+        return null;
+    }
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotation.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotation.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotation.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotation.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.spi.injectorspecific;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Declares an annotation as a custom inject annotation.
+ */
+@Target({ ANNOTATION_TYPE })
+@Retention(RUNTIME)
+@Documented
+public @interface InjectAnnotation {
+
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessor.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessor.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessor.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.spi.injectorspecific;
+
+/**
+ * Processor for injector-specific annotations.
+ */
+public interface InjectAnnotationProcessor {
+
+    /**
+     * Tries to get the name value from the annotation.
+     * 
+     * @return the value to be used for the name or null, in which case 
+     *         the standard annotation or name derived from method/field
+     *         should be used
+     */
+    String getName();
+
+    /**
+     * Tries to get the via value from the annotation.
+     * 
+     * @return the value to be used for the via or null, in
+     *         which case the standard annotation should be used
+     */
+    String getVia();
+
+    /**
+     * 
+     * @return true, if a default value is set
+     */
+    boolean hasDefault();
+
+    /**
+     * Tries to get the default value from the annotation. Only used if {@link hasDefaultValue()} is set to true.
+     * 
+     * @return the value to be used if nothing can be injected
+     */
+    Object getDefault();
+
+    /**
+     * Tries to get the information whether the injection is optional.
+     * 
+     * @return the value to be used for the default or null, in
+     *         which case the standard annotation should be used.
+     */
+    Boolean isOptional();
+
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessorFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessorFactory.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessorFactory.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/InjectAnnotationProcessorFactory.java Wed Jun  4 19:38:55 2014
@@ -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.spi.injectorspecific;
+
+import java.lang.reflect.AnnotatedElement;
+
+import org.apache.sling.models.spi.Injector;
+
+public interface InjectAnnotationProcessorFactory extends Injector {
+    /**
+     * 
+     * @param adaptable the object from which this model is adapted
+     * @param element the field or method which is annotated
+     * @return a ModelAnnotationProcessor in case there is a known
+     *         injector-specific annotation on the given element found otherwise
+     *         null
+     */
+    InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element);
+}

Added: sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/package-info.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/package-info.java (added)
+++ sling/trunk/bundles/extensions/models/api/src/main/java/org/apache/sling/models/spi/injectorspecific/package-info.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@Version("1.0.0")
+package org.apache.sling.models.spi.injectorspecific;
+
+import aQute.bnd.annotation.Version;
\ No newline at end of file

Modified: sling/trunk/bundles/extensions/models/impl/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/pom.xml?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/pom.xml (original)
+++ sling/trunk/bundles/extensions/models/impl/pom.xml Wed Jun  4 19:38:55 2014
@@ -63,7 +63,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.models.api</artifactId>
-            <version>1.0.0</version>
+            <version>1.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java Wed Jun  4 19:38:55 2014
@@ -34,7 +34,6 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -65,6 +64,9 @@ import org.apache.sling.models.annotatio
 import org.apache.sling.models.spi.DisposalCallback;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotation;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
@@ -177,8 +179,7 @@ public class ModelAdapterFactory impleme
         if (type.isInterface()) {
             InvocationHandler handler = createInvocationHandler(adaptable, type);
             if (handler != null) {
-                return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type },
-                        handler);
+                return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
             } else {
                 return null;
             }
@@ -196,12 +197,7 @@ public class ModelAdapterFactory impleme
         Set<Field> result = new HashSet<Field>();
         while (type != null) {
             Field[] fields = type.getDeclaredFields();
-            for (Field field : fields) {
-                Inject injection = field.getAnnotation(Inject.class);
-                if (injection != null) {
-                    result.add(field);
-                }
-            }
+            addAnnotated(fields, result);
             type = type.getSuperclass();
         }
         return result;
@@ -211,76 +207,124 @@ public class ModelAdapterFactory impleme
         Set<Method> result = new HashSet<Method>();
         while (type != null) {
             Method[] methods = type.getDeclaredMethods();
-            for (Method method : methods) {
-                Inject injection = method.getAnnotation(Inject.class);
-                if (injection != null) {
-                    result.add(method);
-                }
-            }
+            addAnnotated(methods, result);
             type = type.getSuperclass();
         }
         return result;
     }
 
-    private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type) {
-        Set<Method> injectableMethods = collectInjectableMethods(type);
-        Map<Method, Object> methods = new HashMap<Method, Object>();
-        MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods);
+    private <T extends AnnotatedElement> void addAnnotated(T[] elements, Set<T> set) {
+        for (T element : elements) {
+            Inject injection = getAnnotation(element, Inject.class);
+            if (injection != null) {
+                set.add(element);
+            } else {
+                InjectAnnotation modelInject = getAnnotation(element, InjectAnnotation.class);
+                if (modelInject != null) {
+                    set.add(element);
+                }
+            }
+        }
+    }
 
-        DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+    private static interface InjectCallback {
+        /**
+         * Is called each time when the given value should be injected into the given element
+         * @param element
+         * @param value
+         * @return true if injection was successful otherwise false
+         */
+        public boolean inject(AnnotatedElement element, Object value);
+    }
+
+    private static class SetFieldCallback implements InjectCallback {
 
+        private final Object object;
+
+        private SetFieldCallback(Object object) {
+            this.object = object;
+        }
+
+        @Override
+        public boolean inject(AnnotatedElement element, Object value) {
+            return setField((Field) element, object, value);
+        }
+    }
+
+    private static class SetMethodsCallback implements InjectCallback {
+
+        private final Map<Method, Object> methods;
+
+        private SetMethodsCallback( Map<Method, Object> methods) {
+            this.methods = methods;
+        }
+
+        @Override
+        public boolean inject(AnnotatedElement element, Object value) {
+            return setMethod((Method) element, methods, value);
+        }
+    }
+
+    private boolean injectFieldOrMethod(final AnnotatedElement element, final Object adaptable, final Type type,
+            final DisposalCallbackRegistry registry, InjectCallback callback) {
+
+        InjectAnnotationProcessor annotationProcessor = null;
+        String source = getSource(element);
+        boolean wasInjectionSuccessful = false;
+
+        // find the right injector
         for (Injector injector : sortedInjectors) {
-            Iterator<Method> it = injectableMethods.iterator();
-            while (it.hasNext()) {
-                Method method = it.next();
-                String source = getSource(method);
-                if (source == null || source.equals(injector.getName())) {
-                    String name = getName(method);
-                    Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
-                    Object injectionAdaptable = getAdaptable(adaptable, method);
-                    if (injectionAdaptable != null) {
-                        Object value = injector.getValue(injectionAdaptable, name, returnType, method, registry);
-                        if (setMethod(method, methods, value)) {
-                            it.remove();
-                        }
+            if (source == null || source.equals(injector.getName())) {
+                // get annotation processor
+                if (injector instanceof InjectAnnotationProcessorFactory) {
+                    annotationProcessor = ((InjectAnnotationProcessorFactory) injector).createAnnotationProcessor(adaptable,
+                            element);
+                }
+
+                String name = getName(element, annotationProcessor);
+                Object injectionAdaptable = getAdaptable(adaptable, element, annotationProcessor);
+                if (injectionAdaptable != null) {
+                    Object value = injector.getValue(injectionAdaptable, name, type, element, registry);
+                    if (callback.inject(element, value)) {
+                        wasInjectionSuccessful = true;
+                        break;
                     }
                 }
             }
         }
+        // if injection failed, use default
+        if (!wasInjectionSuccessful) {
+            wasInjectionSuccessful = injectDefaultValue(element, type, annotationProcessor, callback);
+        }
 
-        registry.seal();
-
-        Iterator<Method> it = injectableMethods.iterator();
-        while (it.hasNext()) {
-            Method method = it.next();
-            Default defaultAnnotation = method.getAnnotation(Default.class);
-            if (defaultAnnotation != null) {
-                Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
-                Object value = getDefaultValue(defaultAnnotation, returnType);
-                if (setMethod(method, methods, value)) {
-                    it.remove();
-                }
-            }
+        // if default is not set, check if mandatory
+        if (!wasInjectionSuccessful && !isOptional(element, annotationProcessor)) {
+            return false;
         }
+        return true;
+    }
 
-        if (injectableMethods.isEmpty()) {
-            return handler;
-        } else {
-            Set<Method> requiredMethods = new HashSet<Method>();
-            for (Method method : injectableMethods) {
-                if (method.getAnnotation(Optional.class) == null) {
-                    requiredMethods.add(method);
-                }
-            }
+    private InvocationHandler createInvocationHandler(final Object adaptable, final Class<?> type) {
+        Set<Method> injectableMethods = collectInjectableMethods(type);
+        final Map<Method, Object> methods = new HashMap<Method, Object>();
+        SetMethodsCallback callback = new SetMethodsCallback(methods);
+        MapBackedInvocationHandler handler = new MapBackedInvocationHandler(methods);
 
-            if (!requiredMethods.isEmpty()) {
-                log.warn("Required methods {} on model class {} were not able to be injected.", requiredMethods,
-                        type);
-                return null;
-            } else {
-                return handler;
+        DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(handler);
+        Set<Method> requiredMethods = new HashSet<Method>();
+
+        for (Method method : injectableMethods) {
+            Type returnType = mapPrimitiveClasses(method.getGenericReturnType());
+            if (!injectFieldOrMethod(method, adaptable, returnType, registry, callback)) {
+                requiredMethods.add(method);
             }
         }
+        registry.seal();
+        if (!requiredMethods.isEmpty()) {
+            log.warn("Required methods {} on model interface {} were not able to be injected.", requiredMethods, type);
+            return null;
+        }
+        return handler;
     }
 
     private DisposalCallbackRegistryImpl createAndRegisterCallbackRegistry(Object object) {
@@ -367,160 +411,133 @@ public class ModelAdapterFactory impleme
         } else {
             object = constructorToUse.newInstance();
         }
+        InjectCallback callback = new SetFieldCallback(object);
 
         DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(object);
 
-        for (Injector injector : sortedInjectors) {
-            Iterator<Field> it = injectableFields.iterator();
-            while (it.hasNext()) {
-                Field field = it.next();
-                String source = getSource(field);
-                if (source == null || source.equals(injector.getName())) {
-                    String name = getName(field);
-                    Type fieldType = mapPrimitiveClasses(field.getGenericType());
-                    Object injectionAdaptable = getAdaptable(adaptable, field);
-                    if (injectionAdaptable != null) {
-                        Object value = injector.getValue(injectionAdaptable, name, fieldType, field, registry);
-                        if (setField(field, object, value)) {
-                            it.remove();
-                        }
-                    }
-                }
+        Set<Field> requiredFields = new HashSet<Field>();
+
+        for (Field field : injectableFields) {
+            Type fieldType = mapPrimitiveClasses(field.getGenericType());
+            if (!injectFieldOrMethod(field, adaptable, fieldType, registry, callback)) {
+                requiredFields.add(field);
             }
         }
 
         registry.seal();
+        if (!requiredFields.isEmpty()) {
+            log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields, type);
+            return null;
+        }
+        try {
+            invokePostConstruct(object);
+            return object;
+        } catch (Exception e) {
+            log.error("Unable to invoke post construct method.", e);
+            return null;
+        }
 
-        Iterator<Field> it = injectableFields.iterator();
-        while (it.hasNext()) {
-            Field field = it.next();
-            Default defaultAnnotation = field.getAnnotation(Default.class);
-            if (defaultAnnotation != null) {
-                Type fieldType = mapPrimitiveClasses(field.getGenericType());
-                Object value = getDefaultValue(defaultAnnotation, fieldType);
-                if (setField(field, object, value)) {
-                    it.remove();
-                }
+    }
+
+    private boolean isOptional(AnnotatedElement point, InjectAnnotationProcessor annotationProcessor) {
+        if (annotationProcessor != null) {
+            Boolean isOptional = annotationProcessor.isOptional();
+            if (isOptional != null) {
+                return isOptional.booleanValue();
             }
         }
+        return (point.getAnnotation(Optional.class) != null);
+    }
 
-        if (injectableFields.isEmpty()) {
-            try {
-                invokePostConstruct(object);
-                return object;
-            } catch (Exception e) {
-                log.error("Unable to invoke post construct method.", e);
-                return null;
-            }
-        } else {
-            Set<Field> requiredFields = new HashSet<Field>();
-            for (Field field : injectableFields) {
-                if (field.getAnnotation(Optional.class) == null) {
-                    requiredFields.add(field);
-                }
-            }
+    private boolean injectDefaultValue(AnnotatedElement point, Type type, InjectAnnotationProcessor processor,
+            InjectCallback callback) {
 
-            if (!requiredFields.isEmpty()) {
-                log.warn("Required properties {} on model class {} were not able to be injected.", requiredFields,
-                        type);
-                return null;
-            } else {
-                try {
-                    invokePostConstruct(object);
-                    return object;
-                } catch (Exception e) {
-                    log.error("Unable to invoke post construct method.", e);
-                    return null;
-                }
+        if (processor != null) {
+            if (processor.hasDefault()) {
+                return callback.inject(point, processor.getDefault());
             }
         }
-    }
+        Default defaultAnnotation = point.getAnnotation(Default.class);
+        if (defaultAnnotation == null) {
+            return false;
+        }
+
+        type = mapPrimitiveClasses(type);
+        Object value = null;
 
-    private Object getDefaultValue(Default defaultAnnotation, Type type) {
         if (type instanceof Class) {
             Class<?> injectedClass = (Class<?>) type;
             if (injectedClass.isArray()) {
                 Class<?> componentType = injectedClass.getComponentType();
                 if (componentType == String.class) {
-                    return defaultAnnotation.values();
-                }
-                if (componentType == Integer.TYPE) {
-                    return defaultAnnotation.intValues();
-                }
-                if (componentType == Integer.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.intValues());
-                }
-                if (componentType == Long.TYPE) {
-                    return defaultAnnotation.longValues();
-                }
-                if (componentType == Long.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.longValues());
-                }
-                if (componentType == Boolean.TYPE) {
-                    return defaultAnnotation.booleanValues();
-                }
-                if (componentType == Boolean.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.booleanValues());
-                }
-                if (componentType == Short.TYPE) {
-                    return defaultAnnotation.shortValues();
-                }
-                if (componentType == Short.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.shortValues());
-                }
-                if (componentType == Float.TYPE) {
-                    return defaultAnnotation.floatValues();
-                }
-                if (componentType == Float.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.floatValues());
-                }
-                if (componentType == Double.TYPE) {
-                    return defaultAnnotation.doubleValues();
-                }
-                if (componentType == Double.class) {
-                    return ArrayUtils.toObject(defaultAnnotation.doubleValues());
+                    value = defaultAnnotation.values();
+                } else if (componentType == Integer.TYPE) {
+                    value = defaultAnnotation.intValues();
+                } else if (componentType == Integer.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.intValues());
+                } else if (componentType == Long.TYPE) {
+                    value = defaultAnnotation.longValues();
+                } else if (componentType == Long.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.longValues());
+                } else if (componentType == Boolean.TYPE) {
+                    value = defaultAnnotation.booleanValues();
+                } else if (componentType == Boolean.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.booleanValues());
+                } else if (componentType == Short.TYPE) {
+                    value = defaultAnnotation.shortValues();
+                } else if (componentType == Short.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.shortValues());
+                } else if (componentType == Float.TYPE) {
+                    value = defaultAnnotation.floatValues();
+                } else if (componentType == Float.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.floatValues());
+                } else if (componentType == Double.TYPE) {
+                    value = defaultAnnotation.doubleValues();
+                } else if (componentType == Double.class) {
+                    value = ArrayUtils.toObject(defaultAnnotation.doubleValues());
+                } else {
+                    log.warn("Default values for {} are not supported", componentType);
+                    return false;
                 }
-
-                log.warn("Default values for {} are not supported", componentType);
-                return null;
             } else {
                 if (injectedClass == String.class) {
-                    return defaultAnnotation.values()[0];
-                }
-                if (injectedClass == Integer.class) {
-                    return defaultAnnotation.intValues()[0];
-                }
-                if (injectedClass == Long.class) {
-                    return defaultAnnotation.longValues()[0];
-                }
-                if (injectedClass == Boolean.class) {
-                    return defaultAnnotation.booleanValues()[0];
-                }
-                if (injectedClass == Short.class) {
-                    return defaultAnnotation.shortValues()[0];
-                }
-                if (injectedClass == Float.class) {
-                    return defaultAnnotation.floatValues()[0];
-                }
-                if (injectedClass == Double.class) {
-                    return defaultAnnotation.doubleValues()[0];
+                    value = defaultAnnotation.values()[0];
+                } else if (injectedClass == Integer.class) {
+                    value = defaultAnnotation.intValues()[0];
+                } else if (injectedClass == Long.class) {
+                    value = defaultAnnotation.longValues()[0];
+                } else if (injectedClass == Boolean.class) {
+                    value = defaultAnnotation.booleanValues()[0];
+                } else if (injectedClass == Short.class) {
+                    value = defaultAnnotation.shortValues()[0];
+                } else if (injectedClass == Float.class) {
+                    value = defaultAnnotation.floatValues()[0];
+                } else if (injectedClass == Double.class) {
+                    value = defaultAnnotation.doubleValues()[0];
+                } else {
+                    log.warn("Default values for {} are not supported", injectedClass);
+                    return false;
                 }
-
-                log.warn("Default values for {} are not supported", injectedClass);
-                return null;
             }
         } else {
             log.warn("Cannot provide default for {}", type);
-            return null;
+            return false;
         }
+        return callback.inject(point, value);
     }
 
-    private Object getAdaptable(Object adaptable, AnnotatedElement point) {
-        Via viaAnnotation = point.getAnnotation(Via.class);
-        if (viaAnnotation == null) {
-            return adaptable;
+    private Object getAdaptable(Object adaptable, AnnotatedElement point, InjectAnnotationProcessor processor) {
+        String viaPropertyName = null;
+        if (processor != null) {
+            viaPropertyName = processor.getVia();
+        }
+        if (viaPropertyName == null) {
+            Via viaAnnotation = point.getAnnotation(Via.class);
+            if (viaAnnotation == null) {
+                return adaptable;
+            }
+            viaPropertyName = viaAnnotation.value();
         }
-        String viaPropertyName = viaAnnotation.value();
         try {
             return PropertyUtils.getProperty(adaptable, viaPropertyName);
         } catch (Exception e) {
@@ -529,19 +546,33 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private String getName(Field field) {
-        Named named = field.getAnnotation(Named.class);
+    private String getName(AnnotatedElement element, InjectAnnotationProcessor processor) {
+        // try to get the name from injector-specific annotation
+        if (processor != null) {
+            String name = processor.getName();
+            if (name != null) {
+                return name;
+            }
+        }
+        // alternative for name attribute
+        Named named = element.getAnnotation(Named.class);
         if (named != null) {
             return named.value();
         }
+        if (element instanceof Method) {
+            return getNameFromMethod((Method) element);
+        } else if (element instanceof Field) {
+            return getNameFromField((Field) element);
+        } else {
+            throw new IllegalArgumentException("The given element must be either method or field but is " + element);
+        }
+    }
+
+    private String getNameFromField(Field field) {
         return field.getName();
     }
 
-    private String getName(Method method) {
-        Named named = method.getAnnotation(Named.class);
-        if (named != null) {
-            return named.value();
-        }
+    private String getNameFromMethod(Method method) {
         String methodName = method.getName();
         if (methodName.startsWith("get")) {
             return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
@@ -606,7 +637,7 @@ public class ModelAdapterFactory impleme
         return type;
     }
 
-    private boolean setField(Field field, Object createdObject, Object value) {
+    private static boolean setField(Field field, Object createdObject, Object value) {
         if (value != null) {
             if (!isAcceptableType(field.getType(), value) && value instanceof Adaptable) {
                 value = ((Adaptable) value).adaptTo(field.getType());
@@ -634,7 +665,7 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
+    private static boolean setMethod(Method method, Map<Method, Object> methods, Object value) {
         if (value != null) {
             if (!isAcceptableType(method.getReturnType(), value) && value instanceof Adaptable) {
                 value = ((Adaptable) value).adaptTo(method.getReturnType());
@@ -649,7 +680,7 @@ public class ModelAdapterFactory impleme
         }
     }
 
-    private boolean isAcceptableType(Class<?> type, Object value) {
+    private static boolean isAcceptableType(Class<?> type, Object value) {
         if (type.isInstance(value)) {
             return true;
         }
@@ -690,8 +721,7 @@ public class ModelAdapterFactory impleme
         properties.put("scheduler.concurrent", false);
         properties.put("scheduler.period", Long.valueOf(30));
 
-        this.jobRegistration = bundleContext.registerService(Runnable.class.getName(), this,
-                properties);
+        this.jobRegistration = bundleContext.registerService(Runnable.class.getName(), this, properties);
 
         this.listener = new ModelPackageBundleListener(ctx.getBundleContext(), this);
 
@@ -703,7 +733,7 @@ public class ModelAdapterFactory impleme
         printerProps.put("felix.webconsole.configprinter.modes", "always");
 
         this.configPrinterRegistration = bundleContext.registerService(Object.class.getName(),
-            new ModelConfigurationPrinter(this), printerProps);
+                new ModelConfigurationPrinter(this), printerProps);
     }
 
     @Deactivate

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/BindingsInjector.java Wed Jun  4 19:38:55 2014
@@ -25,8 +25,12 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -34,7 +38,7 @@ import org.slf4j.LoggerFactory;
 @Component
 @Service
 @Property(name = Constants.SERVICE_RANKING, intValue = 1000)
-public class BindingsInjector implements Injector {
+public class BindingsInjector implements Injector, InjectAnnotationProcessorFactory {
 
     private static final Logger log = LoggerFactory.getLogger(BindingsInjector.class);
 
@@ -52,7 +56,8 @@ public class BindingsInjector implements
         }
     }
 
-    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+            DisposalCallbackRegistry callbackRegistry) {
         SlingBindings bindings = getBindings(adaptable);
         if (bindings == null) {
             return null;
@@ -75,4 +80,38 @@ public class BindingsInjector implements
         }
     }
 
+    @Override
+    public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+        // check if the element has the expected annotation
+        ScriptVariable annotation = element.getAnnotation(ScriptVariable.class);
+        if (annotation != null) {
+            return new ScriptVariableAnnotationProcessor(annotation);
+        }
+        return null;
+    }
+
+    private static class ScriptVariableAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+        private final ScriptVariable annotation;
+
+        public ScriptVariableAnnotationProcessor(ScriptVariable annotation) {
+            this.annotation = annotation;
+        }
+
+        @Override
+        public Boolean isOptional() {
+            return annotation.optional();
+        }
+
+        @Override
+        public String getName() {
+            // since null is not allowed as default value in annotations, the empty string means, the default should be
+            // used!
+            if (annotation.name().isEmpty()) {
+                return null;
+            }
+            return annotation.name();
+        }
+    }
+
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ChildResourceInjector.java Wed Jun  4 19:38:55 2014
@@ -19,18 +19,24 @@ package org.apache.sling.models.impl.inj
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Type;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.injectorspecific.ChildResource;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.Constants;
 
 @Component
 @Service
 @Property(name = Constants.SERVICE_RANKING, intValue = 3000)
-public class ChildResourceInjector implements Injector {
+public class ChildResourceInjector implements Injector, InjectAnnotationProcessorFactory {
 
     @Override
     public String getName() {
@@ -38,7 +44,8 @@ public class ChildResourceInjector imple
     }
 
     @Override
-    public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+    public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+            DisposalCallbackRegistry callbackRegistry) {
         if (adaptable instanceof Resource) {
             return ((Resource) adaptable).getChild(name);
         } else {
@@ -46,4 +53,53 @@ public class ChildResourceInjector imple
         }
     }
 
+    @Override
+    public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+        // check if the element has the expected annotation
+        ChildResource annotation = element.getAnnotation(ChildResource.class);
+        if (annotation != null) {
+            return new ChildResourceAnnotationProcessor(annotation, adaptable);
+        }
+        return null;
+    }
+
+    private static class ChildResourceAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+        private final ChildResource annotation;
+        private final Object adaptable;
+
+        public ChildResourceAnnotationProcessor(ChildResource annotation, Object adaptable) {
+            this.annotation = annotation;
+            this.adaptable = adaptable;
+        }
+
+        @Override
+        public String getName() {
+            // since null is not allowed as default value in annotations, the empty string means, the default should be
+            // used!
+            if (annotation.name().isEmpty()) {
+                return null;
+            }
+            return annotation.name();
+        }
+
+        @Override
+        public Boolean isOptional() {
+            return annotation.optional();
+        }
+
+        @Override
+        public String getVia() {
+            if (StringUtils.isNotBlank(annotation.via())) {
+                return annotation.via();
+            }
+            // automatically go via resource, if this is the httprequest
+            if (adaptable instanceof SlingHttpServletRequest) {
+                return "resource";
+            } else {
+                return null;
+            }
+        }
+    }
+
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/OSGiServiceInjector.java Wed Jun  4 19:38:55 2014
@@ -27,6 +27,7 @@ import java.util.List;
 
 import javax.servlet.ServletRequest;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
@@ -34,9 +35,13 @@ import org.apache.felix.scr.annotations.
 import org.apache.sling.api.scripting.SlingBindings;
 import org.apache.sling.api.scripting.SlingScriptHelper;
 import org.apache.sling.models.annotations.Filter;
+import org.apache.sling.models.annotations.injectorspecific.OSGiService;
 import org.apache.sling.models.spi.DisposalCallback;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
@@ -48,7 +53,7 @@ import org.slf4j.LoggerFactory;
 @Component
 @Service
 @Property(name = Constants.SERVICE_RANKING, intValue = 5000)
-public class OSGiServiceInjector implements Injector {
+public class OSGiServiceInjector implements Injector, InjectAnnotationProcessorFactory {
 
     private static final Logger log = LoggerFactory.getLogger(OSGiServiceInjector.class);
 
@@ -64,17 +69,25 @@ public class OSGiServiceInjector impleme
         this.bundleContext = ctx.getBundleContext();
     }
 
-    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
-        Filter filter = element.getAnnotation(Filter.class);
+    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+            DisposalCallbackRegistry callbackRegistry) {
+        OSGiService annotation = element.getAnnotation(OSGiService.class);
         String filterString = null;
-        if (filter != null) {
-            filterString = filter.value();
+        if (annotation != null) {
+            if (StringUtils.isNotBlank(annotation.filter())) {
+                filterString = annotation.filter();
+            }
+        } else {
+            Filter filter = element.getAnnotation(Filter.class);
+            if (filter != null) {
+                filterString = filter.value();
+            }
         }
-
         return getValue(adaptable, type, filterString, callbackRegistry);
     }
 
-    private <T> Object getService(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) {
+    private <T> Object getService(Object adaptable, Class<T> type, String filter,
+            DisposalCallbackRegistry callbackRegistry) {
         SlingScriptHelper helper = getScriptHelper(adaptable);
 
         if (helper != null) {
@@ -100,7 +113,8 @@ public class OSGiServiceInjector impleme
         }
     }
 
-    private <T> Object[] getServices(Object adaptable, Class<T> type, String filter, DisposalCallbackRegistry callbackRegistry) {
+    private <T> Object[] getServices(Object adaptable, Class<T> type, String filter,
+            DisposalCallbackRegistry callbackRegistry) {
         SlingScriptHelper helper = getScriptHelper(adaptable);
 
         if (helper != null) {
@@ -147,7 +161,8 @@ public class OSGiServiceInjector impleme
         if (type instanceof Class) {
             Class<?> injectedClass = (Class<?>) type;
             if (injectedClass.isArray()) {
-                Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString, callbackRegistry);
+                Object[] services = getServices(adaptable, injectedClass.getComponentType(), filterString,
+                        callbackRegistry);
                 if (services == null) {
                     return null;
                 }
@@ -180,16 +195,16 @@ public class OSGiServiceInjector impleme
             return null;
         }
     }
-    
+
     private static class Callback implements DisposalCallback {
         private final ServiceReference[] refs;
         private final BundleContext context;
-        
+
         public Callback(ServiceReference[] refs, BundleContext context) {
             this.refs = refs;
             this.context = context;
         }
-        
+
         @Override
         public void onDisposed() {
             if (refs != null) {
@@ -200,4 +215,29 @@ public class OSGiServiceInjector impleme
         }
     }
 
+    @Override
+    public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+        // check if the element has the expected annotation
+        OSGiService annotation = element.getAnnotation(OSGiService.class);
+        if (annotation != null) {
+            return new OSGiServiceAnnotationProcessor(annotation);
+        }
+        return null;
+    }
+
+    private static class OSGiServiceAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+        private final OSGiService annotation;
+
+        public OSGiServiceAnnotationProcessor(OSGiService annotation) {
+            this.annotation = annotation;
+        }
+
+        @Override
+        public Boolean isOptional() {
+            return annotation.optional();
+        }
+    }
+
+
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/RequestAttributeInjector.java Wed Jun  4 19:38:55 2014
@@ -24,8 +24,12 @@ import javax.servlet.ServletRequest;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.models.annotations.injectorspecific.RequestAttribute;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,22 +37,23 @@ import org.slf4j.LoggerFactory;
 @Component
 @Service
 @Property(name = Constants.SERVICE_RANKING, intValue = 4000)
-public class RequestAttributeInjector implements Injector {
+public class RequestAttributeInjector implements Injector, InjectAnnotationProcessorFactory {
 
     private static final Logger log = LoggerFactory.getLogger(RequestAttributeInjector.class);
-    
+
     @Override
     public String getName() {
         return "request-attributes";
     }
 
     @Override
-    public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+    public Object getValue(Object adaptable, String name, Type declaredType, AnnotatedElement element,
+            DisposalCallbackRegistry callbackRegistry) {
         if (!(adaptable instanceof ServletRequest)) {
             return null;
         } else if (declaredType instanceof Class<?>) {
             Class<?> clazz = (Class<?>) declaredType;
-            Object attribute = ((ServletRequest)adaptable).getAttribute(name);
+            Object attribute = ((ServletRequest) adaptable).getAttribute(name);
             if (clazz.isInstance(attribute)) {
                 return attribute;
             } else {
@@ -60,4 +65,39 @@ public class RequestAttributeInjector im
         }
     }
 
+    @Override
+    public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+        // check if the element has the expected annotation
+        RequestAttribute annotation = element.getAnnotation(RequestAttribute.class);
+        if (annotation != null) {
+            return new RequestAttributeAnnotationProcessor(annotation);
+        }
+        return null;
+    }
+
+    private static class RequestAttributeAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+        private final RequestAttribute annotation;
+
+        public RequestAttributeAnnotationProcessor(RequestAttribute annotation) {
+            this.annotation = annotation;
+        }
+
+        @Override
+        public Boolean isOptional() {
+            return annotation.optional();
+        }
+
+        @Override
+        public String getName() {
+            // since null is not allowed as default value in annotations, the empty string means, the default should be
+            // used!
+            if (annotation.name().isEmpty()) {
+                return null;
+            }
+            return annotation.name();
+        }
+    }
+
+    
 }

Modified: sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java?rev=1600469&r1=1600468&r2=1600469&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java (original)
+++ sling/trunk/bundles/extensions/models/impl/src/main/java/org/apache/sling/models/impl/injectors/ValueMapInjector.java Wed Jun  4 19:38:55 2014
@@ -19,13 +19,19 @@ package org.apache.sling.models.impl.inj
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Type;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.adapter.Adaptable;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
 import org.apache.sling.models.spi.DisposalCallbackRegistry;
 import org.apache.sling.models.spi.Injector;
+import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor;
+import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory;
 import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,16 +39,17 @@ import org.slf4j.LoggerFactory;
 @Component
 @Service
 @Property(name = Constants.SERVICE_RANKING, intValue = 2000)
-public class ValueMapInjector implements Injector {
+public class ValueMapInjector implements InjectAnnotationProcessorFactory, Injector {
 
     private static final Logger log = LoggerFactory.getLogger(ValueMapInjector.class);
-    
+
     @Override
     public String getName() {
         return "valuemap";
     }
 
-    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element, DisposalCallbackRegistry callbackRegistry) {
+    public Object getValue(Object adaptable, String name, Type type, AnnotatedElement element,
+            DisposalCallbackRegistry callbackRegistry) {
         ValueMap map = getMap(adaptable);
         if (map == null) {
             return null;
@@ -65,4 +72,53 @@ public class ValueMapInjector implements
         }
     }
 
+    @Override
+    public InjectAnnotationProcessor createAnnotationProcessor(Object adaptable, AnnotatedElement element) {
+        // check if the element has the expected annotation
+        ValueMapValue annotation = element.getAnnotation(ValueMapValue.class);
+        if (annotation != null) {
+            return new ValueAnnotationProcessor(annotation, adaptable);
+        }
+        return null;
+    }
+
+    private static class ValueAnnotationProcessor extends AbstractInjectAnnotationProcessor {
+
+        private final ValueMapValue annotation;
+
+        private final Object adaptable;
+
+        public ValueAnnotationProcessor(ValueMapValue annotation, Object adaptable) {
+            this.annotation = annotation;
+            this.adaptable = adaptable;
+        }
+
+        @Override
+        public String getName() {
+            // since null is not allowed as default value in annotations, the empty string means, the default should be
+            // used!
+            if (annotation.name().isEmpty()) {
+                return null;
+            }
+            return annotation.name();
+        }
+
+        @Override
+        public String getVia() {
+            if (StringUtils.isNotBlank(annotation.via())) {
+                return annotation.via();
+            }
+            // automatically go via resource, if this is the httprequest
+            if (adaptable instanceof SlingHttpServletRequest) {
+                return "resource";
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public Boolean isOptional() {
+            return annotation.optional();
+        }
+    }
 }

Added: sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java?rev=1600469&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java (added)
+++ sling/trunk/bundles/extensions/models/impl/src/test/java/org/apache/sling/models/impl/CustomInjectorTest.java Wed Jun  4 19:38:55 2014
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.impl;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import javax.inject.Inject;
+
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.impl.injector.CustomAnnotation;
+import org.apache.sling.models.impl.injector.CustomAnnotationInjector;
+import org.apache.sling.models.impl.injector.SimpleInjector;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CustomInjectorTest {
+
+    @Mock
+    private ComponentContext componentCtx;
+
+    @Mock
+    private BundleContext bundleContext;
+
+    private ModelAdapterFactory factory;
+
+    @Before
+    public void setup() {
+        when(componentCtx.getBundleContext()).thenReturn(bundleContext);
+
+        factory = new ModelAdapterFactory();
+        factory.activate(componentCtx);
+    }
+
+    @Test
+    public void testInjectorWhichDoesNotImplementAnnotationProcessor() {
+        factory.bindInjector(new SimpleInjector(), new ServicePropertiesMap(1, 1));
+
+        TestModel model = factory.getAdapter(new Object(), TestModel.class);
+        assertNotNull(model);
+        assertEquals("test string", model.getTestString());
+    }
+
+    @Test
+    public void testInjectorWithCustomAnnotation() {
+        factory.bindInjector(new SimpleInjector(), new ServicePropertiesMap(1, 1));
+        factory.bindInjector(new CustomAnnotationInjector(), new ServicePropertiesMap(1, 1));
+
+        CustomAnnotationModel model = factory.getAdapter(new Object(), CustomAnnotationModel.class);
+        assertNotNull(model);
+        assertEquals("default value", model.getDefaultString());
+        assertEquals("custom value", model.getCustomString());
+    }
+
+    @Model(adaptables = Object.class)
+    public interface TestModel {
+        @Inject
+        String getTestString();
+    }
+
+    @Model(adaptables = Object.class)
+    public interface CustomAnnotationModel {
+        @CustomAnnotation
+        String getDefaultString();
+
+        @Inject
+        String getCustomString();
+    }
+
+}