1 /* 2 * Copyright (C) 2009 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.util.concurrent; 16 17 import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; 18 19 import com.google.common.annotations.GwtIncompatible; 20 import com.google.common.annotations.J2ktIncompatible; 21 import com.google.common.base.Supplier; 22 import com.google.errorprone.annotations.CanIgnoreReturnValue; 23 import com.google.j2objc.annotations.WeakOuter; 24 import java.time.Duration; 25 import java.util.concurrent.Executor; 26 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.TimeoutException; 28 29 /** 30 * Base class for services that do not need a thread while "running" but may need one during startup 31 * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which 32 * run in an executor which by default uses a separate thread for each method. 33 * 34 * @author Chris Nokleberg 35 * @since 1.0 36 */ 37 @GwtIncompatible 38 @J2ktIncompatible 39 @ElementTypesAreNonnullByDefault 40 public abstract class AbstractIdleService implements Service { 41 42 /* Thread names will look like {@code "MyService STARTING"}. */ 43 private final Supplier<String> threadNameSupplier = new ThreadNameSupplier(); 44 45 @WeakOuter 46 private final class ThreadNameSupplier implements Supplier<String> { 47 @Override get()48 public String get() { 49 return serviceName() + " " + state(); 50 } 51 } 52 53 /* use AbstractService for state management */ 54 private final Service delegate = new DelegateService(); 55 56 @WeakOuter 57 private final class DelegateService extends AbstractService { 58 @Override doStart()59 protected final void doStart() { 60 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 61 .execute( 62 () -> { 63 try { 64 startUp(); 65 notifyStarted(); 66 } catch (Throwable t) { 67 restoreInterruptIfIsInterruptedException(t); 68 notifyFailed(t); 69 } 70 }); 71 } 72 73 @Override doStop()74 protected final void doStop() { 75 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 76 .execute( 77 () -> { 78 try { 79 shutDown(); 80 notifyStopped(); 81 } catch (Throwable t) { 82 restoreInterruptIfIsInterruptedException(t); 83 notifyFailed(t); 84 } 85 }); 86 } 87 88 @Override toString()89 public String toString() { 90 return AbstractIdleService.this.toString(); 91 } 92 } 93 94 /** Constructor for use by subclasses. */ AbstractIdleService()95 protected AbstractIdleService() {} 96 97 /** Start the service. */ startUp()98 protected abstract void startUp() throws Exception; 99 100 /** Stop the service. */ shutDown()101 protected abstract void shutDown() throws Exception; 102 103 /** 104 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 105 * this method to use a custom {@link Executor}, which may configure its worker thread with a 106 * specific name, thread group or priority. The returned executor's {@link 107 * Executor#execute(Runnable) execute()} method is called when this service is started and 108 * stopped, and should return promptly. 109 */ executor()110 protected Executor executor() { 111 return command -> MoreExecutors.newThread(threadNameSupplier.get(), command).start(); 112 } 113 114 @Override toString()115 public String toString() { 116 return serviceName() + " [" + state() + "]"; 117 } 118 119 @Override isRunning()120 public final boolean isRunning() { 121 return delegate.isRunning(); 122 } 123 124 @Override state()125 public final State state() { 126 return delegate.state(); 127 } 128 129 /** @since 13.0 */ 130 @Override addListener(Listener listener, Executor executor)131 public final void addListener(Listener listener, Executor executor) { 132 delegate.addListener(listener, executor); 133 } 134 135 /** @since 14.0 */ 136 @Override failureCause()137 public final Throwable failureCause() { 138 return delegate.failureCause(); 139 } 140 141 /** @since 15.0 */ 142 @CanIgnoreReturnValue 143 @Override startAsync()144 public final Service startAsync() { 145 delegate.startAsync(); 146 return this; 147 } 148 149 /** @since 15.0 */ 150 @CanIgnoreReturnValue 151 @Override stopAsync()152 public final Service stopAsync() { 153 delegate.stopAsync(); 154 return this; 155 } 156 157 /** @since 15.0 */ 158 @Override awaitRunning()159 public final void awaitRunning() { 160 delegate.awaitRunning(); 161 } 162 163 /** @since 28.0 */ 164 @Override awaitRunning(Duration timeout)165 public final void awaitRunning(Duration timeout) throws TimeoutException { 166 Service.super.awaitRunning(timeout); 167 } 168 169 /** @since 15.0 */ 170 @Override awaitRunning(long timeout, TimeUnit unit)171 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 172 delegate.awaitRunning(timeout, unit); 173 } 174 175 /** @since 15.0 */ 176 @Override awaitTerminated()177 public final void awaitTerminated() { 178 delegate.awaitTerminated(); 179 } 180 181 /** @since 28.0 */ 182 @Override awaitTerminated(Duration timeout)183 public final void awaitTerminated(Duration timeout) throws TimeoutException { 184 Service.super.awaitTerminated(timeout); 185 } 186 187 /** @since 15.0 */ 188 @Override awaitTerminated(long timeout, TimeUnit unit)189 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 190 delegate.awaitTerminated(timeout, unit); 191 } 192 193 /** 194 * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging 195 * output. 196 * 197 * @since 14.0 198 */ serviceName()199 protected String serviceName() { 200 return getClass().getSimpleName(); 201 } 202 } 203