Ejemplo n.º 1
0
    NavigableMap<URI, Bundle> stopBundles(Set<URI> bundles) {
      NavigableMap<URI, Bundle> expect, update;
      do {
        expect = get();
        update =
            ImmutableSortedMap.<URI, Bundle>naturalOrder()
                .putAll(Maps.filterKeys(expect, not(in(bundles))))
                .build();
      } while (!compareAndSet(expect, update));

      List<URI> couldNotStop = new ArrayList<>();
      NavigableMap<URI, Bundle> stopped = Maps.filterKeys(expect, in(bundles));
      for (Map.Entry<URI, Bundle> e : stopped.entrySet()) {
        URI bundleURI = e.getKey();
        Bundle bundle = e.getValue();
        try {
          bundle.stop();
        } catch (BundleException exc) {
          LOG.error("Failed to stop bundle " + bundleURI, exc);
          couldNotStop.add(bundleURI);
        }
      }
      if (!couldNotStop.isEmpty()) {
        throw new ModularException("Failed to stop bundles %s", couldNotStop);
      }
      return stopped;
    }
Ejemplo n.º 2
0
 ImmutableMap<Service, Long> startupTimes() {
   List<Entry<Service, Long>> loadTimes;
   monitor.enter();
   try {
     loadTimes = Lists.newArrayListWithCapacity(startupTimers.size());
     // N.B. There will only be an entry in the map if the service has started
     for (Entry<Service, Stopwatch> entry : startupTimers.entrySet()) {
       Service service = entry.getKey();
       Stopwatch stopWatch = entry.getValue();
       if (!stopWatch.isRunning() && !(service instanceof NoOpService)) {
         loadTimes.add(Maps.immutableEntry(service, stopWatch.elapsed(MILLISECONDS)));
       }
     }
   } finally {
     monitor.leave();
   }
   Collections.sort(
       loadTimes,
       Ordering.<Long>natural()
           .onResultOf(
               new Function<Entry<Service, Long>, Long>() {
                 @Override
                 public Long apply(Map.Entry<Service, Long> input) {
                   return input.getValue();
                 }
               }));
   ImmutableMap.Builder<Service, Long> builder = ImmutableMap.builder();
   for (Entry<Service, Long> entry : loadTimes) {
     builder.put(entry);
   }
   return builder.build();
 }
Ejemplo n.º 3
0
  private static Map<Integer, Long> findLocalSources(
      Collection<IndexSnapshotRequestConfig.PartitionRanges> partitionRanges, SiteTracker tracker) {
    Set<Integer> partitions = Sets.newHashSet();
    for (IndexSnapshotRequestConfig.PartitionRanges partitionRange : partitionRanges) {
      partitions.add(partitionRange.partitionId);
    }

    Map<Integer, Long> pidToLocalHSId = Maps.newHashMap();
    List<Long> localSites = Longs.asList(tracker.getLocalSites());

    for (long hsId : localSites) {
      int pid = tracker.getPartitionForSite(hsId);
      if (partitions.contains(pid)) {
        pidToLocalHSId.put(pid, hsId);
      }
    }

    return pidToLocalHSId;
  }
Ejemplo n.º 4
0
    Optional<Bundle> stopBundle(URI bundleURI) {
      NavigableMap<URI, Bundle> expect, update;
      do {
        expect = get();
        update =
            ImmutableSortedMap.<URI, Bundle>naturalOrder()
                .putAll(Maps.filterKeys(expect, not(equalTo(bundleURI))))
                .build();
      } while (expect.containsKey(bundleURI) && !compareAndSet(expect, update));

      Bundle bundle = expect.get(bundleURI);
      if (bundle != null) {
        try {
          bundle.stop();
        } catch (BundleException e) {
          throw loggedModularException(e, "Failed to stop bundle %s", bundleURI);
        }
      }
      return Optional.ofNullable(bundle);
    }
