Author: rmannibucau Date: Fri Aug 23 07:11:37 2013 New Revision: 1516714 URL: http://svn.apache.org/r1516714 Log: TOMEE-1020 creating @Provider (jaxrs) from contructor if parameters uses @Context
Added: tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/ProviderWithConstructorTest.java - copied, changed from r1516706, tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/CdiInterceptorContextTest.java Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/util/ServiceInfos.java tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/rest/ThreadLocalContextManager.java tomee/tomee/trunk/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/util/ServiceInfos.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/util/ServiceInfos.java?rev=1516714&r1=1516713&r2=1516714&view=diff ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/util/ServiceInfos.java (original) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/util/ServiceInfos.java Fri Aug 23 07:11:37 2013 @@ -72,6 +72,10 @@ public final class ServiceInfos { } public static List<Object> resolve(final Collection<ServiceInfo> serviceInfos, final String[] ids) { + return resolve(serviceInfos, ids, null); + } + + public static List<Object> resolve(final Collection<ServiceInfo> serviceInfos, final String[] ids, final Factory factory) { if (ids == null || ids.length == 0) { return null; } @@ -81,8 +85,13 @@ public final class ServiceInfos { Object instance = resolve(serviceInfos, id); if (instance == null) { // maybe id == classname try { - instance = Thread.currentThread().getContextClassLoader().loadClass(id).newInstance(); - } catch (Exception e) { + final Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(id); + if (factory == null) { + instance = aClass.newInstance(); + } else { + instance = factory.newInstance(aClass); + } + } catch (final Exception e) { // ignore } } @@ -147,4 +156,8 @@ public final class ServiceInfos { return service; } + + public static interface Factory { + Object newInstance(final Class<?> clazz) throws Exception; + } } Modified: tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/rest/ThreadLocalContextManager.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/rest/ThreadLocalContextManager.java?rev=1516714&r1=1516713&r2=1516714&view=diff ============================================================================== --- tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/rest/ThreadLocalContextManager.java (original) +++ tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/rest/ThreadLocalContextManager.java Fri Aug 23 07:11:37 2013 @@ -17,7 +17,17 @@ package org.apache.openejb.rest; +import javax.servlet.ServletConfig; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.Application; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.SecurityContext; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Providers; import java.util.Map; public class ThreadLocalContextManager { @@ -53,4 +63,29 @@ public class ThreadLocalContextManager { } OTHERS.remove(); } + + public static Object findThreadLocal(final Class<?> type) { + if (Request.class.equals(type)) { + return ThreadLocalContextManager.REQUEST; + } else if (UriInfo.class.equals(type)) { + return ThreadLocalContextManager.URI_INFO; + } else if (HttpHeaders.class.equals(type)) { + return ThreadLocalContextManager.HTTP_HEADERS; + } else if (SecurityContext.class.equals(type)) { + return ThreadLocalContextManager.SECURITY_CONTEXT; + } else if (ContextResolver.class.equals(type)) { + return ThreadLocalContextManager.CONTEXT_RESOLVER; + } else if (Providers.class.equals(type)) { + return ThreadLocalContextManager.PROVIDERS; + } else if (ServletRequest.class.equals(type)) { + return ThreadLocalContextManager.SERVLET_REQUEST; + } else if (HttpServletRequest.class.equals(type)) { + return ThreadLocalContextManager.HTTP_SERVLET_REQUEST; + } else if (HttpServletResponse.class.equals(type)) { + return ThreadLocalContextManager.HTTP_SERVLET_RESPONSE; + } else if (ServletConfig.class.equals(type)) { + return ThreadLocalContextManager.SERVLET_CONFIG; + } + return null; + } } Modified: tomee/tomee/trunk/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java?rev=1516714&r1=1516713&r2=1516714&view=diff ============================================================================== --- tomee/tomee/trunk/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java (original) +++ tomee/tomee/trunk/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java Fri Aug 23 07:11:37 2013 @@ -28,6 +28,7 @@ import org.apache.cxf.jaxrs.model.Method import org.apache.cxf.jaxrs.model.OperationResourceInfo; import org.apache.cxf.jaxrs.provider.JAXBElementProvider; import org.apache.cxf.jaxrs.provider.json.JSONProvider; +import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.service.invoker.Invoker; import org.apache.cxf.transport.http.AbstractHTTPDestination; import org.apache.cxf.transport.http.HTTPTransportFactory; @@ -38,6 +39,7 @@ import org.apache.openejb.assembler.clas import org.apache.openejb.assembler.classic.util.ServiceConfiguration; import org.apache.openejb.assembler.classic.util.ServiceInfos; import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.rest.ThreadLocalContextManager; import org.apache.openejb.server.cxf.transport.util.CxfUtil; import org.apache.openejb.server.httpd.HttpRequest; import org.apache.openejb.server.httpd.HttpRequestImpl; @@ -59,6 +61,8 @@ import javax.ws.rs.core.Application; import javax.xml.bind.Marshaller; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -107,7 +111,6 @@ public class CxfRsHttpListener implement public CxfRsHttpListener(final HTTPTransportFactory httpTransportFactory, final String star) { transportFactory = httpTransportFactory; wildcard = star; - } @Override @@ -269,13 +272,13 @@ public class CxfRsHttpListener implement for (Object o : additionalProviders) { if (o instanceof Class<?>) { final Class<?> clazz = (Class<?>) o; - final Object instance = ServiceInfos.resolve(services, clazz.getName()); - if (instance != null) { - instances.add(instance); + final Collection<Object> instance = ServiceInfos.resolve(services, new String[] { clazz.getName() }, ProviderFactory.INSTANCE); + if (instance != null && !instance.isEmpty()) { + instances.add(instance.iterator().next()); } else { try { - instances.add(clazz.newInstance()); - } catch (Exception e) { + instances.add(newProvider(clazz)); + } catch (final Exception e) { LOGGER.error("can't instantiate " + clazz.getName(), e); } } @@ -286,6 +289,10 @@ public class CxfRsHttpListener implement return instances; } + private Object newProvider(final Class<?> clazz) throws IllegalAccessException, InstantiationException { + return clazz.newInstance(); + } + public void undeploy() { final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); @@ -514,7 +521,7 @@ public class CxfRsHttpListener implement List<Object> providers = null; if (providersConfig != null) { - providers = ServiceInfos.resolve(services, providersConfig.toArray(new String[providersConfig.size()])); + providers = ServiceInfos.resolve(services, providersConfig.toArray(new String[providersConfig.size()]), ProviderFactory.INSTANCE); if (providers != null && additionalProviders != null && !additionalProviders.isEmpty()) { providers.addAll(providers(services, additionalProviders)); } @@ -545,4 +552,43 @@ public class CxfRsHttpListener implement return Arrays.asList((Object) jaxb, json); } + + private static class ProviderFactory implements ServiceInfos.Factory { + private static final ServiceInfos.Factory INSTANCE = new ProviderFactory(); + + @Override + public Object newInstance(final Class<?> clazz) throws Exception { + boolean found = false; + Object instance = null; + for (final Constructor<?> c : clazz.getConstructors()) { + int contextAnnotations = 0; + for (final Annotation[] annotations : c.getParameterAnnotations()) { + for (final Annotation a : annotations) { + if (javax.ws.rs.core.Context.class.equals(a.annotationType())) { + contextAnnotations++; + break; + } + } + } + if (contextAnnotations == c.getParameterTypes().length) { + if (found) { + LOGGER.warning("Found multiple matching constructor for " + clazz.getName()); + return instance; + } + + final Object[] params = new Object[c.getParameterTypes().length]; + for (int i = 0; i < params.length; i++) { + params[i] = ThreadLocalContextManager.findThreadLocal(c.getParameterTypes()[i]); + // params[i] can be null if not a known type + } + instance = c.newInstance(params); + found = true; + } + } + if (instance != null) { + return instance; + } + return clazz.newInstance(); + } + } } Copied: tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/ProviderWithConstructorTest.java (from r1516706, tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/CdiInterceptorContextTest.java) URL: http://svn.apache.org/viewvc/tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/ProviderWithConstructorTest.java?p2=tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/ProviderWithConstructorTest.java&p1=tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/CdiInterceptorContextTest.java&r1=1516706&r2=1516714&rev=1516714&view=diff ============================================================================== --- tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/CdiInterceptorContextTest.java (original) +++ tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/ProviderWithConstructorTest.java Fri Aug 23 07:11:37 2013 @@ -25,77 +25,86 @@ import org.apache.openejb.testing.Module import org.junit.Test; import org.junit.runner.RunWith; -import javax.interceptor.AroundInvoke; -import javax.interceptor.Interceptor; -import javax.interceptor.InterceptorBinding; -import javax.interceptor.InvocationContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; +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 java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.Assert.assertEquals; @EnableServices("jax-rs") @RunWith(ApplicationComposer.class) -public class CdiInterceptorContextTest { +public class ProviderWithConstructorTest { @Module - @Classes(value = { Endpoint.class, AnswerPerfect.class }, cdiInterceptors = AnswerPerfect.class) + @Classes(value = { AnEndpointToCheckAProvider.class }) public WebApp war() { return new WebApp() .contextRoot("app") .addServlet("REST Application", Application.class.getName()) - .addInitParam("REST Application", "javax.ws.rs.Application", PerfectApplication.class.getName()); + .addInitParam("REST Application", "javax.ws.rs.Application", ApplicationWithProvider.class.getName()); } @Test public void checkServiceWasDeployed() { - assertEquals("perfect", WebClient.create("http://localhost:4204/app").path("/foo").get(String.class)); + assertEquals("/app", WebClient.create("http://localhost:4204/app").path("/foo").accept("openejb/constructor").get(String.class)); } @Path("/foo") - @Perfect - public static class Endpoint { - @GET + public static class AnEndpointToCheckAProvider { + @GET @Produces("openejb/constructor") public String bar() { - return "bar"; + return "bar"; // whatever the value is the provider will return the context path } } - @InterceptorBinding - @Target(TYPE) - @Retention(RUNTIME) - public static @interface Perfect { - + public static class ApplicationWithProvider extends Application { + @Override + public Set<Class<?>> getClasses() { + final Set<Class<?>> classes = new HashSet<Class<?>>(); + classes.add(AnEndpointToCheckAProvider.class); + classes.add(ConstructorProvider.class); + return classes; + } } - @Interceptor @Perfect - public static class AnswerPerfect { - @Context - private HttpServletRequest request; - - @AroundInvoke - public Object invoke(final InvocationContext ic) throws Exception { - if (ic.getMethod().getName().equals("bar") && "foo".equals(request.getRequestURI())) { - return "perfect"; + @Provider + @Produces("openejb/constructor") + public static class ConstructorProvider<T> implements MessageBodyWriter<T> { + private final HttpServletRequest request; + + public ConstructorProvider(final @Context HttpServletRequest request) { + if (request == null) { + throw new IllegalArgumentException(); } - return ic.proceed(); + this.request = request; } - } - public static class PerfectApplication extends Application { @Override - public Set<Class<?>> getClasses() { - final Set<Class<?>> classes = new HashSet<Class<?>>(); - classes.add(Endpoint.class); - return classes; + public long getSize(T t, Class<?> rawType, Type genericType, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + @Override + public boolean isWriteable(Class<?> rawType, Type genericType, Annotation[] annotations, MediaType mediaType) { + return true; + } + + @Override + public void writeTo(T t, Class<?> rawType, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException { + entityStream.write(request.getContextPath().getBytes()); } } + }