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