Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2009 The Guava Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.google.common.util.concurrent;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.util.concurrent.Service.State.FAILED;
 import static com.google.common.util.concurrent.Service.State.NEW;
 import static com.google.common.util.concurrent.Service.State.RUNNING;
 import static com.google.common.util.concurrent.Service.State.STARTING;
 import static com.google.common.util.concurrent.Service.State.STOPPING;
 import static com.google.common.util.concurrent.Service.State.TERMINATED;
 
 import com.google.common.util.concurrent.Service.State// javadoc needs this
 
 import java.util.List;
Base class for implementing services that can handle doStart() and doStop() requests, responding to them with notifyStarted() and notifyStopped() callbacks. Its subclasses must manage threads manually; consider AbstractExecutionThreadService if you need only a single execution thread.

Author(s):
Jesse Wilson
Luke Sandberg
Since:
1.0
 
 public abstract class AbstractService implements Service {
   private final Monitor monitor = new Monitor();
 
   private final Transition startup = new Transition();
   private final Transition shutdown = new Transition();
 
   private final Guard isStartable = new Guard() {
     @Override public boolean isSatisfied() {
       return state() == ;
     }
   };
 
   private final Guard isStoppable = new Guard() {
     @Override public boolean isSatisfied() {
       return state().compareTo() <= 0;
     }
   };
 
   private final Guard hasReachedRunning = new Guard() {
     @Override public boolean isSatisfied() {
       return state().compareTo() >= 0;
     }
   };
 
   private final Guard isStopped = new Guard() {
     @Override public boolean isSatisfied() {
       return state().isTerminal();
     }
   };

  
The listeners to notify during a state transition.
 
   @GuardedBy("monitor")
   private final List<ListenerExecutorPairlisteners = Lists.newArrayList();

  
The queue of listeners that are waiting to be executed.

Enqueue operations should be protected by monitor while calling ExecutionQueue.execute() should not be protected.

 
   private final ExecutionQueue queuedListeners = new ExecutionQueue();

  
The current state of the service. This should be written with the lock held but can be read without it because it is an immutable object in a volatile field. This is desirable so that methods like state(), failureCause() and notably toString() can be run without grabbing the lock.

To update this field correctly the lock must be held to guarantee that the state is consistent.

  @GuardedBy("monitor")
  private volatile StateSnapshot snapshot = new StateSnapshot();

  
Constructor for use by subclasses.
  protected AbstractService() {
    // Add a listener to update the futures. This needs to be added first so that it is executed
    // before the other listeners. This way the other listeners can access the completed futures.
        new Listener() {
          @Override public void running() {
            .set();
          }
          @Override public void stopping(State from) {
            if (from == ) {
              .set();
            }
          }
          @Override public void terminated(State from) {
            if (from == ) {
              .set();
            }
            .set();
          }
          @Override public void failed(State fromThrowable failure) {
            switch (from) {
              case :
                .setException(failure);
                .setException(new Exception("Service failed to start."failure));
                break;
              case :
                .setException(new Exception("Service failed while running"failure));
                break;
              case :
                .setException(failure);
                break;
              case :  /* fall-through */
              case :  /* fall-through */
              case :  /* fall-through */
              default:
                throw new AssertionError("Unexpected from state: " + from);
            }
          }
        },
        MoreExecutors.sameThreadExecutor());
  }

  
This method is called by start() to initiate service startup. The invocation of this method should cause a call to notifyStarted(), either during this method's run, or after it has returned. If startup fails, the invocation should cause a call to notifyFailed(java.lang.Throwable) instead.

This method should return promptly; prefer to do work on a different thread where it is convenient. It is invoked exactly once on service startup, even when start() is called multiple times.

  protected abstract void doStart();

  
This method should be used to initiate service shutdown. The invocation of this method should cause a call to notifyStopped(), either during this method's run, or after it has returned. If shutdown fails, the invocation should cause a call to notifyFailed(java.lang.Throwable) instead.

