| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.nutch.plugin; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.UnsupportedEncodingException; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLDecoder; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.slf4j.Logger; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * The <code>PluginManifestParser</code> parser just parse the manifest file in |
| * all plugin directories. |
| * |
| * @author joa23 |
| */ |
| public class PluginManifestParser { |
| private static final String ATTR_NAME = "name"; |
| private static final String ATTR_CLASS = "class"; |
| private static final String ATTR_ID = "id"; |
| |
| public static final Logger LOG = PluginRepository.LOG; |
| |
| private static final boolean WINDOWS = System.getProperty("os.name") |
| .startsWith("Windows"); |
| |
| private Configuration conf; |
| |
| private PluginRepository pluginRepository; |
| |
| public PluginManifestParser(Configuration conf, |
| PluginRepository pluginRepository) { |
| this.conf = conf; |
| this.pluginRepository = pluginRepository; |
| } |
| |
| /** |
| * Returns a list of all found plugin descriptors. |
| * |
| * @param pluginFolders |
| * folders to search plugins from |
| * @return A {@link Map} of all found {@link PluginDescriptor}s. |
| */ |
| public Map<String, PluginDescriptor> parsePluginFolder(String[] pluginFolders) { |
| Map<String, PluginDescriptor> map = new HashMap<>(); |
| |
| if (pluginFolders == null) { |
| throw new IllegalArgumentException("plugin.folders is not defined"); |
| } |
| |
| for (String name : pluginFolders) { |
| File directory = getPluginFolder(name); |
| if (directory == null) { |
| continue; |
| } |
| LOG.info("Plugins: looking in: " + directory.getAbsolutePath()); |
| for (File oneSubFolder : directory.listFiles()) { |
| if (oneSubFolder.isDirectory()) { |
| String manifestPath = oneSubFolder.getAbsolutePath() + File.separator |
| + "plugin.xml"; |
| try { |
| LOG.debug("parsing: " + manifestPath); |
| PluginDescriptor p = parseManifestFile(manifestPath); |
| map.put(p.getPluginId(), p); |
| } catch (Exception e) { |
| LOG.warn("Error while loading plugin `" + manifestPath + "` " |
| + e.toString()); |
| } |
| } |
| } |
| } |
| return map; |
| } |
| |
| /** |
| * Return the named plugin folder. If the name is absolute then it is |
| * returned. Otherwise, for relative names, the classpath is scanned. |
| */ |
| public File getPluginFolder(String name) { |
| File directory = new File(name); |
| if (!directory.isAbsolute()) { |
| URL url = PluginManifestParser.class.getClassLoader().getResource(name); |
| if (url == null && directory.exists() && directory.isDirectory() |
| && directory.listFiles().length > 0) { |
| return directory; // relative path that is not in the classpath |
| } else if (url == null) { |
| LOG.warn("Plugins: directory not found: " + name); |
| return null; |
| } else if (!"file".equals(url.getProtocol())) { |
| LOG.warn("Plugins: not a file: url. Can't load plugins from: " + url); |
| return null; |
| } |
| String path = url.getPath(); |
| if (WINDOWS && path.startsWith("/")) // patch a windows bug |
| path = path.substring(1); |
| try { |
| path = URLDecoder.decode(path, "UTF-8"); // decode the url path |
| } catch (UnsupportedEncodingException e) { |
| } |
| directory = new File(path); |
| } else if (!directory.exists()) { |
| LOG.warn("Plugins: directory not found: " + name); |
| return null; |
| } |
| return directory; |
| } |
| |
| /** |
| * @param manifestPath |
| * @throws ParserConfigurationException |
| * @throws IOException |
| * @throws SAXException |
| * @throws MalformedURLException |
| */ |
| private PluginDescriptor parseManifestFile(String pManifestPath) |
| throws MalformedURLException, SAXException, IOException, |
| ParserConfigurationException { |
| Document document = parseXML(new File(pManifestPath).toURI().toURL()); |
| String pPath = new File(pManifestPath).getParent(); |
| return parsePlugin(document, pPath); |
| } |
| |
| /** |
| * @param url |
| * @return Document |
| * @throws IOException |
| * @throws SAXException |
| * @throws ParserConfigurationException |
| * @throws DocumentException |
| */ |
| private Document parseXML(URL url) throws SAXException, IOException, |
| ParserConfigurationException { |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| DocumentBuilder builder = factory.newDocumentBuilder(); |
| return builder.parse(url.openStream()); |
| } |
| |
| /** |
| * @param pDocument |
| * @throws MalformedURLException |
| */ |
| private PluginDescriptor parsePlugin(Document pDocument, String pPath) |
| throws MalformedURLException { |
| Element rootElement = pDocument.getDocumentElement(); |
| String id = rootElement.getAttribute(ATTR_ID); |
| String name = rootElement.getAttribute(ATTR_NAME); |
| String version = rootElement.getAttribute("version"); |
| String providerName = rootElement.getAttribute("provider-name"); |
| String pluginClazz = null; |
| if (rootElement.getAttribute(ATTR_CLASS).trim().length() > 0) { |
| pluginClazz = rootElement.getAttribute(ATTR_CLASS); |
| } |
| PluginDescriptor pluginDescriptor = new PluginDescriptor(id, version, name, |
| providerName, pluginClazz, pPath, this.conf); |
| LOG.debug("plugin: id=" + id + " name=" + name + " version=" + version |
| + " provider=" + providerName + "class=" + pluginClazz); |
| parseExtension(rootElement, pluginDescriptor); |
| parseExtensionPoints(rootElement, pluginDescriptor); |
| parseLibraries(rootElement, pluginDescriptor); |
| parseRequires(rootElement, pluginDescriptor); |
| return pluginDescriptor; |
| } |
| |
| /** |
| * @param pRootElement |
| * @param pDescriptor |
| * @throws MalformedURLException |
| */ |
| private void parseRequires(Element pRootElement, PluginDescriptor pDescriptor) |
| throws MalformedURLException { |
| |
| NodeList nodelist = pRootElement.getElementsByTagName("requires"); |
| if (nodelist.getLength() > 0) { |
| |
| Element requires = (Element) nodelist.item(0); |
| |
| NodeList imports = requires.getElementsByTagName("import"); |
| for (int i = 0; i < imports.getLength(); i++) { |
| Element anImport = (Element) imports.item(i); |
| String plugin = anImport.getAttribute("plugin"); |
| if (plugin != null) { |
| pDescriptor.addDependency(plugin); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param pRootElement |
| * @param pDescriptor |
| * @throws MalformedURLException |
| */ |
| private void parseLibraries(Element pRootElement, PluginDescriptor pDescriptor) |
| throws MalformedURLException { |
| NodeList nodelist = pRootElement.getElementsByTagName("runtime"); |
| if (nodelist.getLength() > 0) { |
| |
| Element runtime = (Element) nodelist.item(0); |
| |
| NodeList libraries = runtime.getElementsByTagName("library"); |
| for (int i = 0; i < libraries.getLength(); i++) { |
| Element library = (Element) libraries.item(i); |
| String libName = library.getAttribute(ATTR_NAME); |
| NodeList list = library.getElementsByTagName("export"); |
| Element exportElement = (Element) list.item(0); |
| if (exportElement != null) |
| pDescriptor.addExportedLibRelative(libName); |
| else |
| pDescriptor.addNotExportedLibRelative(libName); |
| } |
| } |
| } |
| |
| /** |
| * @param rootElement |
| * @param pluginDescriptor |
| */ |
| private void parseExtensionPoints(Element pRootElement, |
| PluginDescriptor pPluginDescriptor) { |
| NodeList list = pRootElement.getElementsByTagName("extension-point"); |
| if (list != null) { |
| for (int i = 0; i < list.getLength(); i++) { |
| Element oneExtensionPoint = (Element) list.item(i); |
| String id = oneExtensionPoint.getAttribute(ATTR_ID); |
| String name = oneExtensionPoint.getAttribute(ATTR_NAME); |
| String schema = oneExtensionPoint.getAttribute("schema"); |
| ExtensionPoint extensionPoint = new ExtensionPoint(id, name, schema); |
| pPluginDescriptor.addExtensionPoint(extensionPoint); |
| } |
| } |
| } |
| |
| /** |
| * @param rootElement |
| * @param pluginDescriptor |
| */ |
| private void parseExtension(Element pRootElement, |
| PluginDescriptor pPluginDescriptor) { |
| NodeList extensions = pRootElement.getElementsByTagName("extension"); |
| if (extensions != null) { |
| for (int i = 0; i < extensions.getLength(); i++) { |
| Element oneExtension = (Element) extensions.item(i); |
| String pointId = oneExtension.getAttribute("point"); |
| |
| NodeList extensionImplementations = oneExtension.getChildNodes(); |
| if (extensionImplementations != null) { |
| for (int j = 0; j < extensionImplementations.getLength(); j++) { |
| Node node = extensionImplementations.item(j); |
| if (!node.getNodeName().equals("implementation")) { |
| continue; |
| } |
| Element oneImplementation = (Element) node; |
| String id = oneImplementation.getAttribute(ATTR_ID); |
| String extensionClass = oneImplementation.getAttribute(ATTR_CLASS); |
| LOG.debug("impl: point=" + pointId + " class=" + extensionClass); |
| Extension extension = new Extension(pPluginDescriptor, pointId, id, |
| extensionClass, this.conf, this.pluginRepository); |
| NodeList parameters = oneImplementation |
| .getElementsByTagName("parameter"); |
| if (parameters != null) { |
| for (int k = 0; k < parameters.getLength(); k++) { |
| Element param = (Element) parameters.item(k); |
| extension.addAttribute(param.getAttribute(ATTR_NAME), |
| param.getAttribute("value")); |
| } |
| } |
| pPluginDescriptor.addExtension(extension); |
| } |
| } |
| } |
| } |
| } |
| } |