org.pentaho.platform.plugin.services.pluginmgr.SystemPathXmlPluginProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.plugin.services.pluginmgr.SystemPathXmlPluginProvider.java

Source

/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 *
 * Copyright (c) 2002-2019 Hitachi Vantara. All rights reserved.
 */

package org.pentaho.platform.plugin.services.pluginmgr;

import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.pentaho.platform.api.engine.CsrfProtectionDefinition;
import org.pentaho.platform.api.engine.IContentGeneratorInfo;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IPlatformPlugin;
import org.pentaho.platform.api.engine.IPluginProvider;
import org.pentaho.platform.api.engine.PlatformPluginRegistrationException;
import org.pentaho.platform.api.engine.PluginBeanDefinition;
import org.pentaho.platform.api.engine.PluginServiceDefinition;
import org.pentaho.platform.api.engine.perspective.pojo.IPluginPerspective;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.engine.core.solution.ContentGeneratorInfo;
import org.pentaho.platform.engine.core.solution.ContentInfo;
import org.pentaho.platform.engine.core.solution.PluginOperation;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.services.SolutionURIResolver;
import org.pentaho.platform.engine.services.actionsequence.ActionSequenceResource;
import org.pentaho.platform.plugin.services.messages.Messages;
import org.pentaho.platform.util.logging.Logger;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.platform.util.xml.XMLParserFactoryProducer;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;
import org.pentaho.platform.web.WebUtil;
import org.pentaho.ui.xul.impl.DefaultXulOverlay;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * An implementation of {@link IPluginProvider} that searches for plugin.xml files in the Pentaho system path and
 * instantiates {@link IPlatformPlugin}s from the information in those files.
 * 
 * @author aphillips
 */
public class SystemPathXmlPluginProvider implements IPluginProvider {

    /**
     * Gets the list of plugins that this provider class has discovered.
     * 
     * @return an read-only list of plugins
     * @see IPluginProvider#getPlugins()
     * @throws PlatformPluginRegistrationException
     *           if there is a problem preventing the impl from looking for plugins
     */
    public List<IPlatformPlugin> getPlugins(IPentahoSession session) throws PlatformPluginRegistrationException {
        List<IPlatformPlugin> plugins = new ArrayList<IPlatformPlugin>();

        // look in each of the system setting folders looking for plugin.xml files
        String systemPath = PentahoSystem.getApplicationContext().getSolutionPath("system"); //$NON-NLS-1$
        File systemDir = new File(systemPath);
        if (!systemDir.exists() || !systemDir.isDirectory()) {
            throw new PlatformPluginRegistrationException(
                    Messages.getInstance().getErrorString("PluginManager.ERROR_0004_CANNOT_FIND_SYSTEM_FOLDER")); //$NON-NLS-1$
        }
        File[] kids = systemDir.listFiles();
        // look at each child to see if it is a folder
        for (File kid : kids) {
            if (kid.isDirectory()) {
                try {
                    processDirectory(plugins, kid, session);
                } catch (Throwable t) {
                    // don't throw an exception. we need to continue to process any remaining good plugins
                    String msg = Messages.getInstance().getErrorString(
                            "SystemPathXmlPluginProvider.ERROR_0001_FAILED_TO_PROCESS_PLUGIN", //$NON-NLS-1$
                            kid.getAbsolutePath());
                    Logger.error(getClass().toString(), msg, t);
                    PluginMessageLogger.add(msg);
                }
            }
        }

        return Collections.unmodifiableList(plugins);
    }

    protected void processDirectory(List<IPlatformPlugin> plugins, File folder, IPentahoSession session)
            throws PlatformPluginRegistrationException {
        // see if there is a plugin.xml file
        FilenameFilter filter = new NameFileFilter("plugin.xml", IOCase.SENSITIVE); //$NON-NLS-1$
        File[] kids = folder.listFiles(filter);
        if (kids == null || kids.length == 0) {
            return;
        }
        boolean hasLib = false;
        filter = new NameFileFilter("lib", IOCase.SENSITIVE); //$NON-NLS-1$
        kids = folder.listFiles(filter);
        if (kids != null && kids.length > 0) {
            hasLib = kids[0].exists() && kids[0].isDirectory();
        }
        // we have found a plugin.xml file
        // get the file from the repository
        String path = "system" + RepositoryFile.SEPARATOR + folder.getName() + RepositoryFile.SEPARATOR //$NON-NLS-1$
                + "plugin.xml"; //$NON-NLS-1$
        Document doc = null;
        try {
            try {
                org.dom4j.io.SAXReader reader = XMLParserFactoryProducer.getSAXReader(new SolutionURIResolver());
                doc = reader.read(ActionSequenceResource.getInputStream(path, LocaleHelper.getLocale()));
            } catch (Throwable t) {
                // XML document can't be read. We'll just return a null document.
            }
            if (doc != null) {
                plugins.add(createPlugin(doc, session, folder.getName(), hasLib));
            }
        } catch (Exception e) {
            throw new PlatformPluginRegistrationException(Messages.getInstance()
                    .getErrorString("PluginManager.ERROR_0005_CANNOT_PROCESS_PLUGIN_XML", path), e); //$NON-NLS-1$
        }
        if (doc == null) {
            throw new PlatformPluginRegistrationException(Messages.getInstance()
                    .getErrorString("PluginManager.ERROR_0005_CANNOT_PROCESS_PLUGIN_XML", path)); //$NON-NLS-1$
        }
    }