This method should return promptly; prefer to do work on a different thread where it is convenient. It is invoked exactly once on service shutdown, even when stop() is called multiple times.

  protected abstract void doStop();
  @Override public final Service startAsync() {
    if (.enterIf()) {
      try {
         = new StateSnapshot();
        starting();
        doStart();
       // TODO(user): justify why we are catching Throwable and not RuntimeException
      } catch (Throwable startupFailure) {
        notifyFailed(startupFailure);
      } finally {
        .leave();
        executeListeners();
      }
    } else {
      throw new IllegalStateException("Service " + this + " has already been started");
    }
    return this;
  }
  public final ListenableFuture<Statestart() {
    if (.enterIf()) {
      try {
         = new StateSnapshot();
        starting();
        doStart();
      } catch (Throwable startupFailure) {
        notifyFailed(startupFailure);
      } finally {
        .leave();
        executeListeners();
      }
    }
    return ;
  }
  @Override public final Service stopAsync() {
    stop();
    return this;
  }
  public final ListenableFuture<Statestop() {
    if (.enterIf()) {
      try {
        State previous = state();
        switch (previous) {
          case :
             = new StateSnapshot();
            terminated();
            break;
          case :
             = new StateSnapshot(truenull);
            stopping();
            break;
          case :
             = new StateSnapshot();
            stopping();
            doStop();
            break;
          case :
          case :
          case :
            // These cases are impossible due to the if statement above.
            throw new AssertionError("isStoppable is incorrectly implemented, saw: " + previous);
          default:
            throw new AssertionError("Unexpected state: " + previous);
        }
        // TODO(user): justify why we are catching Throwable and not RuntimeException.  Also, we
        // may inadvertently catch our AssertionErrors.
      } catch (Throwable shutdownFailure) {
        notifyFailed(shutdownFailure);
      } finally {
        .leave();
        executeListeners();
      }
    }
    return ;
  }
  public State startAndWait() {
    return Futures.getUnchecked(start());
  }
  public State stopAndWait() {
    return Futures.getUnchecked(stop());
  }
  @Override public final void awaitRunning() {
    try {
    } finally {
      .leave();
    }
  }
  @Override public final void awaitRunning(long timeoutTimeUnit unitthrows TimeoutException {
    if (.enterWhenUninterruptibly(timeoutunit)) {
      try {
        checkCurrentState();
      } finally {
        .leave();
      }
    } else {
      // It is possible due to races the we are currently in the expected state even though we
      // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never
      // even check the guard.  I don't think we care too much about this use case but it could lead
      // to a confusing error message.
      throw new TimeoutException("Timed out waiting for " + this + " to reach the RUNNING state. "
          + "Current state: " + state());
    }
  }
  @Override public final void awaitTerminated() {
    try {
    } finally {
      .leave();
    }
  }
  @Override public final void awaitTerminated(long timeoutTimeUnit unitthrows TimeoutException {
    if (.enterWhenUninterruptibly(timeoutunit)) {
      try {
        State state = state();
      } finally {
        .leave();
      }
    } else {
      // It is possible due to races the we are currently in the expected state even though we
      // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never
      // even check the guard.  I don't think we care too much about this use case but it could lead
      // to a confusing error message.
      throw new TimeoutException("Timed out waiting for " + this + " to reach a terminal state. "
          + "Current state: " + state());
    }
  }

  
Checks that the current state is equal to the expected state.
  @GuardedBy("monitor")
  private void checkCurrentState(State expected) {
    State actual = state();
    if (actual != expected) {
      if (actual == ) {
        // Handle this specially so that we can include the failureCause, if there is one.
        throw new IllegalStateException("Expected the service to be " + expected
            + ", but the service has FAILED"failureCause());
      }
      throw new IllegalStateException("Expected the service to be " + expected + ", but was "
          + actual);
    }
  }

  
Implementing classes should invoke this method once their service has started. It will cause the service to transition from Service.State.STARTING to Service.State.RUNNING.

  protected final void notifyStarted() {
    .enter();
    try {
      // We have to examine the internal state of the snapshot here to properly handle the stop
      // while starting case.
      if (. != ) {
        IllegalStateException failure = new IllegalStateException(
            "Cannot notifyStarted() when the service is " + .);
        notifyFailed(failure);
        throw failure;
      }
         = new StateSnapshot();
        // We don't call listeners here because we already did that when we set the
        // shutdownWhenStartupFinishes flag.
        doStop();
      } else {
         = new StateSnapshot();
        running();
      }
    } finally {
      .leave();
      executeListeners();
    }
  }

  
Implementing classes should invoke this method once their service has stopped. It will cause the service to transition from Service.State.STOPPING to Service.State.TERMINATED.

  protected final void notifyStopped() {
    .enter();
    try {
      // We check the internal state of the snapshot instead of state() directly so we don't allow
      // notifyStopped() to be called while STARTING, even if stop() has already been called.
      State previous = .;
      if (previous !=  && previous != ) {
        IllegalStateException failure = new IllegalStateException(
            "Cannot notifyStopped() when the service is " + previous);
        notifyFailed(failure);
        throw failure;
      }
       = new StateSnapshot();
      terminated(previous);
    } finally {
      .leave();
      executeListeners();
    }
  }

  
Invoke this method to transition the service to the Service.State.FAILED. The service will not be stopped if it is running. Invoke this method when a service has failed critically or otherwise cannot be started nor stopped.
  protected final void notifyFailed(Throwable cause) {
    checkNotNull(cause);
    .enter();
    try {
      State previous = state();
      switch (previous) {
        case :
        case :
          throw new IllegalStateException("Failed while in state:" + previouscause);
        case :
        case :
        case :
           = new StateSnapshot(falsecause);
          failed(previouscause);
          break;
        case :
          // Do nothing
          break;
        default:
          throw new AssertionError("Unexpected state: " + previous);
      }
    } finally {
      .leave();
      executeListeners();
    }
  }
  public final boolean isRunning() {
    return state() == ;
  }
  public final State state() {
    return .externalState();
  }

  

Since:
14.0
  public final Throwable failureCause() {
    return .failureCause();
  }

  

Since:
13.0
  public final void addListener(Listener listenerExecutor executor) {
    checkNotNull(listener"listener");
    checkNotNull(executor"executor");
    .enter();
    try {
      State currentState = state();
      if (currentState !=  && currentState != ) {
        .add(new ListenerExecutorPair(listenerexecutor));
      }
    } finally {
      .leave();
    }
  }
  @Override public String toString() {
    return getClass().getSimpleName() + " [" + state() + "]";
  }

  
A change from one service state to another, plus the result of the change.
  private class Transition extends AbstractFuture<State> {
    @Override
    public State get(long timeoutTimeUnit unit)
      try {
        return super.get(timeoutunit);
      } catch (TimeoutException e) {
        throw new TimeoutException(AbstractService.this.toString());
      }
    }
  }

  
