You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wink.apache.org by bl...@apache.org on 2010/02/11 00:53:54 UTC

svn commit: r908763 - in /incubator/wink/trunk: wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ wink-common/src/main/resources/org/apache/wink/common/internal/i18n/ wink-common/src/test/java/org/apache/wink/common/internal/...

Author: bluk
Date: Wed Feb 10 23:53:23 2010
New Revision: 908763

URL: http://svn.apache.org/viewvc?rev=908763&view=rev
Log:
Add support for inheritable annotations

For @Provider and @Path, allow them to be
on superclasses/interfaces.

Issue a warning for compatibility in
other environments.

See [WINK-245]

Thanks to Davanum Srinivas for reporting
the issue.

Added:
    incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java   (with props)
Modified:
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ProviderMetadataCollector.java
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollector.java
    incubator/wink/trunk/wink-common/src/main/resources/org/apache/wink/common/internal/i18n/resource.properties
    incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollectorTest.java

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ProviderMetadataCollector.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ProviderMetadataCollector.java?rev=908763&r1=908762&r2=908763&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ProviderMetadataCollector.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ProviderMetadataCollector.java Wed Feb 10 23:53:23 2010
@@ -26,18 +26,60 @@
 import javax.ws.rs.core.Context;
 import javax.ws.rs.ext.Provider;
 
+import org.apache.wink.common.internal.i18n.Messages;
 import org.apache.wink.common.internal.registry.Injectable;
 import org.apache.wink.common.internal.registry.InjectableFactory;
 import org.apache.wink.common.internal.utils.GenericsUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ProviderMetadataCollector extends AbstractMetadataCollector {
 
+    private static final Logger logger = LoggerFactory.getLogger(ProviderMetadataCollector.class);
+
     private ProviderMetadataCollector(Class<?> clazz) {
         super(clazz);
     }
 
     public static boolean isProvider(Class<?> cls) {
-        return cls.getAnnotation(Provider.class) != null;
+        /*
+         * look for the Provider annotation on super classes (even though
+         * @Provider does not have @java.lang.annotation.Inherited) in order to
+         * promote better compatibility with expected behavior
+         */
+        // return cls.getAnnotation(Provider.class) != null;
+        if (cls == Object.class) {
+            return false;
+        }
+
+        if (cls.getAnnotation(Provider.class) != null) {
+            return true;
+        }
+
+        Class<?> declaringClass = cls;
+
+        while (!declaringClass.equals(Object.class)) {
+            // try a superclass
+            Class<?> superclass = declaringClass.getSuperclass();
+            if (superclass.getAnnotation(Provider.class) != null) {
+                // issue warning
+                logger.warn(Messages.getMessage("providerShouldBeAnnotatedDirectly", cls));
+                return true;
+            }
+
+            // try interfaces
+            Class<?>[] interfaces = declaringClass.getInterfaces();
+            for (Class<?> interfaceClass : interfaces) {
+                if (interfaceClass.getAnnotation(Provider.class) != null) {
+                    // issue warning
+                    logger.warn(Messages.getMessage("providerShouldBeAnnotatedDirectly", cls));
+                    return true;
+                }
+            }
+            declaringClass = declaringClass.getSuperclass();
+        }
+
+        return false;
     }
 
     public static ClassMetadata collectMetadata(Class<?> clazz) {

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollector.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollector.java?rev=908763&r1=908762&r2=908763&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollector.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollector.java Wed Feb 10 23:53:23 2010
@@ -62,7 +62,33 @@
     }
 
     public static boolean isStaticResource(Class<?> cls) {
-        return cls.getAnnotation(Path.class) != null;
+        if (cls.getAnnotation(Path.class) != null) {
+            return true;
+        }
+
+        Class<?> declaringClass = cls;
+
+        while (!declaringClass.equals(Object.class)) {
+            // try a superclass
+            Class<?> superclass = declaringClass.getSuperclass();
+            if (superclass.getAnnotation(Path.class) != null) {
+                // issue warning
+                logger.warn(Messages.getMessage("rootResourceShouldBeAnnotatedDirectly", cls));
+                return true;
+            }
+
+            // try interfaces
+            Class<?>[] interfaces = declaringClass.getInterfaces();
+            for (Class<?> interfaceClass : interfaces) {
+                if (interfaceClass.getAnnotation(Path.class) != null) {
+                    // issue warning
+                    logger.warn(Messages.getMessage("rootResourceShouldBeAnnotatedDirectly", cls));
+                    return true;
+                }
+            }
+            declaringClass = declaringClass.getSuperclass();
+        }
+        return false;
     }
 
     public static boolean isDynamicResource(Class<?> cls) {
@@ -137,6 +163,30 @@
             getMetadata().addPath(path.value());
             return true;
         }
+
+        Class<?> declaringClass = cls;
+
+        while (!declaringClass.equals(Object.class)) {
+            // try a superclass
+            Class<?> superclass = declaringClass.getSuperclass();
+            path = superclass.getAnnotation(Path.class);
+            if (path != null) {
+                getMetadata().addPath(path.value());
+                return true;
+            }
+
+            // try interfaces
+            Class<?>[] interfaces = declaringClass.getInterfaces();
+            for (Class<?> interfaceClass : interfaces) {
+                path = interfaceClass.getAnnotation(Path.class);
+                if (path != null) {
+                    getMetadata().addPath(path.value());
+                    return true;
+                }
+            }
+            declaringClass = declaringClass.getSuperclass();
+        }
+
         return false;
     }
 
