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 /** @since 13.0 */ 129 @Override 130 public final void addListener(Listener listener, Executor executor) { 131 delegate.addListener(listener, executor); 132 } 133 134 /** @since 14.0 */ 135 @Override 136 public final Throwable failureCause() { 137 return delegate.failureCause(); 138 } 139 140 /** @since 15.0 */ 141 @CanIgnoreReturnValue 142 @Override 143 public final Service startAsync() { 144 delegate.startAsync(); 145 return this; 146 } 147 148 /** @since 15.0 */ 149 @CanIgnoreReturnValue 150 @Override 151 public final Service stopAsync() { 152 delegate.stopAsync(); 153 return this; 154 } 155 156 /** @since 15.0 */ 157 @Override 158 public final void awaitRunning() { 159 delegate.awaitRunning(); 160 } 161 162 /** @since 28.0 */ 163 @Override 164 public final void awaitRunning(Duration timeout) throws TimeoutException { 165 Service.super.awaitRunning(timeout); 166 } 167 168 /** @since 15.0 */ 169 @Override 170 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 171 delegate.awaitRunning(timeout, unit); 172 } 173 174 /** @since 15.0 */ 175 @Override 176 public final void awaitTerminated() { 177 delegate.awaitTerminated(); 178 } 179 180 /** @since 28.0 */ 181 @Override 182 public final void awaitTerminated(Duration timeout) throws TimeoutException { 183 Service.super.awaitTerminated(timeout); 184 } 185 186 /** @since 15.0 */ 187 @Override 188 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 189 delegate.awaitTerminated(timeout, unit); 190 } 191 192 /** 193 * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging 194 * output. 195 * 196 * @since 14.0 197 */ 198 protected String serviceName() { 199 return getClass().getSimpleName(); 200 } 201}