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 039@ElementTypesAreNonnullByDefault 040public abstract class AbstractIdleService implements Service { 041 042 /* Thread names will look like {@code "MyService STARTING"}. */ 043 private final Supplier<String> threadNameSupplier = new ThreadNameSupplier(); 044 045 @WeakOuter 046 private final class ThreadNameSupplier implements Supplier<String> { 047 @Override 048 public String get() { 049 return serviceName() + " " + state(); 050 } 051 } 052 053 /* use AbstractService for state management */ 054 private final Service delegate = new DelegateService(); 055 056 @WeakOuter 057 private final class DelegateService extends AbstractService { 058 @Override 059 protected final void doStart() { 060 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 061 .execute( 062 () -> { 063 try { 064 startUp(); 065 notifyStarted(); 066 } catch (Throwable t) { 067 restoreInterruptIfIsInterruptedException(t); 068 notifyFailed(t); 069 } 070 }); 071 } 072 073 @Override 074 protected final void doStop() { 075 MoreExecutors.renamingDecorator(executor(), threadNameSupplier) 076 .execute( 077 () -> { 078 try { 079 shutDown(); 080 notifyStopped(); 081 } catch (Throwable t) { 082 restoreInterruptIfIsInterruptedException(t); 083 notifyFailed(t); 084 } 085 }); 086 } 087 088 @Override 089 public String toString() { 090 return AbstractIdleService.this.toString(); 091 } 092 } 093 094 /** Constructor for use by subclasses. */ 095 protected AbstractIdleService() {} 096 097 /** Start the service. */ 098 protected abstract void startUp() throws Exception; 099 100 /** Stop the service. */ 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 */ 110 protected Executor executor() { 111 return command -> MoreExecutors.newThread(threadNameSupplier.get(), command).start(); 112 } 113 114 @Override 115 public String toString() { 116 return serviceName() + " [" + state() + "]"; 117 } 118 119 @Override 120 public final boolean isRunning() { 121 return delegate.isRunning(); 122 } 123 124 @Override 125 public final State state() { 126 return delegate.state(); 127 } 128 129 /** @since 13.0 */ 130 @Override 131 public final void addListener(Listener listener, Executor executor) { 132 delegate.addListener(listener, executor); 133 } 134 135 /** @since 14.0 */ 136 @Override 137 public final Throwable failureCause() { 138 return delegate.failureCause(); 139 } 140 141 /** @since 15.0 */ 142 @CanIgnoreReturnValue 143 @Override 144 public final Service startAsync() { 145 delegate.startAsync(); 146 return this; 147 } 148 149 /** @since 15.0 */ 150 @CanIgnoreReturnValue 151 @Override 152 public final Service stopAsync() { 153 delegate.stopAsync(); 154 return this; 155 } 156 157 /** @since 15.0 */ 158 @Override 159 public final void awaitRunning() { 160 delegate.awaitRunning(); 161 } 162 163 /** @since 28.0 */ 164 @Override 165 public final void awaitRunning(Duration timeout) throws TimeoutException { 166 Service.super.awaitRunning(timeout); 167 } 168 169 /** @since 15.0 */ 170 @Override 171 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 172 delegate.awaitRunning(timeout, unit); 173 } 174 175 /** @since 15.0 */ 176 @Override 177 public final void awaitTerminated() { 178 delegate.awaitTerminated(); 179 } 180 181 /** @since 28.0 */ 182 @Override 183 public final void awaitTerminated(Duration timeout) throws TimeoutException { 184 Service.super.awaitTerminated(timeout); 185 } 186 187 /** @since 15.0 */ 188 @Override 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 */ 199 protected String serviceName() { 200 return getClass().getSimpleName(); 201 } 202}