DisposableSupplierTest.java
/*
* Copyright (c) 2021, 2022 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.inject.weld.internal.managed;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javax.enterprise.inject.Vetoed;
import javax.inject.Inject;
import javax.ws.rs.core.GenericType;
import org.glassfish.jersey.inject.weld.internal.bean.BeanHelper;
import org.glassfish.jersey.internal.inject.DisposableSupplier;
import org.glassfish.jersey.process.internal.RequestScope;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
/**
* Tests that {@link DisposableSupplier} is properly processed by {@link BeanHelper}.
*
* @author Petr Bouda
*/
@Vetoed
public class DisposableSupplierTest extends TestParent {
private static final Type DISPOSABLE_SUPPLIER_CLASS_TYPE =
new GenericType<DisposableSupplier<StringForSupplierClass>>() {}.getType();
private static final Type DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE =
new GenericType<DisposableSupplier<StringForSupplierSingletonClass>>() {}.getType();
private static final Type DISPOSABLE_SUPPLIER_INSTANCE_TYPE =
new GenericType<DisposableSupplier<StringForSupplierInstance>>() {}.getType();
private static final Type PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE =
new GenericType<DisposableSupplier<ProxiableHolderClass>>() {}.getType();
private static AtomicBoolean onlyOnceGuard = new AtomicBoolean(false);
@BeforeEach
public void bindInit() {
if (!onlyOnceGuard.getAndSet(true)) {
BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl())
.to(StringForSupplierInstance.class));
}
}
@Test
public void testBindSingletonClassDisposableSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class)
// .to(StringForSupplierSingletonClass.class));
Object supplier = injectionManager.getInstance(new GenericType<Supplier<StringForSupplierSingletonClass>>() {}.getType());
Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
assertNotNull(supplier);
assertNotNull(disposableSupplier);
assertSame(supplier, disposableSupplier);
}
@Test
public void testBindPerLookupClassDisposableSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class)
// .to(StringForSupplierClass.class));
Object supplier = injectionManager.getInstance(new GenericType<Supplier<StringForSupplierClass>>() {}.getType());
Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
assertNotNull(supplier);
assertNotNull(disposableSupplier);
assertNotSame(supplier, disposableSupplier);
}
@Test
public void testBindInstanceDisposableSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl())
// .to(StringForSupplierInstance.class));
Object supplier = injectionManager.getInstance(new GenericType<Supplier<StringForSupplierInstance>>() {}.getType());
Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE);
assertNotNull(supplier);
assertNotNull(disposableSupplier);
assertSame(supplier, disposableSupplier);
}
@Test
public void testNotBindClassDisposableSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(SupplierGreeting.class).to(GreetingsClass.class));
assertNull(injectionManager.getInstance(new GenericType<DisposableSupplier<GreetingsClass>>() {}.getType()));
}
@Test
public void testNotBindInstanceDisposableSupplier() {
// BindingTestHelper.bind(injectionManager,
// binder -> binder.bindFactory(new SupplierGreeting()).to(GreetingsInstance.class));
assertNull(injectionManager.getInstance(new GenericType<DisposableSupplier<GreetingsInstance>>() {}.getType()));
}
@Test
public void testOnlyIncrementSingletonSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class)
// .to(StringForSupplierSingletonClass.class));
Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
assertEquals("1", ((DisposableSupplier<?>) instance1).get());
Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
assertEquals("2", ((DisposableSupplier<?>) instance2).get());
Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
assertEquals("3", ((DisposableSupplier<?>) instance3).get());
dispose(instance1, instance2, instance3);
}
@Test
public void testOnlyIncrementInstanceSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl())
// .to(StringForSupplierInstance.class));
Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE);
assertEquals("1", ((DisposableSupplier<?>) instance1).get());
Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE);
assertEquals("2", ((DisposableSupplier<?>) instance2).get());
Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE);
assertEquals("3", ((DisposableSupplier<?>) instance3).get());
dispose(instance1, instance2, instance3);
}
@Test
public void testOnlyIncrementPerLookupSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class)
// .to(StringForSupplierClass.class));
Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
assertEquals("1", ((DisposableSupplier<?>) instance1).get());
Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
assertEquals("1", ((DisposableSupplier<?>) instance2).get());
Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
assertEquals("1", ((DisposableSupplier<?>) instance3).get());
dispose(instance1, instance2, instance3);
}
@Test
public void testOnlyIncrementSingletonInstances() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class)
// .to(StringForSupplierSingletonClass.class));
Object instance1 = injectionManager.getInstance(StringForSupplierSingletonClass.class);
assertEquals("1", instance1);
Object instance2 = injectionManager.getInstance(StringForSupplierSingletonClass.class);
assertEquals("2", instance2);
Object instance3 = injectionManager.getInstance(StringForSupplierSingletonClass.class);
assertEquals("3", instance3);
Object o = injectionManager.getInstance(
new GenericType<DisposableSupplier<StringForSupplierSingletonClass>>() {}.getType());
dispose(o, o, o);
}
@Test
public void testOnlyIncrementInstanceInstance() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl())
// .to(StringForSupplierInstance.class));
Object instance1 = injectionManager.getInstance(StringForSupplierInstance.class);
assertEquals("1", instance1);
Object instance2 = injectionManager.getInstance(StringForSupplierInstance.class);
assertEquals("2", instance2);
Object instance3 = injectionManager.getInstance(StringForSupplierInstance.class);
assertEquals("3", instance3);
Object o = injectionManager.getInstance(new GenericType<DisposableSupplier<StringForSupplierInstance>>() {}.getType());
dispose(o, o, o);
}
@Test
public void testDisposeSingletonSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class)
// .to(StringForSupplierSingletonClass.class));
// 1-1
DisposableSupplier<StringForSupplierSingletonClass> supplier1 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
CharSequence instance1 = supplier1.get();
// 2-2
DisposableSupplier<StringForSupplierSingletonClass> supplier2 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
CharSequence instance2 = supplier2.get();
// 3-3
DisposableSupplier<StringForSupplierSingletonClass> supplier3 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
supplier3.get();
// 2-2
supplier1.dispose(null);
// 1-1
supplier2.dispose(null);
// 2-2
Supplier<StringForSupplierSingletonClass> supplier4 = injectionManager.getInstance(
DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE);
CharSequence result = supplier4.get();
assertEquals("2", result);
dispose(supplier3, supplier4);
}
@Test
public void testDisposePerLookupSupplier() {
// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class)
// .to(StringForSupplierClass.class));
// 1
DisposableSupplier<StringForSupplierClass> supplier1 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
CharSequence instance1 = supplier1.get();
// 1
DisposableSupplier<StringForSupplierClass> supplier2 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
CharSequence instance2 = supplier2.get();
// 1
DisposableSupplier<StringForSupplierClass> supplier3 =
injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
supplier3.get();
// 0
supplier1.dispose(null);
// 0
supplier2.dispose(null);
// 1
Supplier<StringForSupplierClass> supplier4 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE);
CharSequence result = supplier4.get();
assertEquals("1", result);
dispose(supplier3, supplier4);
}
@Test
public void testDisposeSingletonSupplierRequestScopedInstance() {
// BindingTestHelper.bind(injectionManager, binder -> {
// binder.bindFactory(ProxiableDisposableSingletonSupplierImpl.class, Singleton.class)
// .to(ProxiableHolderSingletonClass.class)
// .in(RequestScoped.class);
// });
RequestScope request = injectionManager.getInstance(RequestScope.class);
AtomicReference<Supplier<ProxiableHolderSingletonClass>> atomicSupplier = new AtomicReference<>();
request.runInScope(() -> {
// Save Singleton Supplier for later check that the instance was disposed.
Supplier<ProxiableHolderSingletonClass> supplier = injectionManager.getInstance(
new GenericType<DisposableSupplier<ProxiableHolderSingletonClass>>() {}.getType());
atomicSupplier.set(supplier);
// All instances should be the same because they are request scoped.
ProxiableHolderSingletonClass instance1 = injectionManager.getInstance(ProxiableHolderSingletonClass.class);
assertEquals("1", instance1.getValue());
ProxiableHolderSingletonClass instance2 = injectionManager.getInstance(ProxiableHolderSingletonClass.class);
assertEquals("1", instance2.getValue());
});
Supplier<ProxiableHolderSingletonClass> cleanedSupplier = atomicSupplier.get();
// Next should be 1-1
assertEquals("1", cleanedSupplier.get().getValue());
}
/**
* Tests that object created in request scope is disposing at the time of ending the scope.
*/
@Test
public void testDisposePerLookupSupplierRequestScopedInstance() {
// BindingTestHelper.bind(injectionManager, binder -> {
// binder.bindFactory(ProxiableDisposableSupplierImpl.class)
// .to(ProxiableHolderClass.class)
// .in(RequestScoped.class);
// });
RequestScope request = injectionManager.getInstance(RequestScope.class);
AtomicReference<Supplier<ProxiableHolderClass>> atomicSupplier = new AtomicReference<>();
request.runInScope(() -> {
// Save Singleton Supplier for later check that the instance was disposed.
Supplier<ProxiableHolderClass> supplier = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE);
atomicSupplier.set(supplier);
// All instances should be the same because they are request scoped.
ProxiableHolderClass instance1 = injectionManager.getInstance(ProxiableHolderClass.class);
assertEquals("1", instance1.getValue());
ProxiableHolderClass instance2 = injectionManager.getInstance(ProxiableHolderClass.class);
assertEquals("1", instance2.getValue());
});
Supplier<ProxiableHolderClass> cleanedSupplier = atomicSupplier.get();
// Next should be 1
assertEquals("1", cleanedSupplier.get().getValue());
}
/**
* Tests that inherited request scoped is also cleaned by disposing the objects.
*/
@Test
public void testDisposeSingletonSupplierMultiRequestScoped() {
// BindingTestHelper.bind(injectionManager, binder -> {
// binder.bindFactory(ProxiableDisposableSupplierImpl.class)
// .to(ProxiableHolderClass.class)
// .in(RequestScoped.class);
// });
RequestScope request = injectionManager.getInstance(RequestScope.class);
AtomicReference<Supplier<ProxiableHolderClass>> firstSupplier = new AtomicReference<>();
AtomicReference<Supplier<ProxiableHolderClass>> secondSupplier = new AtomicReference<>();
request.runInScope(() -> {
Supplier<ProxiableHolderClass> supplier1 = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE);
firstSupplier.set(supplier1);
ProxiableHolderClass instance1 = injectionManager.getInstance(ProxiableHolderClass.class);
assertEquals("1", instance1.getValue());
request.runInScope(() -> {
// Save Singleton Supplier for later check that the instance was disposed.
Supplier<ProxiableHolderClass> supplier2 = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE);
secondSupplier.set(supplier2);
ProxiableHolderClass instance2 = injectionManager.getInstance(ProxiableHolderClass.class);
// 1-2 because the same static class is used in inherited runInScope
assertEquals("1", instance2.getValue());
});
});
Supplier<ProxiableHolderClass> cleanedSupplier1 = firstSupplier.get();
Supplier<ProxiableHolderClass> cleanedSupplier2 = secondSupplier.get();
// Next should be 1-1
assertEquals("1", cleanedSupplier1.get().getValue());
// 1-2 because the same static class is used but the instance is cleaned.
assertEquals("1", cleanedSupplier2.get().getValue());
}
/**
* PerLookup fields are not disposed therefore they should never be used as a DisposedSupplier because the field stay in
* {@link org.glassfish.jersey.inject.weld.bean.SupplierClassBean} forever.
*/
@Test
public void testDisposeComposedObjectWithPerLookupFields() {
// BindingTestHelper.bind(injectionManager, binder -> {
// binder.bindFactory(DisposableSupplierForComposedImpl.class, Singleton.class)
// .to(StringForComposed.class);
//
// binder.bindAsContract(ComposedObject.class)
// .in(RequestScoped.class);
// });
RequestScope request = injectionManager.getInstance(RequestScope.class);
AtomicReference<Supplier<StringForComposed>> atomicSupplier = new AtomicReference<>();
request.runInScope(() -> {
// Save Singleton Supplier for later check that the instance was disposed.
Supplier<StringForComposed> supplier = injectionManager.getInstance(
new GenericType<DisposableSupplier<StringForComposed>>() {}.getType());
atomicSupplier.set(supplier);
// All instances should be the same because they are request scoped.
ComposedObject instance = injectionManager.getInstance(ComposedObject.class);
assertEquals("1", instance.getFirst().toString());
assertEquals("2", instance.getSecond().toString());
assertEquals("3", instance.getThird().toString());
});
Supplier<StringForComposed> cleanedSupplier = atomicSupplier.get();
// Next should be 1 - all instances are disposed and decremented back
assertEquals("1", cleanedSupplier.get().toString());
}
private void dispose(Object... objects) {
for (Object object : objects) {
if (DisposableSupplier.class.isInstance(object)) {
((DisposableSupplier) object).dispose(null);
}
}
}
@Vetoed
static class ComposedObject {
@Inject
StringForComposed first;
@Inject
StringForComposed second;
@Inject
StringForComposed third;
public StringForComposed getFirst() {
return first;
}
public StringForComposed getSecond() {
return second;
}
public StringForComposed getThird() {
return third;
}
}
@Vetoed
static class DisposableSupplierForComposedImpl implements DisposableSupplier<StringForComposed> {
private final AtomicInteger counter = new AtomicInteger();
@Override
public StringForComposed get() {
// Create a new string - don't share the instances in the string pool.
return new StringForComposed(counter.incrementAndGet() + "");
}
@Override
public void dispose(final StringForComposed instance) {
counter.decrementAndGet();
}
}
@Vetoed
static class DisposableSupplierImpl implements DisposableSupplier<String> {
private final AtomicInteger counter = new AtomicInteger();
@Override
public String get() {
// Create a new string - don't share the instances in the string pool.
return new String(counter.incrementAndGet() + "");
}
@Override
public void dispose(final String instance) {
counter.decrementAndGet();
}
}
@Vetoed
static class ProxiableDisposableSupplierImpl implements DisposableSupplier<AbstractProxiableHolder> {
private final AtomicInteger counter = new AtomicInteger();
@Override
public AbstractProxiableHolder get() {
// Create a new string - don't share the instances in the string pool.
return new ProxiableHolderClass(counter.incrementAndGet() + "");
}
@Override
public void dispose(AbstractProxiableHolder instance) {
counter.decrementAndGet();
}
}
@Vetoed
static class ProxiableDisposableSingletonSupplierImpl implements DisposableSupplier<AbstractProxiableHolder> {
private final AtomicInteger counter = new AtomicInteger();
@Override
public AbstractProxiableHolder get() {
// Create a new string - don't share the instances in the string pool.
return new ProxiableHolderSingletonClass(counter.incrementAndGet() + "");
}
@Override
public void dispose(AbstractProxiableHolder instance) {
counter.decrementAndGet();
}
}
@Vetoed
static class ProxiableHolderSingletonClass extends AbstractProxiableHolder {
public ProxiableHolderSingletonClass(String value) {
super(value);
}
}
@Vetoed
static class ProxiableHolderClass extends AbstractProxiableHolder {
public ProxiableHolderClass(String value) {
super(value);
}
}
@Vetoed
abstract static class AbstractProxiableHolder {
private String value;
public AbstractProxiableHolder(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@Vetoed
static final class GreetingsInstance extends ExtendableString {
public GreetingsInstance(CharSequence inner) {
super(inner);
}
}
@Vetoed
static final class GreetingsClass extends ExtendableString {
public GreetingsClass(CharSequence inner) {
super(inner);
}
}
@Vetoed
static final class StringForComposed extends ExtendableString {
public StringForComposed(CharSequence inner) {
super(inner);
}
}
@Vetoed
static final class StringForSupplierSingletonClass extends ExtendableString {
public StringForSupplierSingletonClass(CharSequence inner) {
super(inner);
}
}
@Vetoed
static final class StringForSupplierClass extends ExtendableString {
public StringForSupplierClass(CharSequence inner) {
super(inner);
}
}
@Vetoed
static final class StringForSupplierInstance extends ExtendableString {
public StringForSupplierInstance(CharSequence inner) {
super(inner);
}
}
static class ExtendableString implements CharSequence, Comparable<ExtendableString> {
private final CharSequence inner;
protected ExtendableString(CharSequence inner) {
this.inner = inner;
}
@Override
public int length() {
return inner.length();
}
@Override
public char charAt(int index) {
return inner.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return inner.subSequence(start, end);
}
@Override
public String toString() {
return inner.toString();
}
@Override
public int compareTo(ExtendableString o) {
if (this == o) return 0;
if (o == null) return -1;
return Objects.compare(inner.toString(), o.toString(), String.CASE_INSENSITIVE_ORDER);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
return Objects.equals(inner.toString(), inner.toString());
}
@Override
public int hashCode() {
return Objects.hash(inner);
}
}
}