Skip to content

Commit

Permalink
[guice] Adding CloseableSingleton scope
Browse files Browse the repository at this point in the history
  • Loading branch information
denis-itskovich committed Feb 4, 2020
1 parent bbc9abb commit da998f3
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.slimgears.util.guice;

import javax.inject.Scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CloseableSingleton {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.slimgears.util.guice;

import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.slimgears.util.stream.Optionals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;
import java.util.Stack;

public class CloseableSingletonScope extends AbstractScope implements AutoCloseable {
private final Logger log = LoggerFactory.getLogger(CloseableSingletonScope.class);
private final ObjectStorage objectStorage = new ObjectStorage();
private final Stack<AutoCloseable> closeables = new Stack<>();

public static void install(Binder binder) {
CloseableSingletonScope scope = new CloseableSingletonScope();
binder.bind(CloseableSingletonScope.class).toInstance(scope);
binder.bindScope(CloseableSingleton.class, scope);
}

private CloseableSingletonScope() {
objectStorage.subscribe(new ObjectStorage.InstantiationListener() {
@Override
public <T> void onInstantiated(Key<T> key, T instance) {
Optional.ofNullable(instance)
.flatMap(Optionals.ofType(AutoCloseable.class))
.ifPresent(closeables::push);
}
});
}

@Override
protected <T> T provide(Key<T> key, Provider<T> unscopedProvider) {
return objectStorage.get(key, unscopedProvider);
}

@Override
public void close() {
while (!closeables.empty()) {
AutoCloseable closeable = closeables.pop();
try {
closeable.close();
} catch (Exception e) {
log.warn("Exception occurred when trying to close {}: ", closeable.getClass().getSimpleName(), e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.slimgears.util.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;

public class CloseableSingletonTest {
@CloseableSingleton
static class DummyCloseableClass implements AutoCloseable {
private final AtomicBoolean closed;

@Inject
public DummyCloseableClass(AtomicBoolean closed) {
this.closed = closed;
}

public boolean isClosed() {
return this.closed.get();
}

@Override
public void close() throws Exception {
closed.set(true);
}
}

@CloseableSingleton
static class DummyNonCloseableClass {

}

@Test
public void testCloseableSingleton() {
AtomicBoolean wasClosed = new AtomicBoolean();
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
CloseableSingletonScope.install(binder());
bind(AtomicBoolean.class).toInstance(wasClosed);
}
});

DummyCloseableClass dummyCloseableClass = injector.getInstance(DummyCloseableClass.class);
DummyNonCloseableClass dummyNonCloseableClass = injector.getInstance(DummyNonCloseableClass.class);
CloseableSingletonScope closeableSingletonScope = injector.getInstance(CloseableSingletonScope.class);

Assert.assertSame(dummyNonCloseableClass, injector.getInstance(DummyNonCloseableClass.class));
Assert.assertSame(dummyCloseableClass, injector.getInstance(DummyCloseableClass.class));

Assert.assertFalse(dummyCloseableClass.isClosed());
closeableSingletonScope.close();

Assert.assertTrue(wasClosed.get());
Assert.assertTrue(dummyCloseableClass.isClosed());

}
}

0 comments on commit da998f3

Please sign in to comment.