    protected PlatformPlugin createPlugin(Document doc, IPentahoSession session, String folder, boolean hasLib) {
        PlatformPlugin plugin = new PlatformPlugin();

        processStaticResourcePaths(plugin, doc, session);
        processPluginInfo(plugin, doc, folder, session);
        processContentTypes(plugin, doc, session);
        processContentGenerators(plugin, doc, session, folder, hasLib);
        processOverlays(plugin, doc, session);
        processLifecycleListeners(plugin, doc);
        processBeans(plugin, doc);
        processWebservices(plugin, doc);
        processExternalResources(plugin, doc);
        processPerspectives(plugin, doc);
        processCsrfProtection(plugin, doc);

        String listenerCount = (StringUtils.isEmpty(plugin.getLifecycleListenerClassname())) ? "0" : "1"; //$NON-NLS-1$//$NON-NLS-2$

        String msg = Messages.getInstance().getString("SystemPathXmlPluginProvider.PLUGIN_PROVIDES", //$NON-NLS-1$
                Integer.toString(plugin.getContentInfos().size()),
                Integer.toString(plugin.getContentGenerators().size()),
                Integer.toString(plugin.getOverlays().size()), listenerCount);
        PluginMessageLogger.add(msg);

        plugin.setSourceDescription(folder);

        return plugin;
    }

    /**
     * @param plugin
     * @param doc
     */
    protected void processPerspectives(PlatformPlugin plugin, Document doc) {
        // TODO Auto-generated method stub
        List<?> nodes = doc.selectNodes("/*/perspective"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;
            if (node != null) {
                IPluginPerspective perspective = PerspectiveUtil.createPerspective(node);
                plugin.addPluginPerspective(perspective);
            }
        }
    }

    protected void processStaticResourcePaths(PlatformPlugin plugin, Document doc, IPentahoSession session) {
        List<?> nodes = doc.selectNodes("//static-path"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;
            if (node != null) {
                String url = node.attributeValue("url"); //$NON-NLS-1$
                String localFolder = node.attributeValue("localFolder"); //$NON-NLS-1$
                plugin.addStaticResourcePath(url, localFolder);
            }
        }
    }

    protected void processExternalResources(PlatformPlugin plugin, Document doc) {
        Node parentNode = doc.selectSingleNode("//external-resources"); //$NON-NLS-1$
        if (parentNode == null) {
            return;
        }
        for (Object obj : parentNode.selectNodes("file")) {
            Element node = (Element) obj;
            if (node != null) {
                String context = node.attributeValue("context"); //$NON-NLS-1$
                String resource = node.getStringValue();
                plugin.addExternalResource(context, resource);
            }
        }
    }

    protected void processLifecycleListeners(PlatformPlugin plugin, Document doc) {
        Element node = (Element) doc.selectSingleNode("//lifecycle-listener"); //$NON-NLS-1$
        if (node != null) {
            String classname = node.attributeValue("class"); //$NON-NLS-1$
            plugin.setLifecycleListenerClassname(classname);
        }
    }

