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;
028import org.apache.hadoop.conf.Configuration;
029
030/**
031 * Composition of services.
032 */
033@Public
034@Evolving
035public class CompositeService extends AbstractService {
036
037  private static final Log LOG = LogFactory.getLog(CompositeService.class);
038
039  /**
040   * Policy on shutdown: attempt to close everything (purest) or
041   * only try to close started services (which assumes
042   * that the service implementations may not handle the stop() operation
043   * except when started.
044   * Irrespective of this policy, if a child service fails during
045   * its init() or start() operations, it will have stop() called on it.
046   */
047  protected static final boolean STOP_ONLY_STARTED_SERVICES = false;
048
049  private final List<Service> serviceList = new ArrayList<Service>();
050
051  public CompositeService(String name) {
052    super(name);
053  }
054
055  /**
056   * Get a cloned list of services
057   * @return a list of child services at the time of invocation -
058   * added services will not be picked up.
059   */
060  public List<Service> getServices() {
061    synchronized (serviceList) {
062      return new ArrayList<Service>(serviceList);
063    }
064  }
065
066  /**
067   * Add the passed {@link Service} to the list of services managed by this
068   * {@link CompositeService}
069   * @param service the {@link Service} to be added
070   */
071  protected void addService(Service service) {
072    if (LOG.isDebugEnabled()) {
073      LOG.debug("Adding service " + service.getName());
074    }
075    synchronized (serviceList) {
076      serviceList.add(service);
077    }
078  }
079
080  /**
081   * If the passed object is an instance of {@link Service},
082   * add it to the list of services managed by this {@link CompositeService}
083   * @param object
084   * @return true if a service is added, false otherwise.
085   */
086  protected boolean addIfService(Object object) {
087    if (object instanceof Service) {
088      addService((Service) object);
089      return true;
090    } else {
091      return false;
092    }
093  }
094
095  protected synchronized boolean removeService(Service service) {
096    synchronized (serviceList) {
097      return serviceList.remove(service);
098    }
099  }
100
101  protected void serviceInit(Configuration conf) throws Exception {
102    List<Service> services = getServices();
103    if (LOG.isDebugEnabled()) {
104      LOG.debug(getName() + ": initing services, size=" + services.size());
105    }
106    for (Service service : services) {
107      service.init(conf);
108    }
109    super.serviceInit(conf);
110  }
111
112  protected void serviceStart() throws Exception {
113    List<Service> services = getServices();
114    if (LOG.isDebugEnabled()) {
115      LOG.debug(getName() + ": starting services, size=" + services.size());
116    }
117    for (Service service : services) {
118      // start the service. If this fails that service
119      // will be stopped and an exception raised
120      service.start();
121    }
122    super.serviceStart();
123  }
124
125  protected void serviceStop() throws Exception {
126    //stop all services that were started
127    int numOfServicesToStop = serviceList.size();
128    if (LOG.isDebugEnabled()) {
129      LOG.debug(getName() + ": stopping services, size=" + numOfServicesToStop);
130    }
131    stop(numOfServicesToStop, STOP_ONLY_STARTED_SERVICES);
132    super.serviceStop();
133  }
134
135  /**
136   * Stop the services in reverse order
137   *
138   * @param numOfServicesStarted index from where the stop should work
139   * @param stopOnlyStartedServices flag to say "only start services that are
140   * started, not those that are NOTINITED or INITED.
141   * @throws RuntimeException the first exception raised during the
142   * stop process -<i>after all services are stopped</i>
143   */
144  private void stop(int numOfServicesStarted, boolean stopOnlyStartedServices) {
145    // stop in reverse order of start
146    Exception firstException = null;
147    List<Service> services = getServices();
148    for (int i = numOfServicesStarted - 1; i >= 0; i--) {
149      Service service = services.get(i);
150      if (LOG.isDebugEnabled()) {
151        LOG.debug("Stopping service #" + i + ": " + service);
152      }
153      STATE state = service.getServiceState();
154      //depending on the stop police
155      if (state == STATE.STARTED 
156         || (!stopOnlyStartedServices && state == STATE.INITED)) {
157        Exception ex = ServiceOperations.stopQuietly(LOG, service);
158        if (ex != null && firstException == null) {
159          firstException = ex;
160        }
161      }
162    }
163    //after stopping all services, rethrow the first exception raised
164    if (firstException != null) {
165      throw ServiceStateException.convert(firstException);
166    }
167  }
168
169  /**
170   * JVM Shutdown hook for CompositeService which will stop the give
171   * CompositeService gracefully in case of JVM shutdown.
172   */
173  public static class CompositeServiceShutdownHook implements Runnable {
174
175    private CompositeService compositeService;
176
177    public CompositeServiceShutdownHook(CompositeService compositeService) {
178      this.compositeService = compositeService;
179    }
180
181    @Override
182    public void run() {
183      ServiceOperations.stopQuietly(compositeService);
184    }
185  }
186
187}