001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.service; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.apache.hadoop.classification.InterfaceAudience.Public; 027import org.apache.hadoop.classification.InterfaceStability.Evolving; 028 029/** 030 * This class contains a set of methods to work with services, especially 031 * to walk them through their lifecycle. 032 */ 033@Public 034@Evolving 035public final class ServiceOperations { 036 private static final Log LOG = LogFactory.getLog(AbstractService.class); 037 038 private ServiceOperations() { 039 } 040 041 /** 042 * Stop a service. 043 * <p/>Do nothing if the service is null or not 044 * in a state in which it can be/needs to be stopped. 045 * <p/> 046 * The service state is checked <i>before</i> the operation begins. 047 * This process is <i>not</i> thread safe. 048 * @param service a service or null 049 */ 050 public static void stop(Service service) { 051 if (service != null) { 052 service.stop(); 053 } 054 } 055 056 /** 057 * Stop a service; if it is null do nothing. Exceptions are caught and 058 * logged at warn level. (but not Throwables). This operation is intended to 059 * be used in cleanup operations 060 * 061 * @param service a service; may be null 062 * @return any exception that was caught; null if none was. 063 */ 064 public static Exception stopQuietly(Service service) { 065 return stopQuietly(LOG, service); 066 } 067 068 /** 069 * Stop a service; if it is null do nothing. Exceptions are caught and 070 * logged at warn level. (but not Throwables). This operation is intended to 071 * be used in cleanup operations 072 * 073 * @param log the log to warn at 074 * @param service a service; may be null 075 * @return any exception that was caught; null if none was. 076 * @see ServiceOperations#stopQuietly(Service) 077 */ 078 public static Exception stopQuietly(Log log, Service service) { 079 try { 080 stop(service); 081 } catch (Exception e) { 082 log.warn("When stopping the service " + service.getName() 083 + " : " + e, 084 e); 085 return e; 086 } 087 return null; 088 } 089 090 091 /** 092 * Class to manage a list of {@link ServiceStateChangeListener} instances, 093 * including a notification loop that is robust against changes to the list 094 * during the notification process. 095 */ 096 public static class ServiceListeners { 097 /** 098 * List of state change listeners; it is final to guarantee 099 * that it will never be null. 100 */ 101 private final List<ServiceStateChangeListener> listeners = 102 new ArrayList<ServiceStateChangeListener>(); 103 104 /** 105 * Thread-safe addition of a new listener to the end of a list. 106 * Attempts to re-register a listener that is already registered 107 * will be ignored. 108 * @param l listener 109 */ 110 public synchronized void add(ServiceStateChangeListener l) { 111 if(!listeners.contains(l)) { 112 listeners.add(l); 113 } 114 } 115 116 /** 117 * Remove any registration of a listener from the listener list. 118 * @param l listener 119 * @return true if the listener was found (and then removed) 120 */ 121 public synchronized boolean remove(ServiceStateChangeListener l) { 122 return listeners.remove(l); 123 } 124 125 /** 126 * Reset the listener list 127 */ 128 public synchronized void reset() { 129 listeners.clear(); 130 } 131 132 /** 133 * Change to a new state and notify all listeners. 134 * This method will block until all notifications have been issued. 135 * It caches the list of listeners before the notification begins, 136 * so additions or removal of listeners will not be visible. 137 * @param service the service that has changed state 138 */ 139 public void notifyListeners(Service service) { 140 //take a very fast snapshot of the callback list 141 //very much like CopyOnWriteArrayList, only more minimal 142 ServiceStateChangeListener[] callbacks; 143 synchronized (this) { 144 callbacks = listeners.toArray(new ServiceStateChangeListener[listeners.size()]); 145 } 146 //iterate through the listeners outside the synchronized method, 147 //ensuring that listener registration/unregistration doesn't break anything 148 for (ServiceStateChangeListener l : callbacks) { 149 l.stateChanged(service); 150 } 151 } 152 } 153 154}