You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by cs...@apache.org on 2018/01/29 11:34:27 UTC

[2/3] aries-jax-rs-whiteboard git commit: Basic RuntimeMethodInfoDTO support for endpoints

Basic RuntimeMethodInfoDTO support for endpoints


Project: http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/repo
Commit: http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/commit/efcc0439
Tree: http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/tree/efcc0439
Diff: http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/diff/efcc0439

Branch: refs/heads/master
Commit: efcc0439668139f9c1d9f5e29c2c9d6fc29098e7
Parents: 427df5d
Author: Carlos Sierra <cs...@apache.org>
Authored: Wed Jan 17 10:53:18 2018 +0100
Committer: Carlos Sierra <cs...@apache.org>
Committed: Mon Jan 29 09:34:51 2018 +0100

----------------------------------------------------------------------
 jax-rs.itests/src/main/java/test/JaxrsTest.java |  35 +++
 jax-rs.whiteboard/pom.xml                       |  10 +-
 .../internal/AriesJaxRSServiceRuntime.java      | 101 +++++--
 .../internal/CXFJaxRsServiceRegistrator.java    |   4 +
 .../aries/jax/rs/whiteboard/internal/Utils.java |   2 +-
 .../jax/rs/whiteboard/internal/Whiteboard.java  |  27 +-
 .../introspection/ClassIntrospector.java        | 205 +++++++++++++
 .../introspection/ClassIntrospectorTest.java    | 287 +++++++++++++++++++
 .../src/test/java/test/types/Bound.java         |  14 +
 .../src/test/java/test/types/PlainResource.java |  29 ++
 .../types/PlainResourceSeveralOperations.java   |  35 +++
 ...lainResourceSeveralOperationsCommonPath.java |  37 +++
 ...nResourceSeveralOperationsDifferentPath.java |  38 +++
 ...esourceSeveralOperationsWithNameBinding.java |  36 +++
 .../types/ResourceMethodInfoDTOWrapper.java     |  57 ++++
 .../test/types/ResourceWithSubResource.java     |  39 +++
 .../src/test/java/test/types/SubResource.java   |  50 ++++
 17 files changed, 970 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.itests/src/main/java/test/JaxrsTest.java
----------------------------------------------------------------------
diff --git a/jax-rs.itests/src/main/java/test/JaxrsTest.java b/jax-rs.itests/src/main/java/test/JaxrsTest.java
index 360d76c..2248356 100644
--- a/jax-rs.itests/src/main/java/test/JaxrsTest.java
+++ b/jax-rs.itests/src/main/java/test/JaxrsTest.java
@@ -17,6 +17,7 @@
 
 package test;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertTrue;
 import static org.osgi.service.jaxrs.whiteboard.JaxRSWhiteboardConstants.*;
 
@@ -47,8 +48,11 @@ import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceRegistration;
 
 import org.osgi.service.jaxrs.runtime.JaxRSServiceRuntime;
+import org.osgi.service.jaxrs.runtime.dto.ApplicationDTO;
 import org.osgi.service.jaxrs.runtime.dto.DTOConstants;
 import org.osgi.service.jaxrs.runtime.dto.FailedApplicationDTO;
+import org.osgi.service.jaxrs.runtime.dto.ResourceDTO;
+import org.osgi.service.jaxrs.runtime.dto.ResourceMethodInfoDTO;
 import org.osgi.service.jaxrs.runtime.dto.RuntimeDTO;
 import org.osgi.util.tracker.ServiceTracker;
 import test.types.ConfigurationAwareResource;
@@ -65,11 +69,13 @@ import test.types.TestFilterAndExceptionMapper;
 import test.types.TestHelper;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
 import javax.ws.rs.client.InvocationCallback;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.container.ContainerResponseFilter;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.ExceptionMapper;
 
@@ -190,6 +196,35 @@ public class JaxrsTest extends TestHelper {
     }
 
     @Test
