001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.util.concurrent; 016 017import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; 018 019import com.google.common.annotations.GwtIncompatible; 020import com.google.common.base.Supplier; 021import com.google.errorprone.annotations.CanIgnoreReturnValue; 022import com.google.j2objc.annotations.WeakOuter; 023import java.util.concurrent.Executor; 024import java.util.concurrent.TimeUnit; 025import java.util.concurrent.TimeoutException; 026 027/** 028 * Base class for services that do not need a thread while "running" but may need one during startup 029 * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which 030 * run in an executor which by default uses a separate thread for each method. 031 * 032 * @author Chris Nokleberg 033 * @since 1.0 034 */ 035@GwtIncompatible 036@ElementTypesAreNonnullByDefault 037public abstract class AbstractIdleService implements Service { 038 039 /* Thread names will look like {@code "MyService STARTING"}. */ 040 private final Supplier<String> threadNameSupplier = new ThreadNameSupplier(); 041 042 @WeakOuter 043 private final class ThreadNameSupplier implements Supplier<String> { 044 @Override 045 public String get() { 046 return serviceName() + " " + state(); 047 } 048 } 049 050 /* use AbstractService for state management */ 051 private final Service delegate = new DelegateService(); 052 053 @WeakOuter 054 private final class DelegateService extends AbstractService { 055 @Override 056 protected final void doStart() { 057 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 058 .execute( 059 new Runnable() { 060 @Override 061 public void run() { 062 try { 063 startUp(); 064 notifyStarted(); 065 } catch (Throwable t) { 066 restoreInterruptIfIsInterruptedException(t); 067 notifyFailed(t); 068 } 069 } 070 }); 071 } 072 073 @Override 074 protected final void doStop() { 075 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 076 .execute( 077 new Runnable() { 078 @Override 079 public void run() { 080 try { 081 shutDown(); 082 notifyStopped(); 083 } catch (Throwable t) { 084 restoreInterruptIfIsInterruptedException(t); 085 notifyFailed(t); 086 } 087 } 088 }); 089 } 090 091 @Override 092 public String toString() { 093 return AbstractIdleService.this.toString(); 094 } 095 } 096 097 /** Constructor for use by subclasses. */ 098 protected AbstractIdleService() {} 099 100 /** Start the service. */ 101 protected abstract void startUp() throws Exception; 102 103 /** Stop the service. */ 104 protected abstract void shutDown() throws Exception; 105 106 /** 107 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 108 * this method to use a custom {@link Executor}, which may configure its worker thread with a 109 * specific name, thread group or priority. The returned executor's {@link 110 * Executor#execute(Runnable) execute()} method is called when this service is started and 111 * stopped, and should return promptly. 112 */ 113 protected Executor executor() { 114 return new Executor() { 115 @Override 116 public void execute(Runnable command) { 117 MoreExecutors.newThread(threadNameSupplier.get(), command).start(); 118 } 119 }; 120 } 121 122 @Override 123 public String toString() { 124 return serviceName() + " [" + state() + "]"; 125 } 126 127 @Override 128 public final boolean isRunning() { 129 return delegate.isRunning(); 130 } 131 132 @Override 133 public final State state() { 134 return delegate.state(); 135 } 136 137 /** @since 13.0 */ 138 @Override 139 public final void addListener(Listener listener, Executor executor) { 140 delegate.addListener(listener, executor); 141 } 142 143 /** @since 14.0 */ 144 @Override 145 public final Throwable failureCause() { 146 return delegate.failureCause(); 147 } 148 149 /** @since 15.0 */ 150 @CanIgnoreReturnValue 151 @Override 152 public final Service startAsync() { 153 delegate.startAsync(); 154 return this; 155 } 156 157 /** @since 15.0 */ 158 @CanIgnoreReturnValue 159 @Override 160 public final Service stopAsync() { 161 delegate.stopAsync(); 162 return this; 163 } 164 165 /** @since 15.0 */ 166 @Override 167 public final void awaitRunning() { 168 delegate.awaitRunning(); 169 } 170 171 /** @since 15.0 */ 172 @Override 173 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 174 delegate.awaitRunning(timeout, unit); 175 } 176 177 /** @since 15.0 */ 178 @Override 179 public final void awaitTerminated() { 180 delegate.awaitTerminated(); 181 } 182 183 /** @since 15.0 */ 184 @Override 185 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 186 delegate.awaitTerminated(timeout, unit); 187 } 188 189 /** 190 * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging 191 * output. 192 * 193 * @since 14.0 194 */ 195 protected String serviceName() { 196 return getClass().getSimpleName(); 197 } 198}