    protected void processBeans(PlatformPlugin plugin, Document doc) {
        List<?> nodes = doc.selectNodes("//bean"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;
            if (node != null) {
                plugin.addBean(new PluginBeanDefinition(node.attributeValue("id"), node.attributeValue("class"))); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
    }

    protected void processWebservices(PlatformPlugin plugin, Document doc) {
        List<?> nodes = doc.selectNodes("//webservice"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;

            PluginServiceDefinition pws = new PluginServiceDefinition();

            pws.setId(getProperty(node, "id")); //$NON-NLS-1$
            String type = getProperty(node, "type"); //$NON-NLS-1$
            if (!StringUtils.isEmpty(type)) {
                pws.setTypes(type.split(",")); //$NON-NLS-1$
            }
            pws.setTitle(getProperty(node, "title")); //$NON-NLS-1$
            pws.setDescription(getProperty(node, "description")); //$NON-NLS-1$

            // TODO: add support for inline service class definition
            pws.setServiceBeanId(getProperty(node, "ref")); //$NON-NLS-1$
            pws.setServiceClass(getProperty(node, "class")); //$NON-NLS-1$

            Collection<String> extraClasses = new ArrayList<String>();
            List<?> extraNodes = node.selectNodes("extra"); //$NON-NLS-1$
            for (Object extra : extraNodes) {
                Element extraElement = (Element) extra;
                String extraClass = getProperty(extraElement, "class"); //$NON-NLS-1$
                if (extraClasses != null) {
                    extraClasses.add(extraClass);
                }
            }
            pws.setExtraClasses(extraClasses);

            if (pws.getServiceBeanId() == null && pws.getServiceClass() == null) {
                PluginMessageLogger.add(Messages.getInstance().getString("PluginManager.NO_SERVICE_CLASS_FOUND")); //$NON-NLS-1$
            } else {
                plugin.addWebservice(pws);
            }
        }
    }

    protected void processPluginInfo(PlatformPlugin plugin, Document doc, String folder, IPentahoSession session) {
        Element node = (Element) doc.selectSingleNode("/plugin"); //$NON-NLS-1$

        // "name" is the attribute that unique identifies a plugin. It acts as the plugin ID. For backwards compatibility,
        // if name is not provided, name is set to the value of the "title" attribute
        //
        if (node != null) {
            String name = (node.attributeValue("name") != null) ? node.attributeValue("name") //$NON-NLS-1$//$NON-NLS-2$
                    : node.attributeValue("title"); //$NON-NLS-1$
            if (StringUtils.isEmpty(name)) {
                String msg = Messages.getInstance()
                        .getErrorString("SystemPathXmlPluginProvider.ERROR_0002_PLUGIN_INVALID", folder); //$NON-NLS-1$
                PluginMessageLogger.add(msg);
                Logger.error(getClass().toString(), msg);
            }

            plugin.setId(name);
            PluginMessageLogger.add(Messages.getInstance()
                    .getString("SystemPathXmlPluginProvider.DISCOVERED_PLUGIN", name, folder)); //$NON-NLS-1$

            IPlatformPlugin.ClassLoaderType loaderType = IPlatformPlugin.ClassLoaderType.DEFAULT;
            String loader = node.attributeValue("loader"); //$NON-NLS-1$
            if (!StringUtils.isEmpty(loader)) {
                loaderType = IPlatformPlugin.ClassLoaderType.valueOf(loader.toUpperCase());
            }
            plugin.setLoadertype(loaderType);
        }
    }

    protected void processOverlays(PlatformPlugin plugin, Document doc, IPentahoSession session) {
        // look for content types
        List<?> nodes = doc.selectNodes("//overlays/overlay"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;
            DefaultXulOverlay overlay = processOverlay(node);

            if (overlay != null) {
                plugin.addOverlay(overlay);
            }
        }
    }

    public static DefaultXulOverlay processOverlay(Element node) {
        DefaultXulOverlay overlay = null;

        String id = node.attributeValue("id"); //$NON-NLS-1$
        String resourceBundleUri = node.attributeValue("resourcebundle"); //$NON-NLS-1$
        String priority = node.attributeValue("priority");

        String xml = node.asXML();

        if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(xml)) {
            // check for overlay priority attribute. if not present, do not provide one
            // so default will be used
            if (StringUtils.isNotEmpty(priority)) {
                try {
                    overlay = new DefaultXulOverlay(id, null, xml, resourceBundleUri, Integer.parseInt(priority));
                } catch (NumberFormatException e) {
                    // don't fail if attribute value is invalid. just use alt constructor without priority
                    overlay = new DefaultXulOverlay(id, null, xml, resourceBundleUri);
                }
            } else {
                overlay = new DefaultXulOverlay(id, null, xml, resourceBundleUri);
            }
        }

        return overlay;
    }

    protected void processContentTypes(PlatformPlugin plugin, Document doc, IPentahoSession session) {
        // look for content types
        List<?> nodes = doc.selectNodes("//content-type"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;

            String title = XmlDom4JHelper.getNodeText("title", node); //$NON-NLS-1$
            String extension = node.attributeValue("type"); //$NON-NLS-1$

            if (title != null && extension != null) {
                String description = XmlDom4JHelper.getNodeText("description", node, ""); //$NON-NLS-1$ //$NON-NLS-2$
                String mimeType = node.attributeValue("mime-type", ""); //$NON-NLS-1$ //$NON-NLS-2$
                String iconUrl = XmlDom4JHelper.getNodeText("icon-url", node, ""); //$NON-NLS-1$ //$NON-NLS-2$

                ContentInfo contentInfo = new ContentInfo();
                contentInfo.setDescription(description);
                contentInfo.setTitle(title);
                contentInfo.setExtension(extension);
                contentInfo.setMimeType(mimeType);
                contentInfo.setIconUrl(iconUrl);

                List<?> operationNodes = node.selectNodes("operations/operation"); //$NON-NLS-1$
                for (Object operationObj : operationNodes) {
                    Element operationNode = (Element) operationObj;
                    String id = XmlDom4JHelper.getNodeText("id", operationNode, ""); //$NON-NLS-1$ //$NON-NLS-2$
                    String perspective = XmlDom4JHelper.getNodeText("perspective", operationNode, ""); //$NON-NLS-1$ //$NON-NLS-2$
                    if (StringUtils.isNotEmpty(id)) {
                        PluginOperation operation = new PluginOperation(id);
                        if (StringUtils.isNotEmpty(perspective)) {
                            operation.setPerspective(perspective);
                        }

                        contentInfo.addOperation(operation);
                    }
                }

                plugin.addContentInfo(contentInfo);
                PluginMessageLogger.add(Messages.getInstance()
                        .getString("PluginManager.USER_CONTENT_TYPE_REGISTERED", extension, title)); //$NON-NLS-1$
            } else {
                PluginMessageLogger.add(Messages.getInstance()
                        .getString("PluginManager.USER_CONTENT_TYPE_NOT_REGISTERED", extension, title)); //$NON-NLS-1$
            }
        }
    }

    /*
     * Finds propName as either an attribute of the given node or the text element of a child element called propName
     */
    private static String getProperty(Element node, String propName) {
        String propValue = null;
        propValue = node.attributeValue(propName);
        if (propValue == null) {
            propValue = XmlDom4JHelper.getNodeText(propName, node, null);
        }
        return propValue;
    }

    protected void processContentGenerators(PlatformPlugin plugin, Document doc, IPentahoSession session,
            String folder, boolean hasLib) {
        // look for content generators
        List<?> nodes = doc.selectNodes("//content-generator"); //$NON-NLS-1$
        for (Object obj : nodes) {
            Element node = (Element) obj;

            String className = getProperty(node, "class"); //$NON-NLS-1$
            if (className == null) {
                className = XmlDom4JHelper.getNodeText("classname", node, null); //$NON-NLS-1$
            }
            String id = node.attributeValue("id"); //$NON-NLS-1$
            String type = node.attributeValue("type"); //$NON-NLS-1$
            String url = node.attributeValue("url"); //$NON-NLS-1$
            String title = getProperty(node, "title"); //$NON-NLS-1$
            String description = getProperty(node, "description"); //$NON-NLS-1$
            try {
                if (id != null && type != null && className != null && title != null) {
                    try {
                        IContentGeneratorInfo info = createContentGenerator(plugin, id, title, description, type,
                                url, className, session, folder);
                        plugin.addContentGenerator(info);
                    } catch (Exception e) {
                        PluginMessageLogger.add(Messages.getInstance()
                                .getString("PluginManager.USER_CONTENT_GENERATOR_NOT_REGISTERED", id, folder)); //$NON-NLS-1$
                    }
                } else {
                    PluginMessageLogger.add(Messages.getInstance()
                            .getString("PluginManager.USER_CONTENT_GENERATOR_NOT_REGISTERED", id, folder)); //$NON-NLS-1$
                }
            } catch (Exception e) {
                PluginMessageLogger.add(Messages.getInstance()
                        .getString("PluginManager.USER_CONTENT_GENERATOR_NOT_REGISTERED", id, folder)); //$NON-NLS-1$
                Logger.error(getClass().toString(), Messages.getInstance().getErrorString(
                        "PluginManager.ERROR_0006_CANNOT_CREATE_CONTENT_GENERATOR_FACTORY", folder), e); //$NON-NLS-1$
            }
        }
    }

    private static IContentGeneratorInfo createContentGenerator(PlatformPlugin plugin, String id, String title,
            String description, String type, String url, String className, IPentahoSession session, String location)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        ContentGeneratorInfo info = new ContentGeneratorInfo();
        info.setId(id);
        info.setTitle(title);
        info.setDescription(description);
        info.setUrl((url != null) ? url : ""); //$NON-NLS-1$
        info.setType(type);
        info.setClassname(className);

        return info;
    }

    protected void processCsrfProtection(PlatformPlugin plugin, Document doc) {

        Node csrfProtectionElem = doc.selectSingleNode("*/csrf-protection");
        if (csrfProtectionElem != null) {
            try {
                CsrfProtectionDefinition protectionDefinition = WebUtil
                        .parseXmlCsrfProtectionDefinition((Element) csrfProtectionElem);

                if (protectionDefinition != null) {
                    plugin.setCsrfProtection(protectionDefinition);
                }
            } catch (IllegalArgumentException parseError) {
                PluginMessageLogger.add(
                        Messages.getInstance().getString("PluginManager.WARN_CSRF_REQUEST_MATCHER_NOT_REGISTERED",
                                plugin.getId(), parseError.getMessage()));
            }
        }
    }
}