Attempts to execute all the listeners in queuedListeners while not holding the monitor.
  private void executeListeners() {
    }
  }
  @GuardedBy("monitor")
  private void starting() {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.listener.starting();
        }
      }, pair.executor);
    }
  }
  @GuardedBy("monitor")
  private void running() {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.listener.running();
        }
      }, pair.executor);
    }
  }
  @GuardedBy("monitor")
  private void stopping(final State from) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.listener.stopping(from);
        }
      }, pair.executor);
    }
  }
  @GuardedBy("monitor")
  private void terminated(final State from) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.listener.terminated(from);
        }
      }, pair.executor);
    }
    // There are no more state transitions so we can clear this out.
    .clear();
  }
  @GuardedBy("monitor")
  private void failed(final State fromfinal Throwable cause) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.listener.failed(fromcause);
        }
      }, pair.executor);
    }
    // There are no more state transitions so we can clear this out.
    .clear();
  }

  
A simple holder for a listener and its executor.
  private static class ListenerExecutorPair {
    final Listener listener;
    final Executor executor;
    ListenerExecutorPair(Listener listenerExecutor executor) {
      this. = listener;
      this. = executor;
    }
  }

  
An immutable snapshot of the current state of the service. This class represents a consistent snapshot of the state and therefore it can be used to answer simple queries without needing to grab a lock.
  private static final class StateSnapshot {
    
The internal state, which equals external state unless shutdownWhenStartupFinishes is true.
    final State state;

    
If true, the user requested a shutdown while the service was still starting up.
    final boolean shutdownWhenStartupFinishes;

    
The exception that caused this service to fail. This will be null unless the service has failed.
    @Nullable
    final Throwable failure;
    StateSnapshot(State internalState) {
      this(internalStatefalsenull);
    }
        State internalStateboolean shutdownWhenStartupFinishes, @Nullable Throwable failure) {
      checkArgument(!shutdownWhenStartupFinishes || internalState == ,
          "shudownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.",
          internalState);
      checkArgument(!(failure != null ^ internalState == ),
          "A failure cause should be set if and only if the state is failed.  Got %s and %s "
          + "instead."internalStatefailure);
      this. = internalState;
      this. = shutdownWhenStartupFinishes;
      this. = failure;
    }

    

See also:
Service.state()
    State externalState() {
      if ( &&  == ) {
        return ;
      } else {
        return ;
      }
    }

    
      checkState( == ,
          "failureCause() is only valid if the service has failed, service is %s");
      return ;
    }
  }
New to GrepCode? Check out our FAQ X