You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2021/12/04 13:28:30 UTC

[sling-org-apache-sling-models-impl] branch master updated: SLING-8069: allow non-public constructors (#11)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git


The following commit(s) were added to refs/heads/master by this push:
     new d56cf5f  SLING-8069: allow non-public constructors (#11)
d56cf5f is described below

commit d56cf5f3013c2095b876c408510c29ec71ea3b05
Author: paul-bjorkstrand <pa...@perficient.com>
AuthorDate: Sat Dec 4 07:28:24 2021 -0600

    SLING-8069: allow non-public constructors (#11)
---
 .../sling/models/impl/ModelAdapterFactory.java     | 13 ++---
 .../apache/sling/models/impl/model/ModelClass.java | 12 ++--
 .../models/impl/model/ModelClassConstructor.java   | 46 +++++++++++++--
 .../models/impl/ConstructorVisibilityTest.java     | 65 ++++++++++++++++++++++
 .../PackagePrivateConstructorModel.java            | 31 +++++++++++
 .../PrivateConstructorModel.java                   | 31 +++++++++++
 .../ProtectedConstructorModel.java                 | 31 +++++++++++
 7 files changed, 208 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
index 4cfb367..569671c 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -695,7 +695,8 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable, ModelFacto
         ModelType object;
         if (constructorToUse.getConstructor().getParameterTypes().length == 0) {
             // no parameters for constructor injection? instantiate it right away
-            object = constructorToUse.getConstructor().newInstance();
+          
+            object = constructorToUse.newInstance();
         } else {
             // instantiate with constructor injection
             // if this fails, make sure resources that may be claimed by injectors are cleared up again
@@ -707,13 +708,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable, ModelFacto
                 } else {
                     object = result.getValue();
                 }
-            } catch (InstantiationException ex) {
-                registry.onDisposed();
-                throw ex;
-            } catch (InvocationTargetException ex) {
-                registry.onDisposed();
-                throw ex;
-            } catch (IllegalAccessException ex) {
+            } catch (InstantiationException | InvocationTargetException | IllegalAccessException ex) {
                 registry.onDisposed();
                 throw ex;
             }
@@ -820,7 +815,7 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable, ModelFacto
             }
             return new Result<>(missingElementsException);
         }