+    public void testApplicationEndpointExtensionRuntimeDTO() {
+        registerApplication(new TestApplication());
+
+        registerAddon(
+            new TestAddon(), JAX_RS_APPLICATION_SELECT,
+            "(" + JAX_RS_APPLICATION_BASE + "=/test-application)");
+
+        RuntimeDTO runtimeDTO = _runtime.getRuntimeDTO();
+
+        assertEquals(1, runtimeDTO.applicationDTOs.length);
+
+        ApplicationDTO applicationDTO = runtimeDTO.applicationDTOs[0];
+        assertEquals(applicationDTO.base, "/test-application");
+        assertEquals(1, applicationDTO.resourceDTOs.length);
+
+        ResourceDTO resourceDTO = applicationDTO.resourceDTOs[0];
+        assertEquals(1, resourceDTO.resourceMethods.length);
+
+        ResourceMethodInfoDTO resourceMethod = resourceDTO.resourceMethods[0];
+        assertEquals(HttpMethod.GET, resourceMethod.method);
+        assertEquals("/{name}", resourceMethod.path);
+        assertArrayEquals(
+            new String[]{MediaType.WILDCARD}, resourceMethod.consumingMimeType);
+        assertArrayEquals(
+            new String[]{MediaType.WILDCARD}, resourceMethod.producingMimeType);
+        assertArrayEquals(new String[]{}, resourceMethod.nameBindings);
+    }
+
+    @Test
     public void testApplicationEndpointExtensionReadd()
         throws InterruptedException {
 

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/pom.xml
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/pom.xml b/jax-rs.whiteboard/pom.xml
index b6e5d51..3b807a8 100644
--- a/jax-rs.whiteboard/pom.xml
+++ b/jax-rs.whiteboard/pom.xml
@@ -139,8 +139,14 @@
             <artifactId>javax.json-api</artifactId>
             <version>1.0</version>
             <scope>runtime</scope>
-        </dependency> 
-    </dependencies>
+        </dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
 
     <build>
         <pluginManagement>

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/AriesJaxRSServiceRuntime.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/AriesJaxRSServiceRuntime.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/AriesJaxRSServiceRuntime.java
index 899756d..a3e21d0 100644
--- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/AriesJaxRSServiceRuntime.java
+++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/AriesJaxRSServiceRuntime.java
@@ -37,7 +37,9 @@ import java.util.stream.Stream;
 import javax.ws.rs.core.Application;
 
 import org.apache.aries.jax.rs.whiteboard.internal.Utils.PropertyHolder;
+import org.apache.aries.jax.rs.whiteboard.internal.introspection.ClassIntrospector;
 import org.apache.aries.osgi.functional.CachingServiceReference;
+import org.apache.cxf.Bus;
 import org.osgi.service.jaxrs.runtime.JaxRSServiceRuntime;
 import org.osgi.service.jaxrs.runtime.dto.ApplicationDTO;
 import org.osgi.service.jaxrs.runtime.dto.BaseDTO;
@@ -82,10 +84,15 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
     }
 
     public void addApplicationEndpoint(
-        String applicationName, CachingServiceReference<?> endpointImmutableServiceReference) {
+        String applicationName,
+        CachingServiceReference<?> endpointImmutableServiceReference,
+        Bus bus, Class<?> theClass) {
 
         _applicationEndpoints.compute(
-            applicationName, merger(endpointImmutableServiceReference));
+            applicationName,
+            merger(
+                new EndpointRuntimeInformation(
+                    endpointImmutableServiceReference, bus, theClass)));
     }
 
     public void addApplicationExtension(
@@ -249,10 +256,13 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
     }
 
     public void removeApplicationEndpoint(
-        String applicationName, CachingServiceReference<?> endpointImmutableServiceReference) {
+        String applicationName,
+        CachingServiceReference<?> cachingServiceReference) {
 
         _applicationEndpoints.compute(
-            applicationName, remover(endpointImmutableServiceReference));
+            applicationName,
+            remover(new EndpointRuntimeInformation(
+                cachingServiceReference, null, null)));
     }
 
     public void removeApplicationExtension(
@@ -347,7 +357,7 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
     }
     private ConcurrentHashMap<String, Map<String, Object>>
         _applications = new ConcurrentHashMap<>();
-    private ConcurrentHashMap<String, Collection<CachingServiceReference<?>>>
+    private ConcurrentHashMap<String, Collection<EndpointRuntimeInformation>>
         _applicationEndpoints = new ConcurrentHashMap<>();
     private ConcurrentHashMap<String, Collection<CachingServiceReference<?>>>
         _applicationExtensions = new ConcurrentHashMap<>();
@@ -414,12 +424,22 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
         };
     }
 
