Hi guys, I think it is important to get JohnzonProvider and JsrProvider even on client side by default since it at least allows to have consistent roundtrips.
Do you think we need to go further (surely write our own client builder and add some clever things inside)? Did my best to avoid it until now but think we can't hack much further without it. On my side it was the last thing I needed, what about you? Romain Manni-Bucau @rmannibucau <https://twitter.com/rmannibucau> | Blog <http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> | LinkedIn <https://www.linkedin.com/in/rmannibucau> | Tomitriber <http://www.tomitribe.com> ---------- Forwarded message ---------- From: <[email protected]> Date: 2015-04-14 18:22 GMT+02:00 Subject: [2/2] tomee git commit: handling ConstrainedTo on server side + hacking default jaxrs provider for client side To: [email protected] handling ConstrainedTo on server side + hacking default jaxrs provider for client side Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/91fd4dbf Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/91fd4dbf Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/91fd4dbf Branch: refs/heads/master Commit: 91fd4dbfb9c739102b961612f48e99f1c6e832a4 Parents: 100593d Author: Romain Manni-Bucau <[email protected]> Authored: Tue Apr 14 18:19:44 2015 +0200 Committer: Romain Manni-Bucau <[email protected]> Committed: Tue Apr 14 18:19:44 2015 +0200 ---------------------------------------------------------------------- .../openejb/server/cxf/rs/CxfRSService.java | 103 ++++++++++++++++--- .../server/cxf/rs/CxfRsHttpListener.java | 25 ++++- .../cxf/rs/DefaultClientProvidersTest.java | 79 ++++++++++++++ 3 files changed, 191 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/91fd4dbf/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java ---------------------------------------------------------------------- diff --git a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java index f1f07b1..581508a 100644 --- a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java +++ b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java @@ -19,8 +19,19 @@ package org.apache.openejb.server.cxf.rs; import org.apache.cxf.Bus; import org.apache.cxf.binding.BindingFactoryManager; import org.apache.cxf.jaxrs.JAXRSBindingFactory; +import org.apache.cxf.jaxrs.client.ClientProviderFactory; +import org.apache.cxf.jaxrs.provider.BinaryDataProvider; +import org.apache.cxf.jaxrs.provider.DataSourceProvider; +import org.apache.cxf.jaxrs.provider.FormEncodingProvider; +import org.apache.cxf.jaxrs.provider.JAXBElementProvider; +import org.apache.cxf.jaxrs.provider.MultipartProvider; +import org.apache.cxf.jaxrs.provider.PrimitiveTextProvider; +import org.apache.cxf.jaxrs.provider.ProviderFactory; +import org.apache.cxf.jaxrs.provider.SourceProvider; import org.apache.cxf.transport.DestinationFactory; import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.johnzon.jaxrs.JohnzonProvider; +import org.apache.johnzon.jaxrs.JsrProvider; import org.apache.openejb.cdi.WebBeansContextBeforeDeploy; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.observer.Observes; @@ -36,6 +47,24 @@ import org.apache.webbeans.annotation.EmptyAnnotationLiteral; import org.apache.webbeans.config.WebBeansContext; import org.apache.webbeans.container.BeanManagerImpl; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; @@ -56,21 +85,6 @@ 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.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.lang.reflect.Type; -import java.net.Socket; -import java.util.Collections; -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; import static java.util.Arrays.asList; @@ -176,6 +190,7 @@ public class CxfRSService extends RESTService { } } hacksOn(); + initCxfClientBuilderProviders(bus); } finally { if (oldLoader != null) { CxfUtil.clearBusLoader(oldLoader); @@ -183,6 +198,64 @@ public class CxfRSService extends RESTService { } } + private void initCxfClientBuilderProviders(final Bus bus) { + if (bus.getProperty("jaxrs.shared.client.factory") == null) { + try { + final Constructor<ClientProviderFactory> constructor = ClientProviderFactory.class.getDeclaredConstructor(ProviderFactory.class, Bus.class); + constructor.setAccessible(true); + final ClientProviderFactory factory = constructor.newInstance(null, bus); + + final Method set = ClientProviderFactory.class.getDeclaredMethod("setProviders", Object[].class); + set.setAccessible(true); + + final String userProviders = SystemInstance.get().getProperty("openejb.jaxrs.client.providers"); + final Object[][] providers; // vararg -> array, reflection -> array + if (userProviders == null) { + providers = new Object[][] {{ + new BinaryDataProvider<>(), + new SourceProvider<>(), + new DataSourceProvider<>(), + new FormEncodingProvider<>(), + new PrimitiveTextProvider<>(), + new JohnzonProvider<>(), + new JAXBElementProvider<>(), + new JsrProvider(), + new MultipartProvider() + }}; + } else { + final Collection<Object> all = new ArrayList<>(16); + + for (String p : userProviders.split(" *, *")) { + p= p.trim(); + if (p.isEmpty()) { + continue; + } + + all.add(Thread.currentThread().getContextClassLoader().loadClass(p).newInstance()); + } + + all.addAll(asList( // added after to be after in the list once sorted + new BinaryDataProvider<>(), + new SourceProvider<>(), + new DataSourceProvider<>(), + new FormEncodingProvider<>(), + new PrimitiveTextProvider<>(), + new JohnzonProvider<>(), + new JAXBElementProvider<>(), + new JsrProvider(), + new MultipartProvider())); + + providers = new Object[][] { all.toArray(new Object[all.size()]) }; + } + set.invoke(factory, providers); + + bus.setProperty("jaxrs.shared.client.factory", factory); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + } + } + private void hacksOn() { CxfHacks.initCxfClassHelper(); } http://git-wip-us.apache.org/repos/asf/tomee/blob/91fd4dbf/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java ---------------------------------------------------------------------- diff --git a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java index 071cd03..56bc5df 100644 --- a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java +++ b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java @@ -94,6 +94,8 @@ import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; import javax.ws.rs.core.Application; import javax.xml.bind.Marshaller; import java.io.File; @@ -134,6 +136,7 @@ public class CxfRsHttpListener implements RsHttpListener { private static final String GLOBAL_PROVIDERS = SystemInstance.get().getProperty(PROVIDERS_KEY); public static final boolean TRY_STATIC_RESOURCES = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.static-first", "true")); + private static final boolean FAIL_ON_CONSTRAINED_TO = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.fail-on-constrainedto", "true")); private static final Map<String, String> STATIC_CONTENT_TYPES; private static final String[] DEFAULT_WELCOME_FILES = new String[]{ "/index.html", "/index.htm" }; @@ -390,6 +393,10 @@ public class CxfRsHttpListener implements RsHttpListener { for (final Object o : additionalProviders) { if (o instanceof Class<?>) { final Class<?> clazz = (Class<?>) o; + if (isNotServerProvider(clazz)) { + continue; + } + final String name = clazz.getName(); if (shouldSkipProvider(name)) { continue; @@ -421,7 +428,11 @@ public class CxfRsHttpListener implements RsHttpListener { } } } else { - final String name = o.getClass().getName(); + final Class<?> clazz = o.getClass(); + if (isNotServerProvider(clazz)) { + continue; + } + final String name = clazz.getName(); if (shouldSkipProvider(name)) { continue; } @@ -432,6 +443,18 @@ public class CxfRsHttpListener implements RsHttpListener { return instances; } + private boolean isNotServerProvider(Class<?> clazz) { + final ConstrainedTo ct = clazz.getAnnotation(ConstrainedTo.class); + if (ct != null && ct.value() != RuntimeType.SERVER) { + if (!FAIL_ON_CONSTRAINED_TO) { + LOGGER.warning(clazz + " is not a SERVER provider, ignoring"); + return true; + } + throw new IllegalArgumentException(clazz + " is not a SERVER provider"); + } + return false; + } + private static boolean shouldSkipProvider(final String name) { return "false".equalsIgnoreCase(SystemInstance.get().getProperty(name + ".activated", "true")) || name.startsWith("org.apache.wink.common.internal."); http://git-wip-us.apache.org/repos/asf/tomee/blob/91fd4dbf/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DefaultClientProvidersTest.java ---------------------------------------------------------------------- diff --git a/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DefaultClientProvidersTest.java b/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DefaultClientProvidersTest.java new file mode 100644 index 0000000..e04f3b8 --- /dev/null +++ b/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DefaultClientProvidersTest.java @@ -0,0 +1,79 @@ +/** + * 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.openejb.server.cxf.rs; + +import org.apache.openejb.junit.ApplicationComposer; +import org.apache.openejb.testing.Classes; +import org.apache.openejb.testing.EnableServices; +import org.apache.openejb.testing.RandomPort; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.beans.ConstructorProperties; +import java.net.URL; +import javax.json.JsonObject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.MediaType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@Classes(DefaultClientProvidersTest.TheEndpoint.class) +@EnableServices("jaxrs") +@RunWith(ApplicationComposer.class) +public class DefaultClientProvidersTest { + @RandomPort("http") + private URL http; + + @Test + public void json() { + final Json json = ClientBuilder.newBuilder().build().target(http.toExternalForm()).path("openejb/DefaultClientProvidersTest") + .request().get(Json.class); + assertNotNull(json); + assertEquals("value", json.key); + } + + @Test + public void jsonp() { + final JsonObject json = ClientBuilder.newBuilder().build().target(http.toExternalForm()).path("openejb/DefaultClientProvidersTest") + .request().get(JsonObject.class); + assertNotNull(json); + assertEquals("value", json.getString("key")); + assertEquals(1, json.size()); + } + + @Path("DefaultClientProvidersTest") + public static class TheEndpoint { + @GET + @Produces(MediaType.APPLICATION_JSON) + public String json() { + return "{\"key\":\"value\"}"; // not supported by org.apache.cxf.jaxrs.provider.json.JSONProvider + } + } + + public static class Json { + private String key; + + @ConstructorProperties("key") + public Json(String key) { + this.key = key; + } + } +}
