package netflix.ocelli; import rx.Observable; import rx.Observable.Transformer; import rx.functions.Func1; import rx.functions.Func2; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Utility class to convert a full snapshot of pool members, T, to an Observable<Instance<T>> * by diffing the new list with the last list. Any {@link Instance} that has been removed * will have it's lifecycle terminated. * * @author elandau * * @param <T> */ public class SnapshotToInstance<T> implements Transformer<List<T>, Instance<T>> { public class State { final Map<T, CloseableInstance<T>> members; final List<Instance<T>> newInstances; public State() { members = new HashMap<T, CloseableInstance<T>>(); newInstances = Collections.emptyList(); } public State(State toCopy) { members = new HashMap<T, CloseableInstance<T>>(toCopy.members); newInstances = new ArrayList<>(); } } @Override public Observable<Instance<T>> call(Observable<List<T>> snapshots) { return snapshots.scan(new State(), new Func2<State, List<T>, State>() { @Override public State call(State state, List<T> instances) { State newState = new State(state); Set<T> keysToRemove = new HashSet<T>(newState.members.keySet()); for (T ii : instances) { keysToRemove.remove(ii); if (!newState.members.containsKey(ii)) { CloseableInstance<T> member = CloseableInstance.from(ii); newState.members.put(ii, member); newState.newInstances.add(member); } } for (T tKey : keysToRemove) { CloseableInstance<T> removed = newState.members.remove(tKey); if (null != removed) { removed.close(); } } return newState; } }).concatMap(new Func1<State, Observable<Instance<T>>>() { @Override public Observable<Instance<T>> call(State state) { return Observable.from(state.newInstances); } }); } }