package netflix.ocelli; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import rx.Observable; import rx.Subscriber; import rx.subjects.PublishSubject; /** * InstanceSubject can be used as a basic bridge from an add/remove host membership * paradigm to Ocelli's internal Instance with lifecycle representation of entity * membership in the load balancer. * * @see LoadBalancer * * @author elandau */ public class InstanceManager<T> extends Observable<Instance<T>> { private final PublishSubject<Instance<T>> subject; private final ConcurrentMap<T, CloseableInstance<T>> instances; public static <T> InstanceManager<T> create() { return new InstanceManager<T>(); } public InstanceManager() { this(PublishSubject.<Instance<T>>create(), new ConcurrentHashMap<T, CloseableInstance<T>>()); } private InstanceManager(final PublishSubject<Instance<T>> subject, final ConcurrentMap<T, CloseableInstance<T>> instances) { super(new OnSubscribe<Instance<T>>() { @Override public void call(Subscriber<? super Instance<T>> s) { // TODO: This is a very naive implementation that may have race conditions // whereby instances may be dropped Observable .from(new ArrayList<Instance<T>>(instances.values())) .concatWith(subject).subscribe(s); } }); this.subject = subject; this.instances = instances; } /** * Add an entity to the source, which feeds into a load balancer * @param t * @return */ public CloseableInstance<T> add(T t) { CloseableInstance<T> member = CloseableInstance.from(t); CloseableInstance<T> existing = instances.putIfAbsent(t, member); if (null == existing) { subject.onNext(member); return member; } return existing; } /** * Remove an entity from the source. If the entity exists it's lifecycle will * onComplete. * * @param t * @return */ public CloseableInstance<T> remove(T t) { CloseableInstance<T> member = instances.remove(t); if (member != null) { member.close(); } return member; } }