package sk.stuba.fiit.perconik.core.services; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import com.google.common.base.Stopwatch; import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Service.State; import sk.stuba.fiit.perconik.utilities.concurrent.TimeValue; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Lists.asList; import static com.google.common.collect.Lists.newArrayList; /** * An immutable set of {@code Service} instances user-specified * iteration order. Does not permit {@code null} elements. * * @param <S> the common supertype that all services must share * * @author Pavol Zbell * @since 1.0 */ public final class ServiceGroup<S extends Service> extends ForwardingSet<S> { // TODO remove and replace it with ServiceManager or use ServiceManager internally private final Set<S> services; ServiceGroup(final Iterable<S> services) { this.services = ImmutableSet.copyOf(services); } /** * Returns the empty service group. */ public static <S extends Service> ServiceGroup<S> of() { return new ServiceGroup<>(ImmutableSet.<S>of()); } /** * Returns a service group containing a single service. */ public static <S extends Service> ServiceGroup<S> of(final S service) { return new ServiceGroup<>(ImmutableSet.of(service)); } /** * Returns a service group containing the given services, in order. * Repeated occurrences of a service (according to {@link Object#equals}) * after the first are ignored. * @throws NullPointerException if any service is {@code null} */ public static <S extends Service> ServiceGroup<S> of(final S first, final S second) { return new ServiceGroup<>(ImmutableSet.of(first, second)); } /** * Returns a service group containing the given services, in order. * Repeated occurrences of a service (according to {@link Object#equals}) * after the first are ignored. * @throws NullPointerException if any service is {@code null} */ @SafeVarargs public static <S extends Service> ServiceGroup<S> of(final S first, final S second, final S ... rest) { return new ServiceGroup<>(asList(first, second, rest)); } @Override protected Set<S> delegate() { return this.services; } public void startSynchronously() { for (S service: this.services) { service.startAsync(); service.awaitRunning(); } } public void startSynchronously(final long timeout, final TimeUnit unit) throws TimeoutException { long slice = NANOSECONDS.convert(timeout, unit); Stopwatch stopwatch = Stopwatch.createStarted(); for (S service: this.services) { service.startAsync(); service.awaitRunning(slice, NANOSECONDS); slice -= stopwatch.elapsed(NANOSECONDS); if (slice < 0) { throw new TimeoutException(); } } } public void startSynchronously(final TimeValue timeout) throws TimeoutException { startSynchronously(timeout.duration(), timeout.unit()); } public void stopSynchronously() { for (S service: this.services) { service.stopAsync(); service.awaitTerminated(); } } public void stopSynchronously(final long timeout, final TimeUnit unit) throws TimeoutException { long slice = NANOSECONDS.convert(timeout, unit); Stopwatch stopwatch = Stopwatch.createStarted(); for (S service: this.services) { service.stopAsync(); service.awaitTerminated(slice, NANOSECONDS); slice -= stopwatch.elapsed(NANOSECONDS); if (slice < 0) { throw new TimeoutException(); } } } public void stopSynchronously(final TimeValue timeout) throws TimeoutException { stopSynchronously(timeout.duration(), timeout.unit()); } public Map<S, State> states() { ImmutableMap.Builder<S, State> builder = ImmutableMap.builder(); for (S service: this.services) { builder.put(service, service.state()); } return builder.build(); } public <U extends S> ServiceGroup<U> narrow(final Class<U> type) { ImmutableSet.Builder<U> builder = ImmutableSet.builder(); for (S service: this.services) { if (type.isInstance(service)) { builder.add(type.cast(service)); } } return new ServiceGroup<>(builder.build()); } public <U extends S> U fetch(final Class<U> type) { Iterator<U> iterator = this.narrow(type).iterator(); U service = iterator.next(); checkArgument(!iterator.hasNext()); return service; } public ServiceGroup<S> reverse() { return new ServiceGroup<>(Lists.reverse(newArrayList(this.services))); } }