Ejemplo n.º 5
0
  /**
   * An encapsulation of all the mutable state of the {@link ServiceManager} that needs to be
   * accessed by instances of {@link ServiceListener}.
   */
  private static final class ServiceManagerState {
    final Monitor monitor = new Monitor();

    @GuardedBy("monitor")
    final SetMultimap<State, Service> servicesByState =
        Multimaps.newSetMultimap(
            new EnumMap<State, Collection<Service>>(State.class),
            new Supplier<Set<Service>>() {
              @Override
              public Set<Service> get() {
                return Sets.newLinkedHashSet();
              }
            });

    @GuardedBy("monitor")
    final Multiset<State> states = servicesByState.keys();

    @GuardedBy("monitor")
    final Map<Service, Stopwatch> startupTimers = Maps.newIdentityHashMap();

    /**
     * These two booleans are used to mark the state as ready to start. {@link #ready}: is set by
     * {@link #markReady} to indicate that all listeners have been correctly installed {@link
     * #transitioned}: is set by {@link #transitionService} to indicate that some transition has
     * been performed.
     *
     * <p>Together, they allow us to enforce that all services have their listeners installed prior
     * to any service performing a transition, then we can fail in the ServiceManager constructor
     * rather than in a Service.Listener callback.
     */
    @GuardedBy("monitor")
    boolean ready;

    @GuardedBy("monitor")
    boolean transitioned;

    final int numberOfServices;

    /**
     * Controls how long to wait for all the services to either become healthy or reach a state from
     * which it is guaranteed that it can never become healthy.
     */
    final Monitor.Guard awaitHealthGuard =
        new Monitor.Guard(monitor) {
          @Override
          public boolean isSatisfied() {
            // All services have started or some service has terminated/failed.
            return states.count(RUNNING) == numberOfServices
                || states.contains(STOPPING)
                || states.contains(TERMINATED)
                || states.contains(FAILED);
          }
        };

    /** Controls how long to wait for all services to reach a terminal state. */
    final Monitor.Guard stoppedGuard =
        new Monitor.Guard(monitor) {
          @Override
          public boolean isSatisfied() {
            return states.count(TERMINATED) + states.count(FAILED) == numberOfServices;
          }
        };

    /** The listeners to notify during a state transition. */
    @GuardedBy("monitor")
    final List<ListenerCallQueue<Listener>> listeners =
        Collections.synchronizedList(new ArrayList<ListenerCallQueue<Listener>>());

    /**
     * It is implicitly assumed that all the services are NEW and that they will all remain NEW
     * until all the Listeners are installed and {@link #markReady()} is called. It is our caller's
     * responsibility to only call {@link #markReady()} if all services were new at the time this
     * method was called and when all the listeners were installed.
     */
    ServiceManagerState(ImmutableCollection<Service> services) {
      this.numberOfServices = services.size();
      servicesByState.putAll(NEW, services);
    }

    /**
     * Attempts to start the timer immediately prior to the service being started via {@link
     * Service#startAsync()}.
     */
    void tryStartTiming(Service service) {
      monitor.enter();
      try {
        Stopwatch stopwatch = startupTimers.get(service);
        if (stopwatch == null) {
          startupTimers.put(service, Stopwatch.createStarted());
        }
      } finally {
        monitor.leave();
      }
    }

    /**
     * Marks the {@link State} as ready to receive transitions. Returns true if no transitions have
     * been observed yet.
     */
    void markReady() {
      monitor.enter();
      try {
        if (!transitioned) {
          // nothing has transitioned since construction, good.
          ready = true;
        } else {
          // This should be an extremely rare race condition.
          List<Service> servicesInBadStates = Lists.newArrayList();
          for (Service service : servicesByState().values()) {
            if (service.state() != NEW) {
              servicesInBadStates.add(service);
            }
          }
          throw new IllegalArgumentException(
              "Services started transitioning asynchronously before "
                  + "the ServiceManager was constructed: "
                  + servicesInBadStates);
        }
      } finally {
        monitor.leave();
      }
    }

    void addListener(Listener listener, Executor executor) {
      checkNotNull(listener, "listener");
      checkNotNull(executor, "executor");
      monitor.enter();
      try {
        // no point in adding a listener that will never be called
        if (!stoppedGuard.isSatisfied()) {
          listeners.add(new ListenerCallQueue<Listener>(listener, executor));
        }
      } finally {
        monitor.leave();
      }
    }

    void awaitHealthy() {
      monitor.enterWhenUninterruptibly(awaitHealthGuard);
      try {
        checkHealthy();
      } finally {
        monitor.leave();
      }
    }

    void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException {
      monitor.enter();
      try {
        if (!monitor.waitForUninterruptibly(awaitHealthGuard, timeout, unit)) {
          throw new TimeoutException(
              "Timeout waiting for the services to become healthy. The "
                  + "following services have not started: "
                  + Multimaps.filterKeys(servicesByState, in(ImmutableSet.of(NEW, STARTING))));
        }
        checkHealthy();
      } finally {
        monitor.leave();
      }
    }

    void awaitStopped() {
      monitor.enterWhenUninterruptibly(stoppedGuard);
      monitor.leave();
    }

    void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException {
      monitor.enter();
      try {
        if (!monitor.waitForUninterruptibly(stoppedGuard, timeout, unit)) {
          throw new TimeoutException(
              "Timeout waiting for the services to stop. The following "
                  + "services have not stopped: "
                  + Multimaps.filterKeys(
                      servicesByState, not(in(ImmutableSet.of(TERMINATED, FAILED)))));
        }
      } finally {
        monitor.leave();
      }
    }

    ImmutableMultimap<State, Service> servicesByState() {
      ImmutableSetMultimap.Builder<State, Service> builder = ImmutableSetMultimap.builder();
      monitor.enter();
      try {
        for (Entry<State, Service> entry : servicesByState.entries()) {
          if (!(entry.getValue() instanceof NoOpService)) {
            builder.put(entry.getKey(), entry.getValue());
          }
        }
      } finally {
        monitor.leave();
      }
      return builder.build();
    }

    ImmutableMap<Service, Long> startupTimes() {
      List<Entry<Service, Long>> loadTimes;
      monitor.enter();
      try {
        loadTimes = Lists.newArrayListWithCapacity(startupTimers.size());
        // N.B. There will only be an entry in the map if the service has started
        for (Entry<Service, Stopwatch> entry : startupTimers.entrySet()) {
          Service service = entry.getKey();
          Stopwatch stopWatch = entry.getValue();
          if (!stopWatch.isRunning() && !(service instanceof NoOpService)) {
            loadTimes.add(Maps.immutableEntry(service, stopWatch.elapsed(MILLISECONDS)));
          }
        }
      } finally {
        monitor.leave();
      }
      Collections.sort(
          loadTimes,
          Ordering.<Long>natural()
              .onResultOf(
                  new Function<Entry<Service, Long>, Long>() {
                    @Override
                    public Long apply(Map.Entry<Service, Long> input) {
                      return input.getValue();
                    }
                  }));
      ImmutableMap.Builder<Service, Long> builder = ImmutableMap.builder();
      for (Entry<Service, Long> entry : loadTimes) {
        builder.put(entry);
      }
      return builder.build();
    }

    /**
     * Updates the state with the given service transition.
     *
     * <p>This method performs the main logic of ServiceManager in the following steps.
     *
     * <ol>
     *   <li>Update the {@link #servicesByState()}
     *   <li>Update the {@link #startupTimers}
     *   <li>Based on the new state queue listeners to run
     *   <li>Run the listeners (outside of the lock)
     * </ol>
     */
    void transitionService(final Service service, State from, State to) {
      checkNotNull(service);
      checkArgument(from != to);
      monitor.enter();
      try {
        transitioned = true;
        if (!ready) {
          return;
        }
        // Update state.
        checkState(
            servicesByState.remove(from, service),
            "Service %s not at the expected location in the state map %s",
            service,
            from);
        checkState(
            servicesByState.put(to, service),
            "Service %s in the state map unexpectedly at %s",
            service,
            to);
        // Update the timer
        Stopwatch stopwatch = startupTimers.get(service);
        if (stopwatch == null) {
          // This means the service was started by some means other than ServiceManager.startAsync
          stopwatch = Stopwatch.createStarted();
          startupTimers.put(service, stopwatch);
        }
        if (to.compareTo(RUNNING) >= 0 && stopwatch.isRunning()) {
          // N.B. if we miss the STARTING event then we may never record a startup time.
          stopwatch.stop();
          if (!(service instanceof NoOpService)) {
            logger.log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch});
          }
        }
        // Queue our listeners

        // Did a service fail?
        if (to == FAILED) {
          fireFailedListeners(service);
        }

        if (states.count(RUNNING) == numberOfServices) {
          // This means that the manager is currently healthy. N.B. If other threads call isHealthy
          // they are not guaranteed to get 'true', because any service could fail right now.
          fireHealthyListeners();
        } else if (states.count(TERMINATED) + states.count(FAILED) == numberOfServices) {
          fireStoppedListeners();
        }
      } finally {
        monitor.leave();
        // Run our executors outside of the lock
        executeListeners();
      }
    }

    @GuardedBy("monitor")
    void fireStoppedListeners() {
      STOPPED_CALLBACK.enqueueOn(listeners);
    }

    @GuardedBy("monitor")
    void fireHealthyListeners() {
      HEALTHY_CALLBACK.enqueueOn(listeners);
    }

    @GuardedBy("monitor")
    void fireFailedListeners(final Service service) {
      new Callback<Listener>("failed({service=" + service + "})") {
        @Override
        void call(Listener listener) {
          listener.failure(service);
        }
      }.enqueueOn(listeners);
    }

    /** Attempts to execute all the listeners in {@link #listeners}. */
    void executeListeners() {
      checkState(
          !monitor.isOccupiedByCurrentThread(),
          "It is incorrect to execute listeners with the monitor held.");
      // iterate by index to avoid concurrent modification exceptions
      for (int i = 0; i < listeners.size(); i++) {
        listeners.get(i).execute();
      }
    }

    @GuardedBy("monitor")
    void checkHealthy() {
      if (states.count(RUNNING) != numberOfServices) {
        IllegalStateException exception =
            new IllegalStateException(
                "Expected to be healthy after starting. The following services are not running: "
                    + Multimaps.filterKeys(servicesByState, not(equalTo(RUNNING))));
        throw exception;
      }
    }
  }
