001 /* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.util.concurrent; 018 019 import com.google.common.annotations.Beta; 020 import com.google.common.base.Throwables; 021 022 import java.util.concurrent.Executor; 023 024 /** 025 * Base class for services that do not need a thread while "running" 026 * but may need one during startup and shutdown. Subclasses can 027 * implement {@link #startUp} and {@link #shutDown} methods, each 028 * which run in a executor which by default uses a separate thread 029 * for each method. 030 * 031 * @author Chris Nokleberg 032 * @since 1.0 033 */ 034 @Beta 035 public abstract class AbstractIdleService implements Service { 036 037 /* use AbstractService for state management */ 038 private final Service delegate = new AbstractService() { 039 @Override protected final void doStart() { 040 executor(State.STARTING).execute(new Runnable() { 041 @Override public void run() { 042 try { 043 startUp(); 044 notifyStarted(); 045 } catch (Throwable t) { 046 notifyFailed(t); 047 throw Throwables.propagate(t); 048 } 049 } 050 }); 051 } 052 053 @Override protected final void doStop() { 054 executor(State.STOPPING).execute(new Runnable() { 055 @Override public void run() { 056 try { 057 shutDown(); 058 notifyStopped(); 059 } catch (Throwable t) { 060 notifyFailed(t); 061 throw Throwables.propagate(t); 062 } 063 } 064 }); 065 } 066 }; 067 068 /** Start the service. */ 069 protected abstract void startUp() throws Exception; 070 071 /** Stop the service. */ 072 protected abstract void shutDown() throws Exception; 073 074 /** 075 * Returns the {@link Executor} that will be used to run this service. 076 * Subclasses may override this method to use a custom {@link Executor}, which 077 * may configure its worker thread with a specific name, thread group or 078 * priority. The returned executor's {@link Executor#execute(Runnable) 079 * execute()} method is called when this service is started and stopped, 080 * and should return promptly. 081 * 082 * @param state {@link Service.State#STARTING} or 083 * {@link Service.State#STOPPING}, used by the default implementation for 084 * naming the thread 085 */ 086 protected Executor executor(final State state) { 087 return new Executor() { 088 @Override 089 public void execute(Runnable command) { 090 new Thread(command, getServiceName() + " " + state).start(); 091 } 092 }; 093 } 094 095 @Override public String toString() { 096 return getServiceName() + " [" + state() + "]"; 097 } 098 099 // We override instead of using ForwardingService so that these can be final. 100 101 @Override public final ListenableFuture<State> start() { 102 return delegate.start(); 103 } 104 105 @Override public final State startAndWait() { 106 return delegate.startAndWait(); 107 } 108 109 @Override public final boolean isRunning() { 110 return delegate.isRunning(); 111 } 112 113 @Override public final State state() { 114 return delegate.state(); 115 } 116 117 @Override public final ListenableFuture<State> stop() { 118 return delegate.stop(); 119 } 120 121 @Override public final State stopAndWait() { 122 return delegate.stopAndWait(); 123 } 124 125 private String getServiceName() { 126 return getClass().getSimpleName(); 127 } 128 }