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