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