You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2018/03/10 16:01:24 UTC

svn commit: r1826411 - in /openwebbeans/meecrowave/trunk/meecrowave-core/src: main/java/org/apache/meecrowave/ main/java/org/apache/meecrowave/cxf/ test/java/org/apache/meecrowave/cxf/

Author: rmannibucau
Date: Sat Mar 10 16:01:24 2018
New Revision: 1826411

URL: http://svn.apache.org/viewvc?rev=1826411&view=rev
Log:
MEECROWAVE-104 untrack jsonb instance for client jsonbjaxrsproviders

Added:
    openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListener.java
    openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/
    openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListenerTest.java
Modified:
    openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
    openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/ConfigurableBus.java

Modified: openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java?rev=1826411&r1=1826410&r2=1826411&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java Sat Mar 10 16:01:24 2018
@@ -103,6 +103,7 @@ import org.apache.meecrowave.api.StartLi
 import org.apache.meecrowave.api.StopListening;
 import org.apache.meecrowave.cxf.ConfigurableBus;
 import org.apache.meecrowave.cxf.CxfCdiAutoSetup;
+import org.apache.meecrowave.cxf.MeecrowaveClientLifecycleListener;
 import org.apache.meecrowave.io.IO;
 import org.apache.meecrowave.logging.jul.Log4j2Logger;
 import org.apache.meecrowave.logging.log4j2.Log4j2Shutdown;
@@ -655,6 +656,7 @@ public class Meecrowave implements AutoC
             clientBus = new ConfigurableBus();
             clientBus.initProviders(configuration,
                     ofNullable(Thread.currentThread().getContextClassLoader()).orElseGet(ClassLoader::getSystemClassLoader));
+            clientBus.addClientLifecycleListener();
         }
 
         try {

Modified: openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/ConfigurableBus.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/ConfigurableBus.java?rev=1826411&r1=1826410&r2=1826411&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/ConfigurableBus.java (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/ConfigurableBus.java Sat Mar 10 16:01:24 2018
@@ -66,6 +66,8 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.ext.Provider;
 
 import org.apache.cxf.bus.extension.ExtensionManagerBus;
+import org.apache.cxf.bus.managers.ClientLifeCycleManagerImpl;
+import org.apache.cxf.endpoint.ClientLifeCycleManager;
 import org.apache.johnzon.core.AbstractJsonFactory;
 import org.apache.johnzon.core.JsonGeneratorFactoryImpl;
 import org.apache.johnzon.core.JsonParserFactoryImpl;
@@ -147,6 +149,15 @@ public class ConfigurableBus extends Ext
         }
     }
 
+    public void addClientLifecycleListener() {
+        ClientLifeCycleManager manager = getExtension(ClientLifeCycleManager.class);
+        if (manager == null) {
+            manager = new ClientLifeCycleManagerImpl();
+            setExtension(manager, ClientLifeCycleManager.class);
+        }
+        manager.registerListener(new MeecrowaveClientLifecycleListener());
+    }
+
     @Provider
     @Produces({MediaType.APPLICATION_JSON, "*/*+json"})
     @Consumes({MediaType.APPLICATION_JSON, "*/*+json"})
@@ -174,6 +185,10 @@ public class ConfigurableBus extends Ext
             setPretty(pretty);
         }
 
