SLING-3886 - adding support for adapter indirection where the adapting target is a superclass or implemented interface of the implementation class. Thanks to Stefan for the patch!
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1621361 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 191d34d..1213bb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,10 @@
</configuration>
</plugin>
<plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
@@ -324,6 +328,11 @@
<version>1.4</version>
</dependency>
<dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
diff --git a/src/main/java/org/apache/sling/models/it/ImplementsExtendsTest.java b/src/main/java/org/apache/sling/models/it/ImplementsExtendsTest.java
new file mode 100644
index 0000000..82f72bb
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/ImplementsExtendsTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.sling.api.adapter.AdapterManager;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.junit.annotations.SlingAnnotationsTestRunner;
+import org.apache.sling.junit.annotations.TestReference;
+import org.apache.sling.models.it.implpicker.CustomLastImplementationPicker;
+import org.apache.sling.models.it.models.implextend.ImplementsInterfacePropertyModel;
+import org.apache.sling.models.it.models.implextend.ImplementsInterfacePropertyModel2;
+import org.apache.sling.models.it.models.implextend.InvalidSampleServiceInterface;
+import org.apache.sling.models.it.models.implextend.SampleServiceInterface;
+import org.apache.sling.models.it.models.implextend.SimplePropertyModel;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SlingAnnotationsTestRunner.class)
+public class ImplementsExtendsTest {
+
+ @TestReference
+ private ResourceResolverFactory rrFactory;
+
+ @TestReference
+ private AdapterManager adapterManager;
+
+ private String firstValue;
+ private String secondValue;
+ private String thirdValue;
+ private ResourceResolver resolver;
+ private Resource resource;
+ private Node createdNode;
+
+ @Before
+ public void setUp() throws Exception {
+ firstValue = RandomStringUtils.randomAlphanumeric(10);
+ thirdValue = RandomStringUtils.randomAlphanumeric(10);
+
+ resolver = rrFactory.getAdministrativeResourceResolver(null);
+ Session session = resolver.adaptTo(Session.class);
+ Node rootNode = session.getRootNode();
+ createdNode = rootNode.addNode("test_" + RandomStringUtils.randomAlphanumeric(10));
+ createdNode.setProperty("first", firstValue);
+ createdNode.setProperty("third", thirdValue);
+ session.save();
+
+ resource = resolver.getResource(createdNode.getPath());
+ }
+
+ /**
+ * Try to adapt to interface, with an different implementation class that has the @Model annotation
+ */
+ @Test
+ public void testImplementsInterfaceModel() {
+ SampleServiceInterface model = adapterManager.getAdapter(resource, SampleServiceInterface.class);
+ assertNotNull(model);
+ assertEquals(ImplementsInterfacePropertyModel.class, model.getClass());
+ assertEquals(firstValue + "|" + secondValue + "|" + thirdValue, model.getAllProperties());
+ }
+
+ /**
+ * Ensure that the implementation class itself cannot be adapted to if it is not part of the "adapter" property in the annotation.
+ */
+ @Test
+ public void testImplementsInterfaceModel_ImplClassNotMapped() {
+ ImplementsInterfacePropertyModel model = adapterManager.getAdapter(resource, ImplementsInterfacePropertyModel.class);
+ assertNull(model);
+ }
+
+ /**
+ * Test implementation class with a mapping that is not valid (an interface that is not implemented).
+ */
+ @Test
+ public void testInvalidImplementsInterfaceModel() {
+ InvalidSampleServiceInterface model = adapterManager.getAdapter(resource, InvalidSampleServiceInterface.class);
+ assertNull(model);
+ }
+
+ /**
+ * Test to adapt to a superclass of the implementation class with the appropriate mapping in the @Model annotation.
+ */
+ @Test
+ public void testExtendsClassModel() {
+ SimplePropertyModel model = adapterManager.getAdapter(resource, SimplePropertyModel.class);
+ assertNotNull(model);
+ assertEquals("!" + firstValue + "|" + secondValue + "|" + thirdValue + "!", model.getAllProperties());
+ }
+
+
+ /**
+ * Try to adapt to interface, with an different implementation class that has the @Model annotation
+ */
+ @Test
+ public void testImplementsInterfaceModelWithPickLastImplementationPicker() throws RepositoryException {
+
+ Session session = resolver.adaptTo(Session.class);
+ Node node = resource.adaptTo(Node.class);
+ Node childNode = node.addNode(CustomLastImplementationPicker.CUSTOM_NAME);
+ childNode.setProperty("first", firstValue);
+ childNode.setProperty("third", thirdValue);
+ session.save();
+
+ Resource childResource = resolver.getResource(childNode.getPath());
+
+ SampleServiceInterface model = adapterManager.getAdapter(childResource, SampleServiceInterface.class);
+ assertNotNull(model);
+ assertEquals(ImplementsInterfacePropertyModel2.class, model.getClass());
+ assertEquals(firstValue + "|" + secondValue + "|" + thirdValue, model.getAllProperties());
+
+ childNode.remove();
+ session.save();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/models/it/implpicker/CustomLastImplementationPicker.java b/src/main/java/org/apache/sling/models/it/implpicker/CustomLastImplementationPicker.java
new file mode 100644
index 0000000..e7565b2
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/implpicker/CustomLastImplementationPicker.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.it.implpicker;
+
+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.resource.Resource;
+import org.apache.sling.models.spi.ImplementationPicker;
+import org.osgi.framework.Constants;
+
+/**
+ * This is a curious {@link ImplementationPicker} implementation for integration test
+ * that picks the last implementation if the resource has the name "custom";
+ */
+@Component
+@Service
+@Property(name = Constants.SERVICE_RANKING, intValue = 100)
+public class CustomLastImplementationPicker implements ImplementationPicker {
+
+ public static final String CUSTOM_NAME = "custom";
+
+ public Class<?> pick(Class<?> adapterType, Class<?>[] implementationsTypes, Object adaptable) {
+ if (adaptable instanceof Resource && StringUtils.equals(((Resource)adaptable).getName(), CUSTOM_NAME)) {
+ return implementationsTypes[implementationsTypes.length - 1];
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/ExtendsClassPropertyModel.java b/src/main/java/org/apache/sling/models/it/models/implextend/ExtendsClassPropertyModel.java
new file mode 100644
index 0000000..e4201b4
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/ExtendsClassPropertyModel.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.models.it.models.implextend;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+
+/**
+ * This is an example for a model that can not be adapted itself, but only
+ * to a superclass it extends. This superclass is defined as "adapters".
+ */
+@Model(adaptables = Resource.class, adapters={SimplePropertyModel.class})
+public class ExtendsClassPropertyModel extends SimplePropertyModel {
+
+ @Override
+ public String getAllProperties() {
+ return "!" + super.getAllProperties() + "!";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel.java b/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel.java
new file mode 100644
index 0000000..e352914
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel.java
@@ -0,0 +1,54 @@
+/*
+ * 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.it.models.implextend;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+
+/**
+ * This is an example for a model that can not be adapted itself, but only
+ * to an interface it implements. This interfaces is defined as "adapters".
+ */
+@Model(adaptables = Resource.class, adapters={SampleServiceInterface.class})
+public class ImplementsInterfacePropertyModel implements SampleServiceInterface {
+
+ @Inject
+ private String first;
+
+ @Inject
+ @Optional
+ private String second;
+
+ @Inject
+ @Named("third")
+ private String thirdProperty;
+
+ public String getAllProperties() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(first)
+ .append("|")
+ .append(second)
+ .append("|")
+ .append(thirdProperty);
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel2.java b/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel2.java
new file mode 100644
index 0000000..5244e84
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/ImplementsInterfacePropertyModel2.java
@@ -0,0 +1,53 @@
+/*
+ * 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.it.models.implextend;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+
+/**
+ * Additional model class that implements the same interface as {@link ImplementsInterfacePropertyModel}.
+ */
+@Model(adaptables = Resource.class, adapters={SampleServiceInterface.class})
+public class ImplementsInterfacePropertyModel2 implements SampleServiceInterface {
+
+ @Inject
+ private String first;
+
+ @Inject
+ @Optional
+ private String second;
+
+ @Inject
+ @Named("third")
+ private String thirdProperty;
+
+ public String getAllProperties() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(first)
+ .append("|")
+ .append(second)
+ .append("|")
+ .append(thirdProperty);
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/InvalidImplementsInterfacePropertyModel.java b/src/main/java/org/apache/sling/models/it/models/implextend/InvalidImplementsInterfacePropertyModel.java
new file mode 100644
index 0000000..87235f7
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/InvalidImplementsInterfacePropertyModel.java
@@ -0,0 +1,53 @@
+/*
+ * 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.it.models.implextend;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.Optional;
+
+/**
+ * This model defines an invalid adapters property containing an interface it does not implement.
+ */
+@Model(adaptables = Resource.class, adapters={InvalidSampleServiceInterface.class})
+public class InvalidImplementsInterfacePropertyModel implements SampleServiceInterface {
+
+ @Inject
+ private String first;
+
+ @Inject
+ @Optional
+ private String second;
+
+ @Inject
+ @Named("third")
+ private String thirdProperty;
+
+ public String getAllProperties() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(first)
+ .append("|")
+ .append(second)
+ .append("|")
+ .append(thirdProperty);
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/InvalidSampleServiceInterface.java b/src/main/java/org/apache/sling/models/it/models/implextend/InvalidSampleServiceInterface.java
new file mode 100644
index 0000000..a10e1a2
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/InvalidSampleServiceInterface.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.it.models.implextend;
+
+/**
+ * Example "service" interface to which sling models can adapt.
+ */
+public interface InvalidSampleServiceInterface {
+
+ /**
+ * @return concanated string with all properties
+ */
+ String getAllProperties();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/SampleServiceInterface.java b/src/main/java/org/apache/sling/models/it/models/implextend/SampleServiceInterface.java
new file mode 100644
index 0000000..f72980c
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/SampleServiceInterface.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.it.models.implextend;
+
+/**
+ * Example "service" interface to which sling models can adapt.
+ */
+public interface SampleServiceInterface {
+
+ /**
+ * @return concatenated string with all properties
+ */
+ String getAllProperties();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/models/it/models/implextend/SimplePropertyModel.java b/src/main/java/org/apache/sling/models/it/models/implextend/SimplePropertyModel.java
new file mode 100644
index 0000000..f81e362
--- /dev/null
+++ b/src/main/java/org/apache/sling/models/it/models/implextend/SimplePropertyModel.java
@@ -0,0 +1,50 @@
+/*
+ * 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.it.models.implextend;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.sling.models.annotations.Optional;
+
+/**
+ * Base class without @Model annotation.
+ */
+public class SimplePropertyModel {
+
+ @Inject
+ private String first;
+
+ @Inject
+ @Optional
+ private String second;
+
+ @Inject
+ @Named("third")
+ private String thirdProperty;
+
+ public String getAllProperties() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(first)
+ .append("|")
+ .append(second)
+ .append("|")
+ .append(thirdProperty);
+ return sb.toString();
+ }
+
+}
\ No newline at end of file