This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.models.impl-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-impl.git
commit e9a5ebd8f6acc142a2849c6d1f58c605b2d38731 Author: Justin Edelson <[email protected]> AuthorDate: Fri Jan 31 04:07:05 2014 +0000 SLING-3357 - allow a model class to have a single argument constructor which takes the adaptable as a parameter. git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/models/impl@1563049 13f79535-47bb-0310-9956-ffa450edef68 --- .../sling/models/impl/ModelAdapterFactory.java | 61 +++++++++++- .../apache/sling/models/impl/ConstructorTest.java | 104 +++++++++++++++++++++ .../classes/InvalidConstructorModel.java | 44 +++++++++ .../classes/SuperclassConstructorModel.java | 47 ++++++++++ .../classes/WithOneConstructorModel.java | 43 +++++++++ .../classes/WithThreeConstructorsModel.java | 58 ++++++++++++ .../classes/WithTwoConstructorsModel.java | 46 +++++++++ 7 files changed, 401 insertions(+), 2 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 92fc791..7d29234 100644 --- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java +++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java @@ -20,13 +20,17 @@ import java.lang.annotation.Annotation; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -70,6 +74,19 @@ import org.slf4j.LoggerFactory; @Component public class ModelAdapterFactory implements AdapterFactory, Runnable { + /** + * Comparator which sorts constructors by the number of parameters + * in reverse order (most params to least params). + */ + public class ParameterCountComparator implements Comparator<Constructor<?>> { + + @Override + public int compare(Constructor<?> o1, Constructor<?> o2) { + return Integer.compare(o2.getParameterTypes().length, o1.getParameterTypes().length); + } + + } + private static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry { private List<DisposalCallback> callbacks = new ArrayList<DisposalCallback>(); @@ -282,11 +299,51 @@ public class ModelAdapterFactory implements AdapterFactory, Runnable { return null; } + @SuppressWarnings("unchecked") private <AdapterType> AdapterType createObject(Object adaptable, Class<AdapterType> type) - throws InstantiationException, IllegalAccessException { + throws InstantiationException, InvocationTargetException, IllegalAccessException { Set<Field> injectableFields = collectInjectableFields(type); - AdapterType object = type.newInstance(); + Constructor<?>[] constructors = type.getConstructors(); + if (constructors.length == 0) { + log.warn("Model class {} does not have a public constructor.", type.getName()); + return null; + } + + // sort the constructor list in order from most params to least params + Arrays.sort(constructors, new ParameterCountComparator()); + + Constructor<AdapterType> constructorToUse = null; + boolean constructorHasParam = false; + for (Constructor<?> constructor : constructors) { + final Class<?>[] paramTypes = constructor.getParameterTypes(); + if (paramTypes.length == 1) { + Class<?> paramType = constructor.getParameterTypes()[0]; + if (paramType.isInstance(adaptable)) { + constructorToUse = (Constructor<AdapterType>) constructor; + constructorHasParam = true; + break; + } + } + + if (constructor.getParameterTypes().length == 0) { + constructorToUse = (Constructor<AdapterType>) constructor; + constructorHasParam = false; + break; + } + } + + if (constructorToUse == null) { + log.warn("Model class {} does not have a usable constructor", type.getName()); + return null; + } + + final AdapterType object; + if (constructorHasParam) { + object = constructorToUse.newInstance(adaptable); + } else { + object = constructorToUse.newInstance(); + } DisposalCallbackRegistryImpl registry = createAndRegisterCallbackRegistry(object); diff --git a/src/test/java/org/apache/sling/models/impl/ConstructorTest.java b/src/test/java/org/apache/sling/models/impl/ConstructorTest.java new file mode 100644 index 0000000..130818d --- /dev/null +++ b/src/test/java/org/apache/sling/models/impl/ConstructorTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.impl; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.impl.injectors.RequestAttributeInjector; +import org.apache.sling.models.testmodels.classes.InvalidConstructorModel; +import org.apache.sling.models.testmodels.classes.SuperclassConstructorModel; +import org.apache.sling.models.testmodels.classes.WithOneConstructorModel; +import org.apache.sling.models.testmodels.classes.WithThreeConstructorsModel; +import org.apache.sling.models.testmodels.classes.WithTwoConstructorsModel; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.component.ComponentContext; + +@RunWith(MockitoJUnitRunner.class) +public class ConstructorTest { + + @Mock + private ComponentContext componentCtx; + + @Mock + private BundleContext bundleContext; + + private ModelAdapterFactory factory; + + @Mock + private SlingHttpServletRequest request; + + @Before + public void setup() { + when(componentCtx.getBundleContext()).thenReturn(bundleContext); + + when(request.getAttribute("attribute")).thenReturn(42); + + factory = new ModelAdapterFactory(); + factory.activate(componentCtx); + factory.bindInjector(new RequestAttributeInjector(), + Collections.<String, Object> singletonMap(Constants.SERVICE_ID, 0L)); + } + + @Test + public void testConstructorInjection() { + WithOneConstructorModel model = factory.getAdapter(request, WithOneConstructorModel.class); + assertNotNull(model); + assertEquals(request, model.getRequest()); + assertEquals(42, model.getAttribute()); + } + + @Test + public void testThreeConstructorsInjection() { + WithThreeConstructorsModel model = factory.getAdapter(request, WithThreeConstructorsModel.class); + assertNotNull(model); + assertEquals(request, model.getRequest()); + assertEquals(42, model.getAttribute()); + } + + @Test + public void testTwoConstructorsInjection() { + WithTwoConstructorsModel model = factory.getAdapter(request, WithTwoConstructorsModel.class); + assertNotNull(model); + assertEquals(request, model.getRequest()); + assertEquals(42, model.getAttribute()); + } + + @Test + public void testSuperclassConstructorsInjection() { + SuperclassConstructorModel model = factory.getAdapter(request, SuperclassConstructorModel.class); + assertNotNull(model); + assertEquals(request, model.getRequest()); + assertEquals(42, model.getAttribute()); + } + + @Test + public void testInvalidConstructorInjector() { + InvalidConstructorModel model = factory.getAdapter(request, InvalidConstructorModel.class); + assertNull(model); + } + +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/InvalidConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/InvalidConstructorModel.java new file mode 100644 index 0000000..bf032b6 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/InvalidConstructorModel.java @@ -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.testmodels.classes; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = SlingHttpServletRequest.class) +public class InvalidConstructorModel { + + private HttpServletResponse response; + + @Inject + private int attribute; + + public InvalidConstructorModel(HttpServletResponse response) { + this.response = response; + } + + public int getAttribute() { + return attribute; + } + + public HttpServletResponse getResponse() { + return response; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/SuperclassConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/SuperclassConstructorModel.java new file mode 100644 index 0000000..c764fc7 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/SuperclassConstructorModel.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.classes; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = SlingHttpServletRequest.class) +public class SuperclassConstructorModel { + + private HttpServletRequest request; + + @Inject + private int attribute; + + public SuperclassConstructorModel(HttpServletRequest request) { + this.request = request; + } + + public SuperclassConstructorModel() { + } + + public int getAttribute() { + return attribute; + } + + public HttpServletRequest getRequest() { + return request; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/WithOneConstructorModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/WithOneConstructorModel.java new file mode 100644 index 0000000..c16b980 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/WithOneConstructorModel.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = SlingHttpServletRequest.class) +public class WithOneConstructorModel { + + private final SlingHttpServletRequest request; + + @Inject + private int attribute; + + public WithOneConstructorModel(SlingHttpServletRequest request) { + this.request = request; + } + + public int getAttribute() { + return attribute; + } + + public SlingHttpServletRequest getRequest() { + return request; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/WithThreeConstructorsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/WithThreeConstructorsModel.java new file mode 100644 index 0000000..d701341 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/WithThreeConstructorsModel.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = SlingHttpServletRequest.class) +public class WithThreeConstructorsModel { + + private SlingHttpServletRequest request; + + private SlingHttpServletResponse response; + + @Inject + private int attribute; + + public WithThreeConstructorsModel(SlingHttpServletRequest request, SlingHttpServletResponse response) { + this.request = request; + this.response = response; + } + + public WithThreeConstructorsModel(SlingHttpServletRequest request) { + this.request = request; + } + + public WithThreeConstructorsModel() { + } + + public int getAttribute() { + return attribute; + } + + public SlingHttpServletRequest getRequest() { + return request; + } + + public SlingHttpServletResponse getResponse() { + return response; + } +} diff --git a/src/test/java/org/apache/sling/models/testmodels/classes/WithTwoConstructorsModel.java b/src/test/java/org/apache/sling/models/testmodels/classes/WithTwoConstructorsModel.java new file mode 100644 index 0000000..edadf24 --- /dev/null +++ b/src/test/java/org/apache/sling/models/testmodels/classes/WithTwoConstructorsModel.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.models.testmodels.classes; + +import javax.inject.Inject; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.models.annotations.Model; + +@Model(adaptables = SlingHttpServletRequest.class) +public class WithTwoConstructorsModel { + + private SlingHttpServletRequest request; + + @Inject + private int attribute; + + public WithTwoConstructorsModel(SlingHttpServletRequest request) { + this.request = request; + } + + public WithTwoConstructorsModel() { + } + + public int getAttribute() { + return attribute; + } + + public SlingHttpServletRequest getRequest() { + return request; + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
