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 com.google.common.util.concurrent.Service.State// javadoc needs this
 
 import java.util.List;
 
 import  javax.annotation.Nullable;
 import  javax.annotation.concurrent.GuardedBy;
 import  javax.annotation.concurrent.Immutable;

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 static final Logger logger = Logger.getLogger(AbstractService.class.getName());
   private final ReentrantLock lock = new ReentrantLock();
 
   private final Transition startup = new Transition();
   private final Transition shutdown = new Transition();

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

  
The queue of listeners that are waiting to be executed.

Enqueue operations should be protected by lock while dequeue operations should be protected by the implicit lock on this object. Dequeue operations should be executed atomically with the execution of the Runnable and additionally the lock should not be held when the listeners are being executed. Use executeListeners for this operation. This is necessary to ensure that elements on the queue are executed in the correct order. Enqueue operations should be protected so that listeners are added in the correct order. We use a concurrent queue implementation so that enqueues can be executed concurrently with dequeues.

 
   @GuardedBy("queuedListeners")
   private final Queue<RunnablequeuedListeners = Queues.newConcurrentLinkedQueue();

  
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("lock")
   private volatile StateSnapshot snapshot = new StateSnapshot(.);
 
   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.
     addListener(
         new Listener() {
           @Override public void starting() {}
 
           @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(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(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();
  public final ListenableFuture<Statestart() {
    .lock();
    try {
      if (. == .) {
         = new StateSnapshot(.);
        starting();
        doStart();
      }
    } catch (Throwable startupFailure) {
      notifyFailed(startupFailure);
    } finally {
      .unlock();
      executeListeners();
    }
    return ;
  }
  public final ListenableFuture<Statestop() {
    .lock();
    try {
      switch (.) {
        case :
           = new StateSnapshot(.);
          terminated(.);
          break;
        case :
           = new StateSnapshot(.truenull);
          stopping(.);
          break;
        case :
           = new StateSnapshot(.);
          stopping(.);
          doStop();
          break;
        case :
        case :
        case :
          // do nothing
          break;
        default:
          throw new AssertionError("Unexpected state: " + .);
      }
    } catch (Throwable shutdownFailure) {
      notifyFailed(shutdownFailure);
    } finally {
      .unlock();
      executeListeners();
    }
    return ;
  }
  public State startAndWait() {
    return Futures.getUnchecked(start());
  }
  public State stopAndWait() {
    return Futures.getUnchecked(stop());
  }

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

Throws:
IllegalStateException if the service is not State.STARTING.
  protected final void notifyStarted() {
    .lock();
    try {
      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 {
      .unlock();
      executeListeners();
    }
  }

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

Throws:
IllegalStateException if the service is neither State.STOPPING nor State.RUNNING.
  protected final void notifyStopped() {
    .lock();
    try {
      if (. != . && . != .) {
        IllegalStateException failure = new IllegalStateException(
            "Cannot notifyStopped() when the service is " + .);
        notifyFailed(failure);
        throw failure;
      }
      State previous = .;
       = new StateSnapshot(.);
      terminated(previous);
    } finally {
      .unlock();
      executeListeners();
    }
  }

  
Invoke this method to transition the service to the 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);
    .lock();
    try {
      switch (.) {
        case :
        case :
          throw new IllegalStateException("Failed while in state:" + .cause);
        case :
        case :
        case :
          State previous = .;
           = new StateSnapshot(.falsecause);
          failed(previouscause);
          break;
        case :
          // Do nothing
          break;
        default:
          throw new AssertionError("Unexpected state: " + .);
      }
    } finally {
      .unlock();
      executeListeners();
    }
  }
  public final boolean isRunning() {
    return state() == .;
  }
  public final State state() {
    return .externalState();
  }
  public final void addListener(Listener listenerExecutor executor) {
    checkNotNull(listener"listener");
    checkNotNull(executor"executor");
    .lock();
    try {
      if (. != . && . != .) {
        .add(new ListenerExecutorPair(listenerexecutor));
      }
    } finally {
      .unlock();
    }
  }
  @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 lock.
  private void executeListeners() {
    if (!.isHeldByCurrentThread()) {
      synchronized () {
        Runnable listener;
        while ((listener = .poll()) != null) {
          listener.run();
        }
      }
    }
  }
  @GuardedBy("lock")
  private void starting() {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.execute(new Runnable() {
            @Override public void run() {
              pair.listener.starting();
            }
          });
        }
      });
    }
  }
  @GuardedBy("lock")
  private void running() {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.execute(new Runnable() {
            @Override public void run() {
              pair.listener.running();
            }
          });
        }
      });
    }
  }
  @GuardedBy("lock")
  private void stopping(final State from) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.execute(new Runnable() {
            @Override public void run() {
              pair.listener.stopping(from);
            }
          });
        }
      });
    }
  }
  @GuardedBy("lock")
  private void terminated(final State from) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.execute(new Runnable() {
            @Override public void run() {
              pair.listener.terminated(from);
            }
          });
        }
      });
    }
    // There are no more state transitions so we can clear this out.
    .clear();
  }
  @GuardedBy("lock")
  private void failed(final State fromfinal Throwable cause) {
    for (final ListenerExecutorPair pair : ) {
      .add(new Runnable() {
        @Override public void run() {
          pair.execute(new Runnable() {
            @Override public void run() {
              pair.listener.failed(fromcause);
            }
          });
        }
      });
    }
    // 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;
    }

    
Executes the given Runnable on executor logging and swallowing all exceptions
    void execute(Runnable runnable) {
      try {
        .execute(runnable);
      } catch (Exception e) {
        .log(."Exception while executing listener " + 
            + " with executor " + e);
      }
    }
  }

  
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.
  @Immutable
  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);
    }
    StateSnapshot(State internalStateboolean shutdownWhenStartupFinishesThrowable 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 ;
      }
    }

    

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