Ejemplo n.º 6
0
 /**
  * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in
  * {@code actual}.
  *
  * <p>For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code
  * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain
  * #resolveType resolve} {@code ParameterizedType List<T>} to {@code List<String>}, and resolve
  * {@code Map<T, Something>} to {@code Map<String, Something>} etc. Similarly, {@code formal} and
  * {@code actual} can be {@code Map<K, V>} and {@code Map<String, Integer>} respectively, or they
  * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination
  * thereof.
  *
  * @param formal The type whose type variables or itself is mapped to other type(s). It's almost
  *     always a bug if {@code formal} isn't a type variable and contains no type variable. Make
  *     sure you are passing the two parameters in the right order.
  * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet
  *     other type variables, in which case these type variables will be further resolved if
  *     corresponding mappings exist in the current {@code TypeResolver} instance.
  */
 public TypeResolver where(Type formal, Type actual) {
   Map<TypeVariable<?>, Type> mappings = Maps.newHashMap();
   populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual));
   return where(mappings);
 }
Ejemplo n.º 7
0
  private static final class TypeMappingIntrospector extends TypeVisitor {

    private static final WildcardCapturer wildcardCapturer = new WildcardCapturer();

    private final Map<TypeVariable<?>, Type> mappings = Maps.newHashMap();

    /**
     * Returns type mappings using type parameters and type arguments found in the generic
     * superclass and the super interfaces of {@code contextClass}.
     */
    static ImmutableMap<TypeVariable<?>, Type> getTypeMappings(Type contextType) {
      TypeMappingIntrospector introspector = new TypeMappingIntrospector();
      introspector.visit(wildcardCapturer.capture(contextType));
      return ImmutableMap.copyOf(introspector.mappings);
    }

    @Override
    void visitClass(Class<?> clazz) {
      visit(clazz.getGenericSuperclass());
      visit(clazz.getGenericInterfaces());
    }

    @Override
    void visitParameterizedType(ParameterizedType parameterizedType) {
      Class<?> rawClass = (Class<?>) parameterizedType.getRawType();
      TypeVariable<?>[] vars = rawClass.getTypeParameters();
      Type[] typeArgs = parameterizedType.getActualTypeArguments();
      checkState(vars.length == typeArgs.length);
      for (int i = 0; i < vars.length; i++) {
        map(vars[i], typeArgs[i]);
      }
      visit(rawClass);
      visit(parameterizedType.getOwnerType());
    }

    @Override
    void visitTypeVariable(TypeVariable<?> t) {
      visit(t.getBounds());
    }

    @Override
    void visitWildcardType(WildcardType t) {
      visit(t.getUpperBounds());
    }

    private void map(final TypeVariable<?> var, final Type arg) {
      if (mappings.containsKey(var)) {
        // Mapping already established
        // This is possible when following both superClass -> enclosingClass
        // and enclosingclass -> superClass paths.
        // Since we follow the path of superclass first, enclosing second,
        // superclass mapping should take precedence.
        return;
      }
      // First, check whether var -> arg forms a cycle
      for (Type t = arg; t != null; t = mappings.get(t)) {
        if (var.equals(t)) {
          // cycle detected, remove the entire cycle from the mapping so that
          // each type variable resolves deterministically to itself.
          // Otherwise, a F -> T cycle will end up resolving both F and T
          // nondeterministically to either F or T.
          for (Type x = arg; x != null; x = mappings.remove(x)) {}
          return;
        }
      }
      mappings.put(var, arg);
    }
  }