-    private static <T extends BaseExtensionDTO> T populateExtensionDTO(
-    		T extensionDTO, CachingServiceReference<?> serviceReference) {
+    private static <T extends BaseDTO> T populateBaseDTO(
+    		T baseDTO, CachingServiceReference<?> serviceReference) {
 
-        extensionDTO.name = getApplicationName(serviceReference::getProperty);
-        extensionDTO.serviceId = (Long)serviceReference.getProperty(
+        baseDTO.name = getApplicationName(serviceReference::getProperty);
+        baseDTO.serviceId = (Long)serviceReference.getProperty(
             "service.id");
+        
+        return baseDTO;
+    }
+
+    private static void populateBaseExtensionDTO(
+        BaseExtensionDTO extensionDTO,
+        CachingServiceReference<?> serviceReference) {
+
+        populateBaseDTO(extensionDTO, serviceReference);
+
         extensionDTO.extensionTypes =
             Arrays.stream(
                 canonicalize(serviceReference.getProperty("objectClass"))).
@@ -427,16 +447,27 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
                 SUPPORTED_EXTENSION_INTERFACES::contains
             ).
             toArray(String[]::new);
+    }
+
+    private static ExtensionDTO populateExtensionDTO(
+        ExtensionDTO extensionDTO,
+        CachingServiceReference<?> serviceReference) {
+
+        populateBaseExtensionDTO(extensionDTO, serviceReference);
 
         return extensionDTO;
     }
 
-    private static <T extends BaseDTO> T populateResourceDTO(
-    		T resourceDTO, CachingServiceReference<?> serviceReference) {
+    private static ResourceDTO populateResourceDTO(
+        ResourceDTO resourceDTO,
+        EndpointRuntimeInformation endpointRuntimeInformation) {
 
-        resourceDTO.name = getApplicationName(serviceReference::getProperty);
-        resourceDTO.serviceId = (Long)serviceReference.getProperty(
-            "service.id");
+        populateBaseDTO(
+            resourceDTO, endpointRuntimeInformation._cachingServiceReference);
+
+        resourceDTO.resourceMethods = ClassIntrospector.getResourceMethodInfos(
+            endpointRuntimeInformation._class,
+            endpointRuntimeInformation._bus);
 
         return resourceDTO;
     }
@@ -506,7 +537,7 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
 
         FailedExtensionDTO failedExtensionDTO = new FailedExtensionDTO();
 
-        populateExtensionDTO(failedExtensionDTO, serviceReference);
+        populateBaseExtensionDTO(failedExtensionDTO, serviceReference);
 
         failedExtensionDTO.failureReason = reason;
 
@@ -518,7 +549,7 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
 
         FailedResourceDTO failedResourceDTO = new FailedResourceDTO();
 
-        populateResourceDTO(failedResourceDTO, serviceReference);
+        populateBaseDTO(failedResourceDTO, serviceReference);
 
         failedResourceDTO.failureReason = reason;
 
@@ -575,12 +606,12 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
     }
 
     private Stream<ResourceDTO> getApplicationEndpointsStream(String name) {
-        Collection<CachingServiceReference<?>> applicationEndpoints =
+        Collection<EndpointRuntimeInformation> endpointRuntimeInformations =
             _applicationEndpoints.get(name);
 
-        Stream<CachingServiceReference<?>> applicationEndpointStream =
-            applicationEndpoints != null ?
-                applicationEndpoints.stream() :
+        Stream<EndpointRuntimeInformation> applicationEndpointStream =
+            endpointRuntimeInformations != null ?
+                endpointRuntimeInformations.stream() :
                 Stream.empty();
 
         return
@@ -639,4 +670,34 @@ public class AriesJaxRSServiceRuntime implements JaxRSServiceRuntime {
         );
     }
 
+    private static class EndpointRuntimeInformation {
+        public EndpointRuntimeInformation(
+            CachingServiceReference cachingServiceReference, Bus bus,
+            Class<?> aClass) {
+
+            _cachingServiceReference = cachingServiceReference;
+            _bus = bus;
+            _class = aClass;
+        }
+
+        @Override
+        public int hashCode() {
+            return _cachingServiceReference.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            EndpointRuntimeInformation that = (EndpointRuntimeInformation) o;
+
+            return _cachingServiceReference.equals(
+                that._cachingServiceReference);
+        }
+        CachingServiceReference _cachingServiceReference;
+        Bus _bus;
+        Class<?> _class;
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/CXFJaxRsServiceRegistrator.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/CXFJaxRsServiceRegistrator.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/CXFJaxRsServiceRegistrator.java
index 79fc2da..2adf92b 100644
--- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/CXFJaxRsServiceRegistrator.java
+++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/CXFJaxRsServiceRegistrator.java
@@ -68,6 +68,10 @@ public class CXFJaxRsServiceRegistrator {
         rewire();
     }
 
+    public Bus getBus() {
+        return _bus;
+    }
+
     public void add(ResourceProvider resourceProvider) {
         if (_closed) {
             return;

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Utils.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Utils.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Utils.java
index 60726a7..c04a6e4 100644
--- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Utils.java
+++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Utils.java
@@ -101,7 +101,7 @@ public class Utils {
         }
     }
 
-    public static <T> ResourceProvider getResourceProvider(
+    public static <T> ServiceReferenceResourceProvider getResourceProvider(
         ServiceObjects<T> serviceObjects) {
 
         CachingServiceReference<T> serviceReference =

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Whiteboard.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Whiteboard.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Whiteboard.java
index 256fa4b..1b3ad2c 100644
--- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Whiteboard.java
+++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/Whiteboard.java
@@ -431,19 +431,20 @@ public class Whiteboard {
                     _runtime::addErroredEndpoint,
                     _runtime::removeErroredEndpoint).
                 then(nothing())
-            ).map(
-                ServiceTuple::getServiceObjects
-            ).map(
-                Utils::getResourceProvider
-            ).effects(
-                registrator::add,
-                registrator::remove
-            ).effects(
-                __ -> _runtime.addApplicationEndpoint(
-                    applicationName, serviceReference),
-                __ -> _runtime.removeApplicationEndpoint(
-                    applicationName, serviceReference)
-            ));
+            ).flatMap(st ->
+                just(st.getServiceObjects()).
+                    map(
+                        Utils::getResourceProvider
+                ).effects(
+                    rp -> _runtime.addApplicationEndpoint(
+                        applicationName, st.getCachingServiceReference(),
+                        registrator.getBus(), st.getService().getClass()),
+                    rp -> _runtime.removeApplicationEndpoint(
+                        applicationName, st.getCachingServiceReference())
+                ).effects(
+                    registrator::add,
+                    registrator::remove
+            )));
     }
 
     private OSGi<?> safeRegisterExtension(

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospector.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospector.java
new file mode 100644
index 0000000..6a3ff7f
--- /dev/null
+++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospector.java
@@ -0,0 +1,205 @@
+/*
+ * 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.aries.jax.rs.whiteboard.internal.introspection;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.MethodDispatcher;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.osgi.service.jaxrs.runtime.dto.ResourceMethodInfoDTO;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ClassIntrospector {
+
+    private static final List<MediaType> _ALL_TYPES_LIST =
+        Collections.singletonList(JAXRSUtils.ALL_TYPES);
+
+    public static ResourceMethodInfoDTO[] getResourceMethodInfos(
+        Class<?> clazz, Bus bus) {
+
+        ClassResourceInfo classResourceInfo =
+            ResourceUtils.createClassResourceInfo(
+                clazz, clazz, true, true, bus);
+
+        Stream<ResourceMethodInfoDTO> convert = convert(
+            new HashSet<>(), "/", null, _ALL_TYPES_LIST, _ALL_TYPES_LIST,
+            Collections.emptySet(), true, classResourceInfo);
+
+        return convert.toArray(ResourceMethodInfoDTO[]::new);
+    }
+
+    private static Stream<ResourceMethodInfoDTO> convert(
+        Set<ClassResourceInfo> visited,
+        String parentPath, String defaultHttpMethod,
+        List<MediaType> defaultConsumeTypes,
+        List<MediaType> defaultProduceTypes,
+        Set<String> defaultNameBindings, boolean recurse,
+        ClassResourceInfo classResourceInfo) {
+
+        visited.add(classResourceInfo);
+
+        String path = parentPath + getPathSafe(classResourceInfo);
+
+        List<MediaType> consumeMime = classResourceInfo.getConsumeMime();
+        if (consumeMime.equals(_ALL_TYPES_LIST)) {
+            consumeMime = defaultConsumeTypes;
+        }
+
+        List<MediaType> produceMime = classResourceInfo.getProduceMime();
+        if (consumeMime.equals(_ALL_TYPES_LIST)) {
+            produceMime = defaultProduceTypes;
+        }
+
+        Set<String> nameBindings = classResourceInfo.getNameBindings();
+        if (nameBindings.isEmpty()) {
+            nameBindings = defaultNameBindings;
+        }
+
+        MethodDispatcher methodDispatcher =
+            classResourceInfo.getMethodDispatcher();
+
+        Set<OperationResourceInfo> operationResourceInfos =
+            methodDispatcher.getOperationResourceInfos();
+
+        ArrayList<MediaType> consumeParam = new ArrayList<>(consumeMime);
+        ArrayList<MediaType> produceParam = new ArrayList<>(produceMime);
+        HashSet<String> nameBindingsParam = new HashSet<>(nameBindings);
+
+        Stream<ResourceMethodInfoDTO> stream =
+            operationResourceInfos.stream().
+            flatMap(
+                ori -> convert(
+                    visited, path, defaultHttpMethod, consumeParam,
+                    produceParam, nameBindingsParam, recurse, ori)
+            ).collect(
+                Collectors.toList()
+            ).stream();
+
+        visited.remove(classResourceInfo);
+
+        return stream;
+    }
+
+    private static Stream<ResourceMethodInfoDTO> convert(
+        Set<ClassResourceInfo> visited,
+        String parentPath, String defaultHttpMethod,
+        List<MediaType> defaultConsumeTypes,
+        List<MediaType> defaultProduceTypes,
+        Set<String> defaultNameBindings, boolean recurse,
+        OperationResourceInfo operationResourceInfo) {
+
+        List<MediaType> consumeTypes = operationResourceInfo.getConsumeTypes();
+        if (consumeTypes == null) {
+            consumeTypes = defaultConsumeTypes;
+        }
+
+        List<MediaType> produceTypes = operationResourceInfo.getProduceTypes();
+        if (produceTypes == null) {
+            produceTypes = defaultProduceTypes;
+        }
+
+        String httpMethod = operationResourceInfo.getHttpMethod();
+
+        if (httpMethod == null) {
+            httpMethod = defaultHttpMethod;
+        }
+
+        Set<String> nameBindings = operationResourceInfo.getNameBindings();
+        if (nameBindings.isEmpty()) {
+            nameBindings = defaultNameBindings;
+        }
+
+        String path = parentPath +
+            operationResourceInfo.getURITemplate().getValue();
+
+        if (operationResourceInfo.isSubResourceLocator()) {
+            ClassResourceInfo classResourceInfo =
+                operationResourceInfo.getClassResourceInfo();
+
+            Class<?> returnType =
+                operationResourceInfo.getAnnotatedMethod().getReturnType();
+
+            ClassResourceInfo subResource = classResourceInfo.getSubResource(
+                returnType, returnType);
+
+            if (subResource != null) {
+                if (recurse) {
+                    return convert(visited,
+                        path, httpMethod, consumeTypes, produceTypes,
+                        nameBindings, !visited.contains(subResource),
+                        subResource);
+                }
+                else {
+                    return Stream.empty();
+                }
+            }
+        }
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.consumingMimeType = consumeTypes.stream().
+            map(
+                MediaType::toString
+            ).toArray(
+                String[]::new
+            );
+
+        resourceMethodInfoDTO.producingMimeType = produceTypes.stream().
+            map(
+                MediaType::toString
+            ).toArray(
+                String[]::new
+            );
+
+        resourceMethodInfoDTO.nameBindings = nameBindings.toArray(
+            new String[0]);
+
+        try {
+            resourceMethodInfoDTO.path = Paths.get(path).normalize().toString();
+        }
+        catch (Exception e) {
+            resourceMethodInfoDTO.path = "/";
+        }
+        resourceMethodInfoDTO.method = httpMethod;
+
+        return Stream.of(resourceMethodInfoDTO);
+    }
+
+    private static String getPathSafe(ClassResourceInfo classResourceInfo) {
+        Path path = classResourceInfo.getPath();
+        if (path == null) {
+            return "/";
+        }
+
+        return path.value();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospectorTest.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospectorTest.java b/jax-rs.whiteboard/src/test/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospectorTest.java
new file mode 100644
index 0000000..de35ab6
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/ClassIntrospectorTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.aries.jax.rs.whiteboard.internal.introspection;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.junit.Test;
+import org.osgi.service.jaxrs.runtime.dto.ResourceMethodInfoDTO;
+import test.types.PlainResource;
+import test.types.PlainResourceSeveralOperations;
+import test.types.PlainResourceSeveralOperationsCommonPath;
+import test.types.PlainResourceSeveralOperationsDifferentPath;
+import test.types.PlainResourceSeveralOperationsWithNameBinding;
+import test.types.ResourceMethodInfoDTOWrapper;
+import test.types.ResourceWithSubResource;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ClassIntrospectorTest {
+
+    private static final String[] ALL_TYPES = {MediaType.WILDCARD};
+
+    @Test
+    public void testPlainResource() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(PlainResource.class, bus);
+
+        assertEquals(1, resourceMethodInfoDTOS.length);
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            resourceMethodInfoDTOS[0];
+
+        assertEquals(HttpMethod.GET, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+    }
+
+    @Test
+    public void testPlainResourceWithNameBinding() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(
+                PlainResourceSeveralOperationsWithNameBinding.class, bus);
+
+        assertEquals(2, resourceMethodInfoDTOS.length);
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            resourceMethodInfoDTOS[0];
+
+        assertEquals(HttpMethod.GET, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/", resourceMethodInfoDTO.path);
+        assertArrayEquals(
+            new String[]{"test.types.Bound"},
+            resourceMethodInfoDTO.nameBindings);
+
+        resourceMethodInfoDTO = resourceMethodInfoDTOS[1];
+
+        assertEquals(HttpMethod.POST, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/", resourceMethodInfoDTO.path);
+        assertArrayEquals(
+            new String[]{"test.types.Bound"},
+            resourceMethodInfoDTO.nameBindings);
+    }
+
+
+    @Test
+    public void testPlainResourceSeveralOperations() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(
+                PlainResourceSeveralOperations.class, bus);
+
+        assertEquals(2, resourceMethodInfoDTOS.length);
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            resourceMethodInfoDTOS[0];
+
+        assertEquals(HttpMethod.GET, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+
+        resourceMethodInfoDTO = resourceMethodInfoDTOS[1];
+
+        assertEquals(HttpMethod.POST, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+    }
+
+    @Test
+    public void testPlainResourceSeveralOperationsWithCommonPath() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(
+                PlainResourceSeveralOperationsCommonPath.class, bus);
+
+        assertEquals(2, resourceMethodInfoDTOS.length);
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            resourceMethodInfoDTOS[0];
+
+        assertEquals(HttpMethod.GET, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/common", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+
+        resourceMethodInfoDTO = resourceMethodInfoDTOS[1];
+
+        assertEquals(HttpMethod.POST, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/common", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+    }
+
+    @Test
+    public void testPlainResourceSeveralOperationsWithDifferentPath() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(
+                PlainResourceSeveralOperationsDifferentPath.class, bus);
+
+        assertEquals(2, resourceMethodInfoDTOS.length);
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            resourceMethodInfoDTOS[0];
+
+        assertEquals(HttpMethod.GET, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/common", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+
+        resourceMethodInfoDTO = resourceMethodInfoDTOS[1];
+
+        assertEquals(HttpMethod.POST, resourceMethodInfoDTO.method);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.consumingMimeType);
+        assertArrayEquals(
+            ALL_TYPES, resourceMethodInfoDTO.producingMimeType);
+        assertEquals("/common/different", resourceMethodInfoDTO.path);
+        assertArrayEquals(new String[]{}, resourceMethodInfoDTO.nameBindings);
+    }
+
+    @Test
+    public void testResourceWithSubresource() {
+        Bus bus = BusFactory.getDefaultBus(true);
+
+        ResourceMethodInfoDTO[] resourceMethodInfoDTOS =
+            ClassIntrospector.getResourceMethodInfos(
+                ResourceWithSubResource.class, bus);
+
+        assertEquals(5, resourceMethodInfoDTOS.length);
+
+        List<ResourceMethodInfoDTOWrapper> wrappers = Arrays.stream(
+            resourceMethodInfoDTOS
+        ).map(
+            ResourceMethodInfoDTOWrapper::new
+        ).collect(
+            Collectors.toList()
+        );
+
+        ResourceMethodInfoDTO resourceMethodInfoDTO =
+            new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.method = HttpMethod.GET;
+        resourceMethodInfoDTO.consumingMimeType = ALL_TYPES;
+        resourceMethodInfoDTO.producingMimeType =
+            new String[]{MediaType.APPLICATION_XML};
+        resourceMethodInfoDTO.path = "/resource";
+        resourceMethodInfoDTO.nameBindings = new String[]{};
+
+        assertTrue(wrappers.remove(new ResourceMethodInfoDTOWrapper(resourceMethodInfoDTO)));
+
+        resourceMethodInfoDTO = new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.method = HttpMethod.GET;
+        resourceMethodInfoDTO.consumingMimeType =
+            new String[]{MediaType.APPLICATION_JSON};
+        resourceMethodInfoDTO.producingMimeType =
+            new String[]{MediaType.APPLICATION_JSON};
+        resourceMethodInfoDTO.path = "/resource/subresource";
+        resourceMethodInfoDTO.nameBindings = new String[]{};
+
+        assertTrue(wrappers.remove(new ResourceMethodInfoDTOWrapper(resourceMethodInfoDTO)));
+
+        resourceMethodInfoDTO = new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.method = HttpMethod.POST;
+        resourceMethodInfoDTO.consumingMimeType =
+            new String[]{MediaType.APPLICATION_XML};
+        resourceMethodInfoDTO.producingMimeType =
+            new String[]{
+                MediaType.TEXT_PLAIN,
+                MediaType.APPLICATION_JSON
+            };
+        resourceMethodInfoDTO.path = "/resource/subresource";
+        resourceMethodInfoDTO.nameBindings = new String[]{};
+
+        assertTrue(wrappers.remove(new ResourceMethodInfoDTOWrapper(resourceMethodInfoDTO)));
+
+        resourceMethodInfoDTO = new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.method = HttpMethod.GET;
+        resourceMethodInfoDTO.consumingMimeType =
+            new String[]{MediaType.APPLICATION_JSON};
+        resourceMethodInfoDTO.producingMimeType =
+            new String[]{MediaType.APPLICATION_JSON};
+        resourceMethodInfoDTO.path = "/resource/subresource/{path}";
+        resourceMethodInfoDTO.nameBindings = new String[]{};
+
+        assertTrue(wrappers.remove(new ResourceMethodInfoDTOWrapper(resourceMethodInfoDTO)));
+
+        resourceMethodInfoDTO = new ResourceMethodInfoDTO();
+
+        resourceMethodInfoDTO.method = HttpMethod.POST;
+        resourceMethodInfoDTO.consumingMimeType =
+            new String[]{MediaType.APPLICATION_XML};
+        resourceMethodInfoDTO.producingMimeType = new String[]{
+            MediaType.TEXT_PLAIN,
+            MediaType.APPLICATION_JSON
+        };
+        resourceMethodInfoDTO.path = "/resource/subresource/{path}";
+        resourceMethodInfoDTO.nameBindings = new String[]{};
+
+        assertTrue(wrappers.remove(new ResourceMethodInfoDTOWrapper(resourceMethodInfoDTO)));
+
+        assertTrue(wrappers.isEmpty());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/Bound.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/Bound.java b/jax-rs.whiteboard/src/test/java/test/types/Bound.java
new file mode 100644
index 0000000..f4dfecc
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/Bound.java
@@ -0,0 +1,14 @@
+package test.types;
+
+import javax.ws.rs.NameBinding;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@NameBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Bound {
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/PlainResource.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/PlainResource.java b/jax-rs.whiteboard/src/test/java/test/types/PlainResource.java
new file mode 100644
index 0000000..affad88
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/PlainResource.java
@@ -0,0 +1,29 @@
+/*
+ * 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 test.types;
+
+import javax.ws.rs.GET;
+
+public class PlainResource {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperations.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperations.java b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperations.java
new file mode 100644
index 0000000..ff38cbc
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperations.java
@@ -0,0 +1,35 @@
+/*
+ * 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 test.types;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+
+public class PlainResourceSeveralOperations {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @POST
+    public String get(String post) {
+        return "post";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsCommonPath.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsCommonPath.java b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsCommonPath.java
new file mode 100644
index 0000000..cc05b45
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsCommonPath.java
@@ -0,0 +1,37 @@
+/*
+ * 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 test.types;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+@Path("/common")
+public class PlainResourceSeveralOperationsCommonPath {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @POST
+    public String get(String post) {
+        return "post";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsDifferentPath.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsDifferentPath.java b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsDifferentPath.java
new file mode 100644
index 0000000..4606c46
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsDifferentPath.java
@@ -0,0 +1,38 @@
+/*
+ * 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 test.types;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+@Path("/common")
+public class PlainResourceSeveralOperationsDifferentPath {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @Path("/different")
+    @POST
+    public String get(String post) {
+        return "post";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsWithNameBinding.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsWithNameBinding.java b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsWithNameBinding.java
new file mode 100644
index 0000000..4511080
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/PlainResourceSeveralOperationsWithNameBinding.java
@@ -0,0 +1,36 @@
+/*
+ * 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 test.types;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+
+@Bound
+public class PlainResourceSeveralOperationsWithNameBinding {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @POST
+    public String post() {
+        return "post";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/ResourceMethodInfoDTOWrapper.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/ResourceMethodInfoDTOWrapper.java b/jax-rs.whiteboard/src/test/java/test/types/ResourceMethodInfoDTOWrapper.java
new file mode 100644
index 0000000..ee10b6b
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/ResourceMethodInfoDTOWrapper.java
@@ -0,0 +1,57 @@
+/*
+ * 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 test.types;
+
+
+import org.osgi.service.jaxrs.runtime.dto.ResourceMethodInfoDTO;
+
+import java.util.Arrays;
+
+public class ResourceMethodInfoDTOWrapper
+    extends ResourceMethodInfoDTO {
+
+    public ResourceMethodInfoDTOWrapper(
+        ResourceMethodInfoDTO resourceMethodInfoDTO) {
+
+        path = resourceMethodInfoDTO.path;
+        method = resourceMethodInfoDTO.method;
+        consumingMimeType = resourceMethodInfoDTO.consumingMimeType;
+        producingMimeType = resourceMethodInfoDTO.producingMimeType;
+        nameBindings = resourceMethodInfoDTO.nameBindings;
+
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ResourceMethodInfoDTO)) return false;
+
+        ResourceMethodInfoDTO that = (ResourceMethodInfoDTO) o;
+
+        return
+            path.equals(that.path) &&
+            method.equals(that.method) &&
+            Arrays.asList(consumingMimeType).equals(
+                Arrays.asList(that.consumingMimeType)) &&
+            Arrays.asList(producingMimeType).equals(
+                Arrays.asList(that.producingMimeType)) &&
+            Arrays.asList(nameBindings).equals(
+                Arrays.asList(that.nameBindings));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/ResourceWithSubResource.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/ResourceWithSubResource.java b/jax-rs.whiteboard/src/test/java/test/types/ResourceWithSubResource.java
new file mode 100644
index 0000000..7836d0f
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/ResourceWithSubResource.java
@@ -0,0 +1,39 @@
+/*
+ * 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 test.types;
+
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+@Path("/resource")
+@Produces("application/xml")
+public class ResourceWithSubResource {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @Path("/subresource")
+    public SubResource getResource() {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/aries-jax-rs-whiteboard/blob/efcc0439/jax-rs.whiteboard/src/test/java/test/types/SubResource.java
----------------------------------------------------------------------
diff --git a/jax-rs.whiteboard/src/test/java/test/types/SubResource.java b/jax-rs.whiteboard/src/test/java/test/types/SubResource.java
new file mode 100644
index 0000000..202d1f4
--- /dev/null
+++ b/jax-rs.whiteboard/src/test/java/test/types/SubResource.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 test.types;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+@Produces("application/json")
+public class SubResource {
+
+    @POST
+    @Consumes("application/xml")
+    @Produces({"text/plain", "application/json"})
+    public String doSomething(String thing) {
+        return "something";
+    }
+
+    @GET
+    @Consumes("application/json")
+    public String getParam() {
+        return "getParam";
+    }
+
+    @Path("/{path}")
+    public SubResource subPath(@PathParam("path") String subPath) {
+        return null;
+    }
+
+}