This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/aries-jax-rs-whiteboard.git
The following commit(s) were added to refs/heads/master by this push: new 9bc35a6 [ARIES-2002] ensure proxies are unwrapped for jaxrs resources 9bc35a6 is described below commit 9bc35a623ed0cf0c4a0e1f583d5be60027a23f49 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Tue Sep 22 16:50:53 2020 +0200 [ARIES-2002] ensure proxies are unwrapped for jaxrs resources --- .../internal/cxf/CxfJaxrsServiceRegistrator.java | 85 ++++++++++++++++++++-- .../internal/cxf/PromiseAwareJAXRSInvoker.java | 12 +-- .../internal/introspection/ClassIntrospector.java | 12 ++- .../whiteboard/internal/introspection/Proxies.java | 31 ++++++++ 4 files changed, 129 insertions(+), 11 deletions(-) diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/CxfJaxrsServiceRegistrator.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/CxfJaxrsServiceRegistrator.java index f1ba154..22dfc88 100644 --- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/CxfJaxrsServiceRegistrator.java +++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/CxfJaxrsServiceRegistrator.java @@ -23,6 +23,7 @@ import static org.apache.aries.jax.rs.whiteboard.internal.Whiteboard.SUPPORTED_E import static org.apache.aries.jax.rs.whiteboard.internal.utils.Utils.canonicalize; import static org.apache.cxf.jaxrs.provider.ProviderFactory.DEFAULT_FILTER_NAME_BINDING; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -34,16 +35,17 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import javax.ws.rs.ApplicationPath; import javax.ws.rs.RuntimeType; import javax.ws.rs.container.DynamicFeature; import javax.ws.rs.core.Application; import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; +import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.RuntimeDelegate; import org.apache.aries.component.dsl.CachingServiceReference; @@ -51,6 +53,7 @@ import org.apache.aries.component.dsl.OSGi; import org.apache.aries.jax.rs.whiteboard.ApplicationClasses; import org.apache.aries.jax.rs.whiteboard.internal.AriesJaxrsServiceRuntime; import org.apache.aries.jax.rs.whiteboard.internal.ServiceReferenceRegistry; +import org.apache.aries.jax.rs.whiteboard.internal.introspection.Proxies; import org.apache.aries.jax.rs.whiteboard.internal.utils.ServiceTuple; import org.apache.cxf.Bus; import org.apache.cxf.common.util.ClassHelper; @@ -61,7 +64,9 @@ import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; import org.apache.cxf.jaxrs.ext.ContextProvider; import org.apache.cxf.jaxrs.ext.ResourceContextProvider; import org.apache.cxf.jaxrs.impl.ConfigurableImpl; +import org.apache.cxf.jaxrs.lifecycle.PerRequestResourceProvider; import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; import org.apache.cxf.jaxrs.model.ApplicationInfo; import org.apache.cxf.jaxrs.model.ClassResourceInfo; import org.apache.cxf.jaxrs.provider.ProviderFactory.ProviderInfoClassComparator; @@ -69,6 +74,7 @@ import org.apache.cxf.jaxrs.provider.ServerConfigurableFactory; import org.apache.cxf.jaxrs.sse.SseContextProvider; import org.apache.cxf.jaxrs.sse.SseEventSinkContextProvider; import org.apache.cxf.jaxrs.utils.AnnotationUtils; +import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.message.Message; public class CxfJaxrsServiceRegistrator { @@ -196,19 +202,88 @@ public class CxfJaxrsServiceRegistrator { } public <T> T createEndpoint(Application app, Class<T> endpointType) { - JAXRSServerFactoryBean bean = - RuntimeDelegate.getInstance().createEndpoint( - app, JAXRSServerFactoryBean.class); + // final JAXRSServerFactoryBean bean = ResourceUtils.createApplication(app, false, false, false, null); + final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean(); + Set<Object> singletons = app.getSingletons(); + if (!singletons.isEmpty() && singletons.stream().map(Object::getClass).count() < singletons.size()) { + throw new IllegalArgumentException("More than one instance of the same singleton class is available: " + singletons); + } + + final List<Class<?>> resourceClasses = new ArrayList<>(); + final List<Object> providers = new ArrayList<>(); + final List<org.apache.cxf.feature.Feature> features = new ArrayList<>(); + final Map<Class<?>, ResourceProvider> map = new HashMap<>(); + + // Note, app.getClasses() returns a list of per-request classes + // or singleton provider classes + for (Class<?> cls : app.getClasses()) { + if (!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers())) { + if (isProvider(cls)) { + providers.add(ResourceUtils.createProviderInstance(cls)); + } else if (org.apache.cxf.feature.Feature.class.isAssignableFrom(cls)) { + features.add(ResourceUtils.createFeatureInstance((Class<? extends org.apache.cxf.feature.Feature>) cls)); + } else { + resourceClasses.add(cls); + /* todo: support singleton provider otherwise perfs can be a shame + if (useSingletonResourceProvider) { + map.put(cls, new SingletonResourceProvider(ResourceUtils.createProviderInstance(cls))); + } else { + */ + map.put(cls, new PerRequestResourceProvider(cls)); + } + } + } + + // we can get either a provider or resource class here + for (final Object o : singletons) { + if (isProvider(o.getClass())) { + providers.add(o); + } else if (o instanceof org.apache.cxf.feature.Feature) { + features.add((org.apache.cxf.feature.Feature) o); + } else { + final Class<?> unwrapped = Proxies.unwrap(o.getClass()); + resourceClasses.add(unwrapped); + map.put(unwrapped, new SingletonResourceProvider(o)); + } + } + + String address = "/"; + ApplicationPath appPath = ResourceUtils.locateApplicationPath(app.getClass()); + if (appPath != null) { + address = appPath.value(); + } + if (!address.startsWith("/")) { + address = "/" + address; + } + // todo resolve conflicts between @ApplicationPath and @JaxrsApplicationBase (if same value -> use only one?) + bean.setAddress(address); + bean.setStaticSubresourceResolution(false); + bean.setResourceClasses(resourceClasses); + bean.setProviders(providers); + bean.getFeatures().addAll(features); + for (Map.Entry<Class<?>, ResourceProvider> entry : map.entrySet()) { + bean.setResourceProvider(entry.getKey(), entry.getValue()); + } + Map<String, Object> appProps = app.getProperties(); + if (appProps != null) { + bean.getProperties(true).putAll(appProps); + } + bean.setApplication(app); if (JAXRSServerFactoryBean.class.isAssignableFrom(endpointType)) { return endpointType.cast(bean); } + bean.setApplication(app); bean.setStart(false); - Server server = bean.create(); + final Server server = bean.create(); return endpointType.cast(server); } + private boolean isProvider(Class<?> cls) { + return cls.isAnnotationPresent(Provider.class) || SUPPORTED_EXTENSION_INTERFACES.values().stream().anyMatch(it -> it.isAssignableFrom(cls)); + } + public Bus getBus() { return _bus; } diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/PromiseAwareJAXRSInvoker.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/PromiseAwareJAXRSInvoker.java index e71c2c0..0115ddf 100644 --- a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/PromiseAwareJAXRSInvoker.java +++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/cxf/PromiseAwareJAXRSInvoker.java @@ -17,6 +17,8 @@ package org.apache.aries.jax.rs.whiteboard.internal.cxf; import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.cxf.jaxrs.JAXRSInvoker; import org.apache.cxf.jaxrs.impl.AsyncResponseImpl; @@ -24,6 +26,7 @@ import org.apache.cxf.message.Message; import org.osgi.util.promise.Promise; public class PromiseAwareJAXRSInvoker extends JAXRSInvoker { + private final ConcurrentMap<Class<?>, Boolean> promises = new ConcurrentHashMap<>(); /** * OSGi promises are a great way to do asynchronous work, and should be handled @@ -40,11 +43,10 @@ public class PromiseAwareJAXRSInvoker extends JAXRSInvoker { } // Slower check, is it a Promise? - Class<?> clazz = result.getClass(); - if(Arrays.stream(clazz.getInterfaces()) - .map(Class::getName) - .anyMatch(n -> "org.osgi.util.promise.Promise".equals(n))) { - + final Class<?> clazz = result.getClass(); + if (promises.computeIfAbsent(clazz, type -> Arrays.stream(type.getInterfaces()) + .map(Class::getName) + .anyMatch("org.osgi.util.promise.Promise"::equals))) { return handlePromiseFromAnotherClassSpace(inMessage, result, clazz); } 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 index 07877de..941a399 100644 --- 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 @@ -18,6 +18,7 @@ package org.apache.aries.jax.rs.whiteboard.internal.introspection; import org.apache.cxf.Bus; +import org.apache.cxf.common.util.ClassUnwrapper; import org.apache.cxf.jaxrs.model.ClassResourceInfo; import org.apache.cxf.jaxrs.model.MethodDispatcher; import org.apache.cxf.jaxrs.model.OperationResourceInfo; @@ -45,9 +46,10 @@ public class ClassIntrospector { public static Collection<ResourceMethodInfoDTO> getResourceMethodInfos( Class<?> clazz, Bus bus) { + final Class<?> realClass = unwrap(clazz, bus); ClassResourceInfo classResourceInfo = ResourceUtils.createClassResourceInfo( - clazz, clazz, true, true, bus); + realClass, realClass, true, true, bus); Stream<ResourceMethodInfoDTO> convert = convert( new HashSet<>(), "/", null, null, null, null, @@ -56,6 +58,14 @@ public class ClassIntrospector { return convert.collect(Collectors.toList()); } + private static Class<?> unwrap(final Class<?> clazz, final Bus bus) { + final ClassUnwrapper unwrapper = bus == null ? null : bus.getExtension(ClassUnwrapper.class); + if (unwrapper != null) { + return unwrapper.getRealClassFromClass(clazz); + } + return Proxies.unwrap(clazz); + } + private static Stream<ResourceMethodInfoDTO> convert( Set<ClassResourceInfo> visited, String parentPath, String defaultHttpMethod, diff --git a/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/Proxies.java b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/Proxies.java new file mode 100644 index 0000000..415e7e7 --- /dev/null +++ b/jax-rs.whiteboard/src/main/java/org/apache/aries/jax/rs/whiteboard/internal/introspection/Proxies.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.aries.jax.rs.whiteboard.internal.introspection; + +public final class Proxies { + private Proxies() { + // no-op + } + + public static Class<?> unwrap(final Class<?> aClass) { + Class<?> current = aClass; + while (current != null && current != Object.class && current.getName().contains("$$")) { + current = current.getSuperclass(); + } + return current == null ? aClass : current; + } +}