Skip to content

Commit

Permalink
Fix memory leak when client does not use HK2
Browse files Browse the repository at this point in the history
Signed-off-by: jansupol <jan.supol@oracle.com>
  • Loading branch information
jansupol committed Nov 29, 2024
1 parent 47e37bd commit 1a318f3
Show file tree
Hide file tree
Showing 7 changed files with 628 additions and 99 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -20,21 +20,29 @@
import org.glassfish.jersey.internal.util.LazyUid;
import org.glassfish.jersey.process.internal.RequestScope;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

public class NonInjectionRequestScope extends RequestScope {

private final NonInjectionManager nonInjectionManager;

public NonInjectionRequestScope(NonInjectionManager nonInjectionManager) {
this.nonInjectionManager = nonInjectionManager;
}

@Override
public org.glassfish.jersey.process.internal.RequestContext createContext() {
return new Instance();
return new Instance(nonInjectionManager);
}

/**
* Implementation of the request scope instance.
*/
public static final class Instance implements org.glassfish.jersey.process.internal.RequestContext {

private final NonInjectionManager injectionManager;

private static final ExtendedLogger logger = new ExtendedLogger(Logger.getLogger(Instance.class.getName()), Level.FINEST);

/*
Expand All @@ -48,10 +56,11 @@ public static final class Instance implements org.glassfish.jersey.process.inter
/**
* Holds the number of snapshots of this scope.
*/
private final AtomicInteger referenceCounter;
private int referenceCounter;

private Instance() {
this.referenceCounter = new AtomicInteger(1);
private Instance(NonInjectionManager injectionManager) {
this.injectionManager = injectionManager;
this.referenceCounter = 1;
}

/**
Expand All @@ -65,7 +74,7 @@ private Instance() {
@Override
public NonInjectionRequestScope.Instance getReference() {
// TODO: replace counter with a phantom reference + reference queue-based solution
referenceCounter.incrementAndGet();
referenceCounter++;
return this;
}

Expand All @@ -77,7 +86,9 @@ public NonInjectionRequestScope.Instance getReference() {
*/
@Override
public void release() {
referenceCounter.decrementAndGet();
if (0 == --referenceCounter) {
injectionManager.disposeRequestScopedInstances();
}
}

@Override
Expand Down
57 changes: 57 additions & 0 deletions tests/e2e-inject/non-inject/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>e2e-inject</artifactId>
<groupId>org.glassfish.jersey.tests</groupId>
<version>2.46-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>e2e-inject-noninject</artifactId>

<dependencies>
<dependency>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>jersey-injectless-client</artifactId>
<version>2.46-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.tests.e2e.inject.noninject;

import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.internal.inject.DisposableSupplier;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class DisposableSuplierTest {
private static final AtomicInteger disposeCounter = new AtomicInteger(0);
private static final String HOST = "http://somewhere.anywhere";

public interface ResponseObject {
Response getResponse();
}

private static class TestDisposableSupplier implements DisposableSupplier<ResponseObject> {
AtomicInteger counter = new AtomicInteger(300);

@Override
public void dispose(ResponseObject instance) {
disposeCounter.incrementAndGet();
}

@Override
public ResponseObject get() {
return new ResponseObject() {

@Override
public Response getResponse() {
return Response.ok().build();
}
};
}
}

private static class DisposableSupplierInjectingFilter implements ClientRequestFilter {
private final ResponseObject responseSupplier;

@Inject
private DisposableSupplierInjectingFilter(ResponseObject responseSupplier) {
this.responseSupplier = responseSupplier;
}

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.abortWith(responseSupplier.getResponse());
}
}

@Test
public void testDisposeCount() {
disposeCounter.set(0);
int CNT = 4;
Client client = ClientBuilder.newClient()
.register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(TestDisposableSupplier.class).to(ResponseObject.class)
.proxy(true).proxyForSameScope(false).in(RequestScoped.class);
}
}).register(DisposableSupplierInjectingFilter.class);

for (int i = 0; i != CNT; i++) {
try (Response response = client.target(HOST).request().get()) {
MatcherAssert.assertThat(response.getStatus(), Matchers.is(200));
}
}

MatcherAssert.assertThat(disposeCounter.get(), Matchers.is(CNT));
}
}
Loading

0 comments on commit 1a318f3

Please sign in to comment.