+        public Jsonb getProvider() {
+            return delegate.get();
+        }
+
         protected Jsonb createJsonb() {
             return JsonbBuilder.newBuilder()
                     .withProvider(provider)

Added: openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListener.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListener.java?rev=1826411&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListener.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListener.java Sat Mar 10 16:01:24 2018
@@ -0,0 +1,96 @@
+/**
+ * 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.meecrowave.cxf;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.enterprise.inject.spi.CDI;
+import javax.json.bind.Jsonb;
+
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.ClientLifeCycleListener;
+import org.apache.cxf.jaxrs.client.ClientProviderFactory;
+import org.apache.cxf.jaxrs.model.ProviderInfo;
+import org.apache.cxf.jaxrs.provider.ProviderFactory;
+import org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider;
+import org.apache.johnzon.jsonb.JohnzonJsonb;
+import org.apache.johnzon.jsonb.cdi.JohnzonCdiExtension;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
+
+// ensure client providers don't leak if the user relies on johnzon JsonbJaxrsProvider
+// in a scope < application (see JohnzonCdiExtension integration)
+public class MeecrowaveClientLifecycleListener implements ClientLifeCycleListener {
+    private final Method getReadersWriters;
+    private final Field delegate;
+
+    public MeecrowaveClientLifecycleListener() {
+        try {
+            getReadersWriters = ProviderFactory.class.getDeclaredMethod("getReadersWriters");
+            getReadersWriters.setAccessible(true);
+        } catch (final NoSuchMethodException e) {
+            throw new IllegalArgumentException("Incompatible cxf version detected", e);
+        }
+        try {
+            delegate = JsonbJaxrsProvider.class.getDeclaredField("delegate");
+            delegate.setAccessible(true);
+        } catch (final NoSuchFieldException e) {
+            throw new IllegalArgumentException("Incompatible johnzon version detected", e);
+        }
+    }
+
+    @Override
+    public void clientCreated(final Client client) {
+        // no-op
+    }
+
+    @Override
+    public void clientDestroyed(final Client client) {
+        try {
+            final ClientProviderFactory cpf = ClientProviderFactory.class.cast(
+                    client.getEndpoint().get(ClientProviderFactory.class.getName()));
+            final Collection<Object> invoke = Collection.class.cast(getReadersWriters.invoke(cpf));
+            invoke.stream()
+                    .map(p -> ProviderInfo.class.isInstance(p) ? ProviderInfo.class.cast(p).getProvider() : p)
+                    .filter(p -> !ConfigurableBus.ConfiguredJsonbJaxrsProvider.class.isInstance(p) && JsonbJaxrsProvider.class.isInstance(p))
+                    .map(JsonbJaxrsProvider.class::cast)
+                    .map(p -> {
+                        try {
+                            return ((AtomicReference<Jsonb>) delegate.get(p)).get();
+                        } catch (final IllegalAccessException e) {
+                            throw new IllegalStateException(e);
+                        }
+                    })
+                    .filter(Objects::nonNull)
+                    .filter(JohnzonJsonb.class::isInstance)
+                    .map(JohnzonJsonb.class::cast)
+                    .distinct()
+                    .forEach(jsonb -> CDI.current().select(JohnzonCdiExtension.class).get().untrack(jsonb));
+        } catch (final RuntimeException re) {
+            new LogFacade(MeecrowaveClientLifecycleListener.class.getName())
+                    .debug(re.getMessage(), re);
+        } catch (final Exception re) { // reflection etc which shouldn't fail
+            new LogFacade(MeecrowaveClientLifecycleListener.class.getName())
+                    .error(re.getMessage(), re);
+        }
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListenerTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListenerTest.java?rev=1826411&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListenerTest.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/src/test/java/org/apache/meecrowave/cxf/MeecrowaveClientLifecycleListenerTest.java Sat Mar 10 16:01:24 2018
@@ -0,0 +1,91 @@
+/**
+ * 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.meecrowave.cxf;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Collection;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.CDI;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider;
+import org.apache.johnzon.jsonb.cdi.JohnzonCdiExtension;
+import org.apache.meecrowave.Meecrowave;
+import org.junit.Test;
+
+public class MeecrowaveClientLifecycleListenerTest {
+    @Test
+    public void autoClose() throws IOException, NoSuchFieldException, IllegalAccessException {
+        try (final Meecrowave meecrowave = new Meecrowave(new Meecrowave.Builder()
+                .randomHttpPort()
+                .includePackages(MeecrowaveClientLifecycleListenerTest.class.getName())).bake()) {
+            final JohnzonCdiExtension johnzonCdiExtension = CDI.current().select(JohnzonCdiExtension.class).get();
+            final Field jsonbs = JohnzonCdiExtension.class.getDeclaredField("jsonbs");
+            jsonbs.setAccessible(true);
+            final BeanManager beanManager = CDI.current().getBeanManager();
+            final JohnzonCdiExtension extensionInstance = JohnzonCdiExtension.class.cast(beanManager.getContext(ApplicationScoped.class).get(
+                    beanManager.resolve(beanManager.getBeans(JohnzonCdiExtension.class))));
+            final Collection<?> o = Collection.class.cast(jsonbs.get(extensionInstance));
+
+            { // ensure server is init whatever test suite we run in
+                final Client client = ClientBuilder.newClient();
+                get(meecrowave, client);
+                client.close();
+            }
+
+            final int origin = o.size();
+            final Client client = ClientBuilder.newClient();
+            final JsonbJaxrsProvider<?> provider = new JsonbJaxrsProvider<>();
+            client.register(provider);
+            get(meecrowave, client);
+            assertEquals(origin + 1, o.size());
+            client.close();
+            assertEquals(origin, o.size());
+        }
+    }
+
+    private void get(final Meecrowave meecrowave, final Client client) {
+        client.target("http://localhost:" + meecrowave.getConfiguration().getHttpPort() + "/MeecrowaveClientLifecycleListenerTest")
+                .request(MediaType.APPLICATION_JSON_TYPE)
+                .get(Foo.class);
+    }
+
+    @Path("MeecrowaveClientLifecycleListenerTest")
+    @ApplicationScoped
+    public static class Endpoint {
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        public Foo get() {
+            return new Foo();
+        }
+    }
+
+    public static class Foo {
+    }
+}