-        return new Result<>(constructor.getConstructor().newInstance(paramValues.toArray(new Object[paramValues.size()])));
+        return new Result<>(constructor.newInstance(paramValues.toArray(new Object[paramValues.size()])));
     }
 
     private Result<Boolean> injectDefaultValue(InjectableElement point, InjectAnnotationProcessor processor,
diff --git a/src/main/java/org/apache/sling/models/impl/model/ModelClass.java b/src/main/java/org/apache/sling/models/impl/model/ModelClass.java
index 6c17e47..4f81a62 100644
--- a/src/main/java/org/apache/sling/models/impl/model/ModelClass.java
+++ b/src/main/java/org/apache/sling/models/impl/model/ModelClass.java
@@ -34,7 +34,7 @@ public class ModelClass<ModelType> {
     private final Class<ModelType> type;
     private final Model modelAnnotation;
     final DefaultInjectionStrategy defaultInjectionStrategy;
-    private volatile ModelClassConstructor<?>[] constructors;
+    private volatile ModelClassConstructor<ModelType>[] constructors;
     private volatile InjectableField[] injectableFields;
     private volatile InjectableMethod[] injectableMethods;
 
@@ -60,18 +60,18 @@ public class ModelClass<ModelType> {
     }
     
     @SuppressWarnings("unchecked")
-    private static ModelClassConstructor<?>[] getConstructors(Class<?> type, StaticInjectAnnotationProcessorFactory[] processorFactories, DefaultInjectionStrategy defaultInjectionStrategy) {
+    private static <T> ModelClassConstructor<T>[] getConstructors(Class<T> type, StaticInjectAnnotationProcessorFactory[] processorFactories, DefaultInjectionStrategy defaultInjectionStrategy) {
         if (type.isInterface()) {
             return new ModelClassConstructor[0];
         }
-        Constructor<?>[] constructors = type.getConstructors();
+        Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors();
         
         // sort the constructor list in order from most params to least params, and constructors with @Inject annotation first
         Arrays.sort(constructors, new ParameterCountInjectComparator());
 
-        ModelClassConstructor<?>[] array = new ModelClassConstructor[constructors.length];
+        ModelClassConstructor<T>[] array = new ModelClassConstructor[constructors.length];
         for (int i=0; i<array.length; i++) {
-            array[i] = new ModelClassConstructor(constructors[i], processorFactories, defaultInjectionStrategy);
+            array[i] = new ModelClassConstructor<>(constructors[i], processorFactories, defaultInjectionStrategy);
         }
         return array;
     }
@@ -112,7 +112,7 @@ public class ModelClass<ModelType> {
         return this.modelAnnotation != null;
     }
     
-    public ModelClassConstructor[] getConstructors() {
+    public ModelClassConstructor<ModelType>[] getConstructors() {
         return constructors;
     }
 
diff --git a/src/main/java/org/apache/sling/models/impl/model/ModelClassConstructor.java b/src/main/java/org/apache/sling/models/impl/model/ModelClassConstructor.java
index 4e9a047..5629839 100644
--- a/src/main/java/org/apache/sling/models/impl/model/ModelClassConstructor.java
+++ b/src/main/java/org/apache/sling/models/impl/model/ModelClassConstructor.java
@@ -19,6 +19,7 @@
 package org.apache.sling.models.impl.model;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Type;
 
 import javax.inject.Inject;
@@ -27,13 +28,13 @@ import org.apache.sling.models.annotations.DefaultInjectionStrategy;
 import org.apache.sling.models.impl.ReflectionUtil;
 import org.apache.sling.models.spi.injectorspecific.StaticInjectAnnotationProcessorFactory;
 
-public class ModelClassConstructor<ModelType> {
+public class ModelClassConstructor<M> {
 
-    private final Constructor<ModelType> constructor;
+    private final Constructor<M> constructor;
     private final boolean hasInjectAnnotation;
     private final ConstructorParameter[] constructorParametersArray;
 
-    public ModelClassConstructor(Constructor<ModelType> constructor, StaticInjectAnnotationProcessorFactory[] processorFactories, DefaultInjectionStrategy defaultInjectionStrategy) {
+    public ModelClassConstructor(Constructor<M> constructor, StaticInjectAnnotationProcessorFactory[] processorFactories, DefaultInjectionStrategy defaultInjectionStrategy) {
         this.constructor = constructor;
         this.hasInjectAnnotation = constructor.isAnnotationPresent(Inject.class);
 
@@ -49,10 +50,43 @@ public class ModelClassConstructor<ModelType> {
         }
     }
 
-    public Constructor<ModelType> getConstructor() {
+    /**
+     * Proxies the call to {@link Constructor#newInstance(Object...)}, checking (and
+     * setting) accessibility first.
+     * 
+     * @param parameters
+     *            the constructor parameters that are passed to
+     *            {@link Constructor#newInstance(Object...)}
+     * @return The constructed object
+     * 
+     * @throws InstantiationException when {@link Constructor#newInstance(Object...)} would throw
+     * @throws IllegalAccessException when {@link Constructor#newInstance(Object...)} would throw
+     * @throws IllegalArgumentException when {@link Constructor#newInstance(Object...)} would throw
+     * @throws InvocationTargetException when {@link Constructor#newInstance(Object...)} would throw
+     * 
+     * @see Constructor#newInstance(Object...)
+     */
+    @SuppressWarnings({"java:S3011","java:S1874"})
+    public M newInstance(Object... parameters) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        synchronized (constructor) {
+            boolean accessible = constructor.isAccessible();
+            try {
+                if (!accessible) {
+                    constructor.setAccessible(true);
+                }
+                return constructor.newInstance(parameters);
+            } finally {
+                if (!accessible) {
+                    constructor.setAccessible(false);
+                }
+            }
+        }
+    }
+
+    public Constructor<M> getConstructor() {
         return constructor;
     }
-    
+
     public boolean hasInjectAnnotation() {
         return hasInjectAnnotation;
     }
@@ -60,5 +94,5 @@ public class ModelClassConstructor<ModelType> {
     public ConstructorParameter[] getConstructorParameters() {
         return constructorParametersArray;
     };
-    
+
 }
diff --git a/src/test/java/org/apache/sling/models/impl/ConstructorVisibilityTest.java b/src/test/java/org/apache/sling/models/impl/ConstructorVisibilityTest.java
new file mode 100644
index 0000000..b1c449f
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/impl/ConstructorVisibilityTest.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;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.impl.injectors.RequestAttributeInjector;
+import org.apache.sling.models.impl.injectors.SelfInjector;
+import org.apache.sling.models.testmodels.classes.constructorvisibility.PackagePrivateConstructorModel;
+import org.apache.sling.models.testmodels.classes.constructorvisibility.PrivateConstructorModel;
+import org.apache.sling.models.testmodels.classes.constructorvisibility.ProtectedConstructorModel;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ConstructorVisibilityTest {
+    private ModelAdapterFactory factory;
+
+    @Mock
+    private SlingHttpServletRequest request;
+
+    @Before
+    public void setup() {
+        factory = AdapterFactoryTest.createModelAdapterFactory();
+        factory.bindInjector(new RequestAttributeInjector(), new ServicePropertiesMap(1, 1));
+        factory.bindInjector(new SelfInjector(), new ServicePropertiesMap(2, 2));
+        factory.adapterImplementations.addClassesAsAdapterAndImplementation(ProtectedConstructorModel.class, PackagePrivateConstructorModel.class, PrivateConstructorModel.class);
+    }
+
+    @Test
+    public void testNonPublicConstructorProtectedModel() {
+        ProtectedConstructorModel model = factory.createModel(request, ProtectedConstructorModel.class);
+        assertNotNull(model);
+    }
+    
+    @Test
+    public void testNonPublicConstructorPackagePrivateModel() {
+        PackagePrivateConstructorModel model = factory.createModel(request, PackagePrivateConstructorModel.class);
+        assertNotNull(model);
+    }
+    
+    @Test
+    public void testNonPublicConstructorPrivateModel() {
+        PrivateConstructorModel model = factory.createModel(request, PrivateConstructorModel.class);
+        assertNotNull(model);
+    }
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PackagePrivateConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PackagePrivateConstructorModel.java
new file mode 100644
index 0000000..cb3cdba
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PackagePrivateConstructorModel.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.classes.constructorvisibility;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+public class PackagePrivateConstructorModel {
+
+    @Inject
+    PackagePrivateConstructorModel() {
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PrivateConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PrivateConstructorModel.java
new file mode 100644
index 0000000..85eef0d
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/PrivateConstructorModel.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.classes.constructorvisibility;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+public class PrivateConstructorModel {
+
+    @Inject
+    private PrivateConstructorModel() {
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/ProtectedConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/ProtectedConstructorModel.java
new file mode 100644
index 0000000..aa3f2de
--- /dev/null
+++ b/src/test/java/org/apache/sling/models/testmodels/classes/constructorvisibility/ProtectedConstructorModel.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.classes.constructorvisibility;
+
+import javax.inject.Inject;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+
+@Model(adaptables = SlingHttpServletRequest.class)
+public class ProtectedConstructorModel {
+
+    @Inject
+    protected ProtectedConstructorModel() {
+    }
+
+}