All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.extensions.ExtensionReader Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.extensions;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.metamodel.util.FileHelper;
import org.datacleaner.util.StringUtils;
import org.datacleaner.util.FileFilters;
import org.datacleaner.util.ResourceManager;
import org.datacleaner.util.http.HttpXmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Component used for reading {@link ExtensionPackage} objects from the
 * classpath (aka. "Internal" extensions) and from external files (aka.
 * "External" extensions).
 */
public class ExtensionReader {

    private static final Logger logger = LoggerFactory.getLogger(ExtensionReader.class);

    private final ResourceManager resourceManager = ResourceManager.get();

    public List getInternalExtensions() {
        final List extensionDescriptorUrls = resourceManager.getUrls("datacleaner-extension.xml");
        List result = new ArrayList();
        for (URL url : extensionDescriptorUrls) {
            ExtensionPackage extension = getInternalExtension(url);
            if (extension != null) {
                result.add(extension);
            }
        }
        return result;
    }

    private ExtensionPackage getInternalExtension(URL url) {
        logger.info("Reading extension descriptor: {}", url);
        try {
            final InputStream inputStream = url.openStream();
            try {
                return readExtension(inputStream, new File[0]);
            } finally {
                FileHelper.safeClose(inputStream);
            }
        } catch (Exception e) {
            logger.error("Error reading internal extension of URL: " + url, e);
        }
        return null;
    }

    public ExtensionPackage readExternalExtension(File fileOrDirectory) {
        if (fileOrDirectory == null) {
            return null;
        }
        if (!fileOrDirectory.isDirectory()) {
            return readExternalExtension(new File[] { fileOrDirectory });
        }

        return readExternalExtension(fileOrDirectory.getName(), new File[] { fileOrDirectory });
    }

    public ExtensionPackage readExternalExtension(File[] files) {
        return readExternalExtension(null, files);
    }

    public ExtensionPackage readExternalExtension(String name, final File[] files) {
        final boolean autoDetectPackage;
        final File[] jarFiles;
        if (files.length == 1 && files[0].isDirectory()) {
            jarFiles = files[0].listFiles(FileFilters.JAR);
            autoDetectPackage = false;
        } else {
            jarFiles = files;
            autoDetectPackage = true;
        }

        if (jarFiles.length == 0) {
            return null;
        }

        // check if any of the files has an extension descriptor file
        for (File file : jarFiles) {
            if (file.getName().toLowerCase().endsWith(".jar")) {
                try {
                    try (JarFile jarFile = new JarFile(file)) {
                        JarEntry entry = jarFile.getJarEntry("datacleaner-extension.xml");
                        if (entry == null) {
                            logger.info("No extension descriptor file (datacleaner-extension.xml) found in file: {}",
                                    file);
                        } else {
                            final InputStream inputStream = jarFile.getInputStream(entry);
                            try {
                                final ExtensionPackage extension = readExtension(name, inputStream, jarFiles);
                                return extension;
                            } finally {
                                FileHelper.safeClose(inputStream);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.error("Failed to read from JAR file", e);
                }
            }
        }

        // no extension descriptor files found
        final String extensionName;
        if (StringUtils.isNullOrEmpty(name)) {
            final StringBuilder extensionNameBuilder = new StringBuilder();
            for (File file : files) {
                if (extensionNameBuilder.length() > 0) {
                    extensionNameBuilder.append(", ");
                }
                extensionNameBuilder.append(file.getName());
            }
            extensionName = extensionNameBuilder.toString();
        } else {
            extensionName = name;
        }
        final String packageName = (autoDetectPackage ? autoDetectPackageName(jarFiles[0]) : "");
        final ExtensionPackage extension = new ExtensionPackage(extensionName, packageName, true, files);

        return extension;
    }

    private ExtensionPackage readExtension(InputStream inputStream, File[] files) throws Exception {
        return readExtension(null, inputStream, files);
    }

    private ExtensionPackage readExtension(String name, InputStream inputStream, File[] files) throws Exception {
        final DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        final Document document = documentBuilder.parse(inputStream);
        final Element documentElement = document.getDocumentElement();
        if (StringUtils.isNullOrEmpty(name)) {
            name = HttpXmlUtils.getChildNodeText(documentElement, "name");
        }
        final String scanPackage = HttpXmlUtils.getChildNodeText(documentElement, "package");

        final ExtensionPackage extensionPackage = new ExtensionPackage(name, scanPackage, true, files);

        final String description = HttpXmlUtils.getChildNodeText(documentElement, "description");
        if (!StringUtils.isNullOrEmpty(description)) {
            extensionPackage.getAdditionalProperties().put("description", description);
        }

        final String version = HttpXmlUtils.getChildNodeText(documentElement, "version");
        if (!StringUtils.isNullOrEmpty(version)) {
            extensionPackage.getAdditionalProperties().put("version", version);
        }

        final String icon = HttpXmlUtils.getChildNodeText(documentElement, "icon");
        if (!StringUtils.isNullOrEmpty(icon)) {
            extensionPackage.getAdditionalProperties().put("icon", icon);
        }

        final String url = HttpXmlUtils.getChildNodeText(documentElement, "url");
        if (!StringUtils.isNullOrEmpty(url)) {
            extensionPackage.getAdditionalProperties().put("url", url);
        }

        final String author = HttpXmlUtils.getChildNodeText(documentElement, "author");
        if (!StringUtils.isNullOrEmpty(url)) {
            extensionPackage.getAdditionalProperties().put("author", author);
        }

        return extensionPackage;
    }

    /**
     * Auto-detects a package name based on a JAR file's contents (finding the
     * common denominating package path)
     * 
     * @param file
     * @return
     */
    public String autoDetectPackageName(File file) {
        try {
            Set packageNames = new HashSet();
            try (JarFile jarFile = new JarFile(file)) {
                Enumeration entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    if (name.endsWith(".class")) {
                        logger.debug("Considering package of entry '{}'", name);

                        int lastIndexOfSlash = name.lastIndexOf('/');
                        if (lastIndexOfSlash != -1) {
                            name = name.substring(0, lastIndexOfSlash);
                            packageNames.add(name);
                        }

                    }
                }
            }

            if (packageNames.isEmpty()) {
                return null;
            }

            logger.info("Found {} packages in extension jar: {}", packageNames.size(), packageNames);

            // find the longest common prefix of all the package names
            String packageName = StringUtils.getLongestCommonToken(packageNames, '/');
            if (packageName == "") {
                logger.debug("No common package prefix");
                return null;
            }

            packageName = packageName.replace('/', '.');
            return packageName;
        } catch (Exception e) {
            logger.warn("Error occurred while auto detecting package name", e);
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy