Java tutorial
/*********************************************************************************************************************** * * Copyright (C) 2010 by the Stratosphere project (http://stratosphere.eu) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * **********************************************************************************************************************/ package eu.stratosphere.nephele.plugins; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.SAXException; import eu.stratosphere.nephele.configuration.Configuration; import eu.stratosphere.nephele.protocols.PluginCommunicationProtocol; import eu.stratosphere.nephele.taskmanager.TaskManager; import eu.stratosphere.nephele.util.StringUtils; /** * The plugin manager is responsible for loading and managing the individual plugins. * <p> * This class is thread-safe. * * @author warneke */ public final class PluginManager { /** * The log object used to report errors and information in general. */ private static final Log LOG = LogFactory.getLog(PluginManager.class); /** * The name of the file containing the plugin configuration. */ private static final String PLUGIN_CONFIG_FILE = "nephele-plugins.xml"; /** * The singleton instance of this class. */ private static PluginManager INSTANCE = null; private final Map<String, AbstractPluginLoader> plugins; private PluginManager(final String configDir, final PluginLookupService pluginLookupService) { // Check if the configuration file exists final File configFile = new File(configDir + File.separator + PLUGIN_CONFIG_FILE); if (configFile.exists()) { this.plugins = loadPlugins(configFile, pluginLookupService); } else { this.plugins = Collections.emptyMap(); LOG.warn("Unable to load plugins: configuration file " + configFile.getAbsolutePath() + " not found"); } } private String getTextChild(final Node node) { final NodeList nodeList = node.getChildNodes(); if (nodeList.getLength() != 1) { return null; } final Node child = nodeList.item(0); if (!(child instanceof Text)) { return null; } final Text text = (Text) child; return text.getNodeValue(); } @SuppressWarnings("unchecked") private Map<String, AbstractPluginLoader> loadPlugins(final File configFile, final PluginLookupService pluginLookupService) { final Map<String, AbstractPluginLoader> tmpPluginList = new LinkedHashMap<String, AbstractPluginLoader>(); final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); // Ignore comments in the XML file docBuilderFactory.setIgnoringComments(true); docBuilderFactory.setNamespaceAware(true); try { final DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); final Document doc = builder.parse(configFile); if (doc == null) { LOG.error("Unable to load plugins: doc is null"); return Collections.emptyMap(); } final Element root = doc.getDocumentElement(); if (root == null) { LOG.error("Unable to load plugins: root is null"); return Collections.emptyMap(); } if (!"plugins".equals(root.getNodeName())) { LOG.error("Unable to load plugins: unknown element " + root.getNodeName()); return Collections.emptyMap(); } final NodeList pluginNodes = root.getChildNodes(); int pluginCounter = 0; for (int i = 0; i < pluginNodes.getLength(); ++i) { final Node pluginNode = pluginNodes.item(i); // Ignore text at this point if (pluginNode instanceof Text) { continue; } if (!"plugin".equals(pluginNode.getNodeName())) { LOG.error("Unable to load plugins: unknown element " + pluginNode.getNodeName()); continue; } // Increase plugin counter ++pluginCounter; final NodeList pluginChildren = pluginNode.getChildNodes(); String pluginName = null; String pluginClass = null; Configuration pluginConfiguration = null; for (int j = 0; j < pluginChildren.getLength(); ++j) { final Node pluginChild = pluginChildren.item(j); // Ignore text at this point if (pluginChild instanceof Text) { continue; } if ("name".equals(pluginChild.getNodeName())) { pluginName = getTextChild(pluginChild); if (pluginName == null) { LOG.error("Skipping plugin " + pluginCounter + " from configuration because it does not provide a proper name"); continue; } } if ("class".equals(pluginChild.getNodeName())) { pluginClass = getTextChild(pluginChild); if (pluginClass == null) { LOG.error("Skipping plugin " + pluginCounter + " from configuration because it does not provide a loader class"); continue; } } if ("configuration".equals(pluginChild.getNodeName())) { pluginConfiguration = new Configuration(); final NodeList configurationNodes = pluginChild.getChildNodes(); for (int k = 0; k < configurationNodes.getLength(); ++k) { final Node configurationNode = configurationNodes.item(k); // Ignore text at this point if (configurationNode instanceof Text) { continue; } if (!"property".equals(configurationNode.getNodeName())) { LOG.error("Unexpected node " + configurationNode.getNodeName() + ", skipping..."); continue; } String key = null; String value = null; final NodeList properties = configurationNode.getChildNodes(); for (int l = 0; l < properties.getLength(); ++l) { final Node property = properties.item(l); // Ignore text at this point if (configurationNode instanceof Text) { continue; } if ("key".equals(property.getNodeName())) { key = getTextChild(property); if (key == null) { LOG.warn("Skipping configuration entry for plugin " + pluginName + " because of invalid key"); continue; } } if ("value".equals(property.getNodeName())) { value = getTextChild(property); if (value == null) { LOG.warn("Skipping configuration entry for plugin " + pluginName + " because of invalid value"); continue; } } } if (key != null && value != null) { pluginConfiguration.setString(key, value); } } } } if (pluginName == null) { LOG.error("Plugin " + pluginCounter + " does not provide a name, skipping..."); continue; } if (pluginClass == null) { LOG.error("Plugin " + pluginCounter + " does not provide a loader class, skipping..."); continue; } if (pluginConfiguration == null) { LOG.warn("Plugin " + pluginCounter + " does not provide a configuration, using default configuration"); pluginConfiguration = new Configuration(); } Class<? extends AbstractPluginLoader> loaderClass; try { loaderClass = (Class<? extends AbstractPluginLoader>) Class.forName(pluginClass); } catch (ClassNotFoundException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } if (loaderClass == null) { LOG.error("Unable to load plugin " + pluginName + ": loaderClass is null"); continue; } Constructor<? extends AbstractPluginLoader> constructor; try { constructor = (Constructor<? extends AbstractPluginLoader>) loaderClass .getConstructor(String.class, Configuration.class, PluginLookupService.class); } catch (SecurityException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } catch (NoSuchMethodException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } if (constructor == null) { LOG.error("Unable to load plugin " + pluginName + ": constructor is null"); continue; } AbstractPluginLoader pluginLoader = null; try { pluginLoader = constructor.newInstance(pluginName, pluginConfiguration, pluginLookupService); } catch (IllegalArgumentException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } catch (InstantiationException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } catch (IllegalAccessException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } catch (InvocationTargetException e) { LOG.error("Unable to load plugin " + pluginName + ": " + StringUtils.stringifyException(e)); continue; } if (pluginLoader == null) { LOG.error("Unable to load plugin " + pluginName + ": pluginLoader is null"); continue; } LOG.info("Successfully loaded plugin " + pluginName); tmpPluginList.put(pluginName, pluginLoader); } } catch (IOException e) { LOG.error("Error while loading plugins: " + StringUtils.stringifyException(e)); } catch (SAXException e) { LOG.error("Error while loading plugins: " + StringUtils.stringifyException(e)); } catch (ParserConfigurationException e) { LOG.error("Error while loading plugins: " + StringUtils.stringifyException(e)); } return Collections.unmodifiableMap(tmpPluginList); } private static synchronized PluginManager getInstance(final String configDir, final PluginLookupService pluginLookupService) { if (INSTANCE == null) { INSTANCE = new PluginManager(configDir, pluginLookupService); } return INSTANCE; } private Map<PluginID, JobManagerPlugin> getJobManagerPluginsInternal() { final Map<PluginID, JobManagerPlugin> jobManagerPluginMap = new HashMap<PluginID, JobManagerPlugin>(); final Iterator<AbstractPluginLoader> it = this.plugins.values().iterator(); while (it.hasNext()) { final AbstractPluginLoader apl = it.next(); final PluginID pluginID = apl.getPluginID(); final JobManagerPlugin jmp = apl.getJobManagerPlugin(); if (jmp != null) { if (!jobManagerPluginMap.containsKey(pluginID)) { jobManagerPluginMap.put(pluginID, jmp); } else { LOG.error("Detected ID collision for plugin " + apl.getPluginName() + ", skipping it..."); } } } return Collections.unmodifiableMap(jobManagerPluginMap); } private Map<PluginID, TaskManagerPlugin> getTaskManagerPluginsInternal() { final Map<PluginID, TaskManagerPlugin> taskManagerPluginMap = new HashMap<PluginID, TaskManagerPlugin>(); final Iterator<AbstractPluginLoader> it = this.plugins.values().iterator(); while (it.hasNext()) { final AbstractPluginLoader apl = it.next(); final PluginID pluginID = apl.getPluginID(); final TaskManagerPlugin tmp = apl.getTaskManagerPlugin(); if (tmp != null) { if (!taskManagerPluginMap.containsKey(pluginID)) { taskManagerPluginMap.put(pluginID, tmp); } else { LOG.error("Detected ID collision for plugin " + apl.getPluginName() + ", skipping it..."); } } } return Collections.unmodifiableMap(taskManagerPluginMap); } public static Map<PluginID, JobManagerPlugin> getJobManagerPlugins(final PluginCommunicationProtocol jobManager, final String configDir) { final JobManagerLookupService lookupService = new JobManagerLookupService(jobManager); return getInstance(configDir, lookupService).getJobManagerPluginsInternal(); } public static Map<PluginID, TaskManagerPlugin> getTaskManagerPlugins(final TaskManager taskManager, final String configDir) { final TaskManagerLookupService lookupService = new TaskManagerLookupService(taskManager); return getInstance(configDir, lookupService).getTaskManagerPluginsInternal(); } }