package com.github.davidmoten.rx; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import com.github.davidmoten.util.Preconditions; import rx.Observable; import rx.functions.Action1; import rx.functions.Func0; import rx.functions.Func1; import rx.plugins.RxJavaHooks; //TODO as suggested by George Campbell, may replace with his PR public class ResourceManager<T> { private final Func0<T> resourceFactory; private final Action1<? super T> disposeAction; private final boolean disposeEagerly; protected ResourceManager(Func0<T> resourceFactory, Action1<? super T> disposeAction, boolean disposeEagerly) { Preconditions.checkNotNull(resourceFactory); Preconditions.checkNotNull(disposeAction); this.resourceFactory = resourceFactory; this.disposeAction = disposeAction; this.disposeEagerly = disposeEagerly; } public static <T> ResourceManagerBuilder<T> resourceFactory(Func0<T> resourceFactory) { return new ResourceManagerBuilder<T>(resourceFactory); } public static <T extends Closeable> CloseableResourceManagerBuilder<T> closeableResourceFactory( Func0<T> resourceFactory) { return new CloseableResourceManagerBuilder<T>(resourceFactory); } public final static class ResourceManagerBuilder<T> { private final Func0<T> resourceFactory; private boolean disposeEagerly = false; private ResourceManagerBuilder(Func0<T> resourceFactory) { this.resourceFactory = resourceFactory; } public ResourceManagerBuilder<T> disposeEagerly(boolean value) { this.disposeEagerly = value; return this; } public ResourceManager<T> disposeAction(Action1<? super T> disposeAction) { return new ResourceManager<T>(resourceFactory, disposeAction, disposeEagerly); } } public final static class CloseableResourceManagerBuilder<T extends Closeable> { private final Func0<T> resourceFactory; private boolean disposeEagerly = false; private CloseableResourceManagerBuilder(Func0<T> resourceFactory) { this.resourceFactory = resourceFactory; } public CloseableResourceManagerBuilder<T> disposeEagerly(boolean value) { this.disposeEagerly = value; return this; } public ResourceManager<T> create() { return new ResourceManager<T>(resourceFactory, CloserHolder.INSTANCE, disposeEagerly); } } public static <T> ResourceManager<T> create(Func0<T> resourceFactory, Action1<? super T> disposeAction) { return new ResourceManager<T>(resourceFactory, disposeAction, false); } public static <T> ResourceManager<T> create(Callable<T> resourceFactory, Action1<? super T> disposeAction) { return new ResourceManager<T>(Functions.toFunc0(resourceFactory), disposeAction, false); } public static <T> ResourceManager<T> create(Callable<T> resourceFactory, Checked.A1<? super T> disposeAction) { return new ResourceManager<T>(Functions.toFunc0(resourceFactory), Checked.a1(disposeAction), false); } public static <T extends Closeable> ResourceManager<T> create(Func0<T> resourceFactory) { return create(resourceFactory, CloserHolder.INSTANCE); } public static <T extends Closeable> ResourceManager<T> create(Callable<T> resourceFactory) { return create(Functions.toFunc0(resourceFactory), CloserHolder.INSTANCE); } public <R> Observable<R> observable( Func1<? super T, ? extends Observable<? extends R>> observableFactory) { return Observable.using(resourceFactory, observableFactory, disposeAction, disposeEagerly); } public <R> ResourceManager<R> map(final Checked.F1<? super T, ? extends R> resourceMapper, final Checked.A1<? super R> disposeAction) { return map(Checked.f1(resourceMapper), Checked.a1(disposeAction)); } public <R> ResourceManager<R> map(final Func1<? super T, ? extends R> resourceMapper, final Action1<? super R> disposeAction) { final AtomicReference<T> ref = new AtomicReference<T>(); Func0<R> rf = new Func0<R>() { @Override public R call() { T a = resourceFactory.call(); try { R b = resourceMapper.call(a); ref.set(a); return b; } catch (Throwable e) { ResourceManager.this.disposeAction.call(a); if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } } }; Action1<R> disposer = new Action1<R>() { @Override public void call(R r) { try { disposeAction.call(r); } catch (Throwable e) { RxJavaHooks.onError(e); } try { ResourceManager.this.disposeAction.call(ref.get()); } catch (Throwable e) { RxJavaHooks.onError(e); } } }; return create(rf, disposer); } public <R extends Closeable> ResourceManager<R> map( final Func1<? super T, ? extends R> resourceMapper) { return map(resourceMapper, CloserHolder.INSTANCE); } private static final class CloserHolder { static final Action1<Closeable> INSTANCE = new Action1<Closeable>() { @Override public void call(Closeable c) { try { c.close(); } catch (IOException e) { RxJavaHooks.onError(e); } } }; } }