diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ShutdownHookLeakTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ShutdownHookLeakTest.java index 82563eb96d..121671187a 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/ShutdownHookLeakTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/ShutdownHookLeakTest.java @@ -39,21 +39,20 @@ */ package org.glassfish.jersey.client; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.number.OrderingComparison.lessThan; +import static org.junit.Assert.assertThat; + +import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.Collection; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.client.WebTarget; import org.junit.Test; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.number.OrderingComparison.lessThan; -import static org.junit.Assert.assertThat; - /** * Reproducer for JERSEY-2786. * @@ -61,7 +60,8 @@ */ public class ShutdownHookLeakTest { - private final int ITERATIONS = 1000; + private static final int ITERATIONS = 4000; + private static final int THRESHOLD = ITERATIONS * 2 / 3; @Test public void testShutdownHookDoesNotLeak() throws Exception { @@ -71,27 +71,41 @@ public void testShutdownHookDoesNotLeak() throws Exception { final Collection shutdownHooks = getShutdownHooks(client); for (int i = 0; i < ITERATIONS; i++) { - WebTarget target2 = target.property("Washington", "Irving"); - Builder req = target2.request().property("how", "now").property("and", "what"); - req.buildGet().property("Irving", "Washington").property("Thomas", "Alva"); + // Create/Initialize client runtime. + target.property("Washington", "Irving") + .request() + .property("how", "now") + .buildGet() + .property("Irving", "Washington"); } - assertThat( - "shutdown hook deque size should not copy number of property invocation", - // 66 % seems like a reasonable threshold for this test to keep it stable - shutdownHooks.size(), is(lessThan(2 * ITERATIONS / 3))); + System.gc(); - client.close(); + int notEnqueued = 0; + int notNull = 0; + for (Object o : shutdownHooks) { + if (((WeakReference) o).get() != null) { + notNull++; + } + if (!((WeakReference) o).isEnqueued()) { + notEnqueued++; + } + } assertThat( - "shutdown hook deque size should be empty after Client closed", - shutdownHooks.size(), is(equalTo(0))); + "Non-null shutdown hook references count should not copy number of property invocation", + // 66 % seems like a reasonable threshold for this test to keep it stable + notNull, is(lessThan(THRESHOLD))); + assertThat( + "Shutdown hook references count not enqueued in the ReferenceQueue should not copy number of property invocation", + // 66 % seems like a reasonable threshold for this test to keep it stable + notEnqueued, is(lessThan(THRESHOLD))); } - private Collection getShutdownHooks(javax.ws.rs.client.Client client) throws NoSuchFieldException, IllegalAccessException { - JerseyClient jerseyClient = (JerseyClient) client; - Field shutdownHooksField = JerseyClient.class.getDeclaredField("shutdownHooks"); + private Collection getShutdownHooks(final Client client) throws NoSuchFieldException, IllegalAccessException { + final JerseyClient jerseyClient = (JerseyClient) client; + final Field shutdownHooksField = JerseyClient.class.getDeclaredField("shutdownHooks"); shutdownHooksField.setAccessible(true); return (Collection) shutdownHooksField.get(jerseyClient); } diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java index f9bc1c5afe..46f7234d53 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java @@ -139,6 +139,7 @@ private static ServiceLocator _createLocator(final String name, final ServiceLoc result.setNeutralContextClassLoader(false); ServiceLocatorUtilities.enablePerThreadScope(result); + ServiceLocatorUtilities.enableImmediateScope(result); for (final Binder binder : binders) { bind(result, binder); diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/inject/InjectionsTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/inject/InjectionsTest.java new file mode 100644 index 0000000000..12422cf71c --- /dev/null +++ b/core-common/src/test/java/org/glassfish/jersey/internal/inject/InjectionsTest.java @@ -0,0 +1,108 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal.inject; + +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import org.glassfish.hk2.api.Immediate; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.junit.Test; + +/** + * Test for the {@link Injections} class. + * + * @author Jord Sonneveld (jord@moz.com) + * + */ +public class InjectionsTest { + + /** + * Verify that services marked with the HK2 Immediate annotation are indeed + * created "immediately" (or at least "soon"). + * + * Because Immediate services are instantiated in a separate thread, we use + * a {@link CountDownLatch} to wait for the service to be created. + * + * After the {@link ServiceLocator} is created, we specifically do not call + * any more methods on it: the locator must instantiate the Immediate + * service without any further prompting to the locator. + * + * @throws InterruptedException if awaiting on the latch is interrupted. + */ + @Test + public void testHK2ImmediateAnnotation() throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + + @SuppressWarnings("unused") // It is unused by design + ServiceLocator sl = Injections.createLocator(new AbstractBinder() { + @Override + protected void configure() { + bind(latch).to(CountDownLatch.class); + bind(ImmediateMe.class).to(ImmediateMe.class).in( + Immediate.class); + } + }); + + // 10 seconds is a LONG time. It should be faster than that. However, 10 + // seconds gives us a reasonable upper limit to wait in case the test + // fails. + assertTrue("Latch should be unlocked within 10 seconds.", + latch.await(10, TimeUnit.SECONDS)); + } + + /** + * Helper class for testing Immediate services. + * + */ + public static final class ImmediateMe { + @Inject + public ImmediateMe(CountDownLatch latch) { + latch.countDown(); + } + } + +}