|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved. |
3 | 3 | *
|
4 | 4 | * This program and the accompanying materials are made available under the
|
5 | 5 | * terms of the Eclipse Public License v. 2.0, which is available at
|
|
19 | 19 | import org.glassfish.jersey.client.ClientConfig;
|
20 | 20 | import org.glassfish.jersey.client.ClientLifecycleListener;
|
21 | 21 | import org.glassfish.jersey.client.ClientProperties;
|
| 22 | +import org.glassfish.jersey.client.JerseyClient; |
22 | 23 | import org.glassfish.jersey.examples.sse.jersey.App;
|
23 | 24 | import org.glassfish.jersey.examples.sse.jersey.DomainResource;
|
24 | 25 | import org.glassfish.jersey.examples.sse.jersey.ServerSentEventsResource;
|
|
33 | 34 |
|
34 | 35 | import javax.ws.rs.client.Entity;
|
35 | 36 | import javax.ws.rs.core.Application;
|
| 37 | + |
| 38 | +import java.io.ByteArrayInputStream; |
| 39 | +import java.io.InputStream; |
| 40 | +import java.lang.reflect.Field; |
36 | 41 | import java.util.ArrayList;
|
37 | 42 | import java.util.List;
|
| 43 | +import java.util.Map; |
38 | 44 | import java.util.concurrent.Callable;
|
39 | 45 | import java.util.concurrent.CountDownLatch;
|
40 | 46 | import java.util.concurrent.ExecutorService;
|
|
44 | 50 | import java.util.concurrent.atomic.AtomicInteger;
|
45 | 51 |
|
46 | 52 | import static org.hamcrest.CoreMatchers.equalTo;
|
| 53 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
47 | 54 |
|
48 | 55 | public class SSETest extends JerseyTest {
|
49 | 56 | private static final int MAX_CLIENTS = 10;
|
@@ -123,13 +130,29 @@ public void testInboundEventReaderMultiple() throws Exception {
|
123 | 130 | }
|
124 | 131 |
|
125 | 132 | System.gc();
|
| 133 | + triggerCleanupOfWeakHashMap(); |
126 | 134 | closeLatch.await(15_000, TimeUnit.MILLISECONDS);
|
127 | 135 | // One ClientConfig is on the Client
|
128 | 136 | // + COUNT of them is created by .register(SseFeature.class)
|
129 | 137 | Assertions.assertEquals(COUNT + 1, atomicInteger.get());
|
130 | 138 | Assertions.assertEquals(0, closeLatch.getCount());
|
131 | 139 | }
|
132 | 140 |
|
| 141 | + // WeakHashMap does not clean after GC. It cleans after some operations. This triggers it. |
| 142 | + private void triggerCleanupOfWeakHashMap() throws Exception { |
| 143 | + Field field = JerseyClient.class.getDeclaredField("clientRuntimeLifeCycle"); |
| 144 | + field.setAccessible(true); |
| 145 | + Map<InputStream, Object> clientRuntimeLifeCycle = (Map<InputStream, Object>) field.get(client()); |
| 146 | + assertEquals(0, clientRuntimeLifeCycle.size()); |
| 147 | + clientRuntimeLifeCycle.clear(); |
| 148 | + System.gc(); |
| 149 | + Thread.sleep(100); |
| 150 | + // Required to invoke WeakHashMap#expungeStaleEntries |
| 151 | + ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]); |
| 152 | + clientRuntimeLifeCycle.put(in, new Object()); |
| 153 | + clientRuntimeLifeCycle.size(); |
| 154 | + } |
| 155 | + |
133 | 156 |
|
134 | 157 |
|
135 | 158 | public static class ClientRuntimeCloseVerifier implements ClientLifecycleListener {
|
|
0 commit comments