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