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.time.Duration; 024import java.util.concurrent.Executor; 025import java.util.concurrent.TimeUnit; 026import java.util.concurrent.TimeoutException; 027 028/** 029 * Base class for services that do not need a thread while "running" but may need one during startup 030 * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which 031 * run in an executor which by default uses a separate thread for each method. 032 * 033 * @author Chris Nokleberg 034 * @since 1.0 035 */ 036@GwtIncompatible 037@ElementTypesAreNonnullByDefault 038public abstract class AbstractIdleService implements Service { 039 040 /* Thread names will look like {@code "MyService STARTING"}. */ 041 private final Supplier<String> threadNameSupplier = new ThreadNameSupplier(); 042 043 @WeakOuter 044 private final class ThreadNameSupplier implements Supplier<String> { 045 @Override 046 public String get() { 047 return serviceName() + " " + state(); 048 } 049 } 050 051 /* use AbstractService for state management */ 052 private final Service delegate = new DelegateService(); 053 054 @WeakOuter 055 private final class DelegateService extends AbstractService { 056 @Override 057 protected final void doStart() { 058 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 059 .execute( 060 new Runnable() { 061 @Override 062 public void run() { 063 try { 064 startUp(); 065 notifyStarted(); 066 } catch (Throwable t) { 067 restoreInterruptIfIsInterruptedException(t); 068 notifyFailed(t); 069 } 070 } 071 }); 072 } 073 074 @Override 075 protected final void doStop() { 076 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 077 .execute( 078 new Runnable() { 079 @Override 080 public void run() { 081 try { 082 shutDown(); 083 notifyStopped(); 084 } catch (Throwable t) { 085 restoreInterruptIfIsInterruptedException(t); 086 notifyFailed(t); 087 } 088 } 089 }); 090 } 091 092 @Override 093 public String toString() { 094 return AbstractIdleService.this.toString(); 095 } 096 } 097 098 /** Constructor for use by subclasses. */ 099 protected AbstractIdleService() {} 100 101 /** Start the service. */ 102 protected abstract void startUp() throws Exception; 103 104 /** Stop the service. */ 105 protected abstract void shutDown() throws Exception; 106 107 /** 108 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 109 * this method to use a custom {@link Executor}, which may configure its worker thread with a 110 * specific name, thread group or priority. The returned executor's {@link 111 * Executor#execute(Runnable) execute()} method is called when this service is started and 112 * stopped, and should return promptly. 113 */ 114 protected Executor executor() { 115 return new Executor() { 116 @Override 117 public void execute(Runnable command) { 118 MoreExecutors.newThread(threadNameSupplier.get(), command).start(); 119 } 120 }; 121 } 122 123 @Override 124 public String toString() { 125 return serviceName() + " [" + state() + "]"; 126 } 127 128 @Override 129 public final boolean isRunning() { 130 return delegate.isRunning(); 131 } 132 133 @Override 134 public final State state() { 135 return delegate.state(); 136 } 137 138 /** @since 13.0 */ 139 @Override 140 public final void addListener(Listener listener, Executor executor) { 141 delegate.addListener(listener, executor); 142 } 143 144 /** @since 14.0 */ 145 @Override 146 public final Throwable failureCause() { 147 return delegate.failureCause(); 148 } 149 150 /** @since 15.0 */ 151 @CanIgnoreReturnValue 152 @Override 153 public final Service startAsync() { 154 delegate.startAsync(); 155 return this; 156 } 157 158 /** @since 15.0 */ 159 @CanIgnoreReturnValue 160 @Override 161 public final Service stopAsync() { 162 delegate.stopAsync(); 163 return this; 164 } 165 166 /** @since 15.0 */ 167 @Override 168 public final void awaitRunning() { 169 delegate.awaitRunning(); 170 } 171 172 /** @since 28.0 */ 173 @Override 174 public final void awaitRunning(Duration timeout) throws TimeoutException { 175 Service.super.awaitRunning(timeout); 176 } 177 178 /** @since 15.0 */ 179 @Override 180 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 181 delegate.awaitRunning(timeout, unit); 182 } 183 184 /** @since 15.0 */ 185 @Override 186 public final void awaitTerminated() { 187 delegate.awaitTerminated(); 188 } 189 190 /** @since 28.0 */ 191 @Override 192 public final void awaitTerminated(Duration timeout) throws TimeoutException { 193 Service.super.awaitTerminated(timeout); 194 } 195 196 /** @since 15.0 */ 197 @Override 198 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 199 delegate.awaitTerminated(timeout, unit); 200 } 201 202 /** 203 * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging 204 * output. 205 * 206 * @since 14.0 207 */ 208 protected String serviceName() { 209 return getClass().getSimpleName(); 210 } 211}