@@ -164,7 +214,8 @@
                         for (Injectable id : methodMetadata.getFormalParameters()) {
                             if (id.getParamType() == Injectable.ParamType.ENTITY) {
                                 logger
-                                    .warn(Messages.getMessage("subresourceLocatorIllegalEntityParameter"),
+                                    .warn(Messages
+                                              .getMessage("subresourceLocatorIllegalEntityParameter"),
                                           methodName);
                                 continue F1;
                             }
@@ -174,7 +225,8 @@
                         if (!methodMetadata.getConsumes().isEmpty() || !methodMetadata
                             .getProduces().isEmpty()) {
                             logger
-                                .warn(Messages.getMessage("subresourceLocatorAnnotatedConsumesProduces"),
+                                .warn(Messages
+                                          .getMessage("subresourceLocatorAnnotatedConsumesProduces"),
                                       methodName);
                         }
                         getMetadata().getSubResourceLocators().add(methodMetadata);
@@ -271,15 +323,14 @@
         // method/sub-resource locator,
         // since there is at least one JAX-RS annotation on the method
         if (metadata.getHttpMethod() == null && metadata.getPath() == null) {
-            if(metadata.isEncoded() || defaultValue != null) {
+            if (metadata.isEncoded() || defaultValue != null) {
                 // property methods may have @Encoded or @DefaultValue but
                 // are not HTTP methods/paths
                 return null;
             }
-            logger
-                .warn(Messages.getMessage("methodNotAnnotatedCorrectly"),
-                      method.getName(),
-                      method.getDeclaringClass().getCanonicalName());
+            logger.warn(Messages.getMessage("methodNotAnnotatedCorrectly"),
+                        method.getName(),
+                        method.getDeclaringClass().getCanonicalName());
             return null;
         }
 

Modified: incubator/wink/trunk/wink-common/src/main/resources/org/apache/wink/common/internal/i18n/resource.properties
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/resources/org/apache/wink/common/internal/i18n/resource.properties?rev=908763&r1=908762&r2=908763&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/resources/org/apache/wink/common/internal/i18n/resource.properties (original)
+++ incubator/wink/trunk/wink-common/src/main/resources/org/apache/wink/common/internal/i18n/resource.properties Wed Feb 10 23:53:23 2010
@@ -130,4 +130,8 @@
 restServletWinkApplicationInitParam=Using application classes {} named in init-param {}
 restServletUseDeploymentConfigurationParam=Using deployment configuration class {} named in init-param {}
 restServletUsePropertiesFileAtLocation=Using properties file at {} named in init-param {}
-adminServletRequestProcessorInitBeforeAdmin=Request processor should be initialized prior calling to admin servlet.
\ No newline at end of file
+adminServletRequestProcessorInitBeforeAdmin=Request processor should be initialized prior calling to admin servlet.
+
+rootResourceShouldBeAnnotatedDirectly=The root resource class {0} should be annotated directly.  In environments where annotation scanning is done automatically without an Application sub-class, the class may not be detected as a root resource.
+providerShouldBeAnnotatedDirectly=The provider class {0} should be annotated directly.  In environments where annotation scanning is done automatically without an Application sub-class, the class may not be detected as a provider.
+

Modified: incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollectorTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollectorTest.java?rev=908763&r1=908762&r2=908763&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollectorTest.java (original)
+++ incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/registry/metadata/ResourceMetadataCollectorTest.java Wed Feb 10 23:53:23 2010
@@ -22,7 +22,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
-import java.util.Map.Entry;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -32,9 +31,6 @@
 
 import junit.framework.TestCase;
 
-import org.apache.wink.common.internal.registry.metadata.ClassMetadata;
-import org.apache.wink.common.internal.registry.metadata.ResourceMetadataCollector;
-
 public class ResourceMetadataCollectorTest extends TestCase {
 
     @Path("/myresource")
@@ -50,6 +46,56 @@
         }
     }
 
+    @Path("superclassvalue")
+    public static class SuperResource {
+
+    }
+
+    public static class MySubclassResource extends SuperResource {
+
+    }
+
+    @Path("interfacevalue")
+    public static interface MyInterface {
+
+    }
+
+    public static class MyInterfaceImpl implements MyInterface {
+
+    }
+
+    public static class MySuperInterfaceImpl extends SuperResource implements MyInterface {
+
+    }
+
+    /**
+     * Tests that @Path is inheritable. This may not follow the JAX-RS
+     * specification so a warning will be issued.
+     * 
+     * @throws Exception
+     */
+    public void testPathInheritance() throws Exception {
+        ClassMetadata classMetadata =
+            ResourceMetadataCollector.collectMetadata(MySubclassResource.class);
+        assertTrue(ResourceMetadataCollector.isResource(MySubclassResource.class));
+        assertFalse(ResourceMetadataCollector.isDynamicResource(MySubclassResource.class));
+        assertTrue(ResourceMetadataCollector.isStaticResource(MySubclassResource.class));
+        assertEquals("superclassvalue", classMetadata.getPath());
+
+        classMetadata = ResourceMetadataCollector.collectMetadata(MyInterfaceImpl.class);
+        assertTrue(ResourceMetadataCollector.isResource(MyInterfaceImpl.class));
+        assertFalse(ResourceMetadataCollector.isDynamicResource(MyInterfaceImpl.class));
+        assertTrue(ResourceMetadataCollector.isStaticResource(MyInterfaceImpl.class));
+        assertEquals("interfacevalue", classMetadata.getPath());
+
+        // superclass will take precedence over interface
+        classMetadata = ResourceMetadataCollector.collectMetadata(MySuperInterfaceImpl.class);
+        assertTrue(ResourceMetadataCollector.isResource(MySuperInterfaceImpl.class));
+        assertFalse(ResourceMetadataCollector.isDynamicResource(MySuperInterfaceImpl.class));
+        assertTrue(ResourceMetadataCollector.isStaticResource(MySuperInterfaceImpl.class));
+        assertEquals("superclassvalue", classMetadata.getPath());
+    }
+
     /**
      * JAX-RS 1.1 allows syntax such as:
      * 
@@ -69,7 +115,7 @@
         HashSet<MediaType> expected = new HashSet<MediaType>(3);
         expected.add(new MediaType("abcd", "efg"));
         expected.add(new MediaType("hijk", "lmn")); // make sure whitespace is
-                                                    // ignored
+        // ignored
         expected.add(new MediaType("opqr", "stu"));
 
         assertEquals(expected, values);

Added: incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java?rev=908763&view=auto
==============================================================================
--- incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java (added)
+++ incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java Wed Feb 10 23:53:23 2010
@@ -0,0 +1,118 @@
+/*
+ * 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.wink.server.internal.providers.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.wink.common.utils.ProviderUtils;
+import org.apache.wink.server.internal.servlet.MockServletInvocationTest;
+import org.apache.wink.test.mock.MockRequestConstructor;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+public class InheritedProviderAnnTest extends MockServletInvocationTest {
+
+    public static interface MyMessageBodyWriterInterface extends MessageBodyWriter<String> {
+
+    }
+
+    @Override
+    protected Class<?>[] getClasses() {
+        return new Class<?>[] {TestResource.class, TestProvider.class};
+    }
+
+    @Provider
+    @Produces("abcd/efgh")
+    public static class TestParentProvider implements MyMessageBodyWriterInterface {
+
+        public long getSize(String arg0, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType arg4) {
+            return -1;
+        }
+
+        public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) {
+            return String.class.equals(arg0);
+        }
+
+        public void writeTo(String arg0,
+                            Class<?> arg1,
+                            Type arg2,
+                            Annotation[] arg3,
+                            MediaType arg4,
+                            MultivaluedMap<String, Object> arg5,
+                            OutputStream arg6) throws IOException, WebApplicationException {
+            arg6.write("parent".getBytes(ProviderUtils.getCharset(arg4)));
+        }
+    }
+
+    public static class TestProvider extends TestParentProvider {
+
+        public void writeTo(String arg0,
+                            Class<?> arg1,
+                            Type arg2,
+                            Annotation[] arg3,
+                            MediaType arg4,
+                            MultivaluedMap<String, Object> arg5,
+                            OutputStream arg6) throws IOException, WebApplicationException {
+            arg6.write("child".getBytes(ProviderUtils.getCharset(arg4)));
+        }
+    }
+
+    @Path("/string")
+    public static class TestResource {
+
+        @GET
+        public String getForm() {
+            return "hello";
+        }
+    }
+
+    /**
+     * Tests that a {@link Provider} can be inherited from the superclass. While
+     * this is not required by the specification, it is in order to promote
+     * better compatibility with providers (i.e. Jackson) that expected this
+     * behavior.
+     * 
+     * @throws Exception
+     */
+    public void testInheritedProvider() throws Exception {
+        MockHttpServletRequest request =
+            MockRequestConstructor.constructMockRequest("GET", "/string", MediaType.TEXT_PLAIN);
+        MockHttpServletResponse response = invoke(request);
+        assertEquals(200, response.getStatus());
+        assertEquals("hello", response.getContentAsString());
+
+        request = MockRequestConstructor.constructMockRequest("GET", "/string", "abcd/efgh");
+        response = invoke(request);
+        assertEquals(200, response.getStatus());
+        assertEquals("child", response.getContentAsString());
+    }
+}

Propchange: incubator/wink/trunk/wink-server/src/test/java/org/apache/wink/server/internal/providers/entity/InheritedProviderAnnTest.java
------------------------------------------------------------------------------
    svn:eol-style = native