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.util; 020 021import org.apache.commons.logging.*; 022 023import org.apache.hadoop.conf.Configuration; 024 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.List; 029import java.util.concurrent.Executor; 030import java.util.concurrent.Executors; 031 032/** 033 * Provides convenience functions for dispatching calls through 034 * to plugins registered with a class. Classes that wish to provide 035 * plugin interfaces should use this class to load the plugin list 036 * from the Configuration and to dispatch calls to the loaded instances. 037 * 038 * Calls dispatched through this class are performed on a second thread 039 * so as to not block execution of the plugged service. 040 */ 041public class PluginDispatcher<T extends ServicePlugin> { 042 public static final Log LOG = LogFactory.getLog(PluginDispatcher.class.getName()); 043 044 private final List<T> plugins; 045 private Executor executor; 046 047 /** 048 * Load a PluginDispatcher from the given Configuration. The start() 049 * callback will not be automatically called. 050 * 051 * @param conf the Configuration from which to load 052 * @param key the configuration key that lists class names to instantiate 053 * @param clazz the class or interface from which plugins must extend 054 */ 055 public static <X extends ServicePlugin> PluginDispatcher<X> createFromConfiguration( 056 Configuration conf, String key, Class<X> clazz) { 057 List<X> plugins = new ArrayList<X>(); 058 try { 059 plugins.addAll(conf.getInstances(key, clazz)); 060 } catch (Throwable t) { 061 LOG.warn("Unable to load "+key+" plugins"); 062 } 063 return new PluginDispatcher<X>(plugins); 064 } 065 066 PluginDispatcher(Collection<T> plugins) { 067 this.plugins = Collections.synchronizedList(new ArrayList<T>(plugins)); 068 executor = Executors.newSingleThreadExecutor(); 069 } 070 071 PluginDispatcher(Collection<T> plugins, Executor executor) { 072 this.plugins = Collections.synchronizedList(new ArrayList<T>(plugins)); 073 this.executor = executor; 074 } 075 076 /** 077 * Dispatch a call to all active plugins. 078 * 079 * Exceptions will be caught and logged at WARN level. 080 * 081 * @param callback a function which will run once for each plugin, with 082 * that plugin as the argument 083 */ 084 public void dispatchCall(final SingleArgumentRunnable<T> callback) { 085 executor.execute(new Runnable() { 086 public void run() { 087 for (T plugin : plugins) { 088 try { 089 callback.run(plugin); 090 } catch (Throwable t) { 091 LOG.warn("Uncaught exception dispatching to plugin " + plugin, t); 092 } 093 } 094 }}); 095 } 096 097 /** 098 * Dispatches the start(...) hook common to all ServicePlugins. This 099 * also automatically removes any plugin that throws an exception while 100 * attempting to start. 101 * 102 * @param plugPoint passed to ServicePlugin.start() 103 */ 104 public void dispatchStart(final Object plugPoint) { 105 dispatchCall( 106 new SingleArgumentRunnable<T>() { 107 public void run(T p) { 108 try { 109 p.start(plugPoint); 110 } catch (Throwable t) { 111 LOG.error("ServicePlugin " + p + " could not be started. " + 112 "Removing from future callbacks.", t); 113 plugins.remove(p); 114 } 115 } 116 }); 117 } 118 119 /** 120 * Convenience function for dispatching the stop() hook common to all 121 * ServicePlugins. 122 */ 123 public void dispatchStop() { 124 dispatchCall( 125 new SingleArgumentRunnable<T>() { 126 public void run(T p) { 127 try { 128 p.stop(); 129 } catch (Throwable t) { 130 LOG.warn("ServicePlugin " + p + " could not be stopped", t); 131 } 132 } 133 }); 134 } 135}