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

org.apache.nifi.util.file.classloader.ClassLoaderUtils Maven / Gradle / Ivy

Go to download

This nifi-utils module should be a general purpose place to store widely and generally useful functions that any component might want to leverage. NO DEPENDENCIES should be added. This module is likely to be leveraged by every extension and should not bring along any other dependencies. The only dependency intended is the nifi-api and even this is expected to be already provided in any case where it would be used. The typical place this util would be found is within a nar and all nars already have nifi-api as a parent dependency. The nifi-api can be thought of as a NiFi Application Container level dependency.

There is a newer version: 2.1.0
Show newest version
/*
 * 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.nifi.util.file.classloader;

import org.apache.nifi.util.security.MessageDigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HexFormat;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ClassLoaderUtils {

    static final Logger LOGGER = LoggerFactory.getLogger(ClassLoaderUtils.class);

    public static ClassLoader getCustomClassLoader(String modulePath, ClassLoader parentClassLoader, FilenameFilter filenameFilter) throws MalformedURLException {
        URL[] classpaths = getURLsForClasspath(modulePath, filenameFilter, false);
        return createModuleClassLoader(classpaths, parentClassLoader);
    }

    /**
     *
     * @param modulePath a module path to get URLs from, the module path may be
     * a comma-separated list of paths
     * @param filenameFilter a filter to apply when a module path is a directory
     * and performs a listing, a null filter will return all matches
     * @param suppressExceptions indicates whether to suppress exceptions
     * @return an array of URL instances representing all of the modules
     * resolved from processing modulePath
     * @throws MalformedURLException if a module path does not exist
     */
    public static URL[] getURLsForClasspath(String modulePath, FilenameFilter filenameFilter, boolean suppressExceptions) throws MalformedURLException {
        return getURLsForClasspath(modulePath == null ? Collections.emptySet() : Collections.singleton(modulePath), filenameFilter, suppressExceptions);
    }

    /**
     *
     * @param modulePaths one or modules paths to get URLs from, each module
     * path may be a comma-separated list of paths
     * @param filenameFilter a filter to apply when a module path is a directory
     * and performs a listing, a null filter will return all matches
     * @param suppressExceptions if true then all modules will attempt to be
     * resolved even if some throw an exception, if false the first exception
     * will be thrown
     * @return an array of URL instances representing all of the modules
     * resolved from processing modulePaths
     * @throws MalformedURLException if a module path does not exist
     */
    public static URL[] getURLsForClasspath(Set modulePaths, FilenameFilter filenameFilter, boolean suppressExceptions) throws MalformedURLException {
        // use LinkedHashSet to maintain the ordering that the incoming paths are processed
        Set modules = new LinkedHashSet<>();
        if (modulePaths != null) {
            modulePaths.stream()
                    .flatMap(path -> Arrays.stream(path.split(",")))
                    .filter(path -> isNotBlank(path))
                    .map(String::trim)
                    .forEach(m -> modules.add(m));
        }
        return toURLs(modules, filenameFilter, suppressExceptions);
    }

    private static boolean isNotBlank(final String value) {
        return value != null && !value.trim().isEmpty();
    }

    protected static URL[] toURLs(Set modulePaths, FilenameFilter filenameFilter, boolean suppressExceptions) throws MalformedURLException {
        List additionalClasspath = new LinkedList<>();
        if (modulePaths != null) {
            for (String modulePathString : modulePaths) {
                // If the path is already a URL, just add it (but don't check if it exists, too expensive and subject to network availability)
                boolean isUrl = true;
                try {
                    additionalClasspath.add(URI.create(modulePathString).toURL());
                } catch (IllegalArgumentException | MalformedURLException e) {
                    isUrl = false;
                }
                if (!isUrl) {
                    try {
                        File modulePath = new File(modulePathString);

                        if (modulePath.exists()) {

                            additionalClasspath.add(modulePath.toURI().toURL());

                            if (modulePath.isDirectory()) {
                                File[] files = modulePath.listFiles(filenameFilter);

                                if (files != null) {
                                    for (File classpathResource : files) {
                                        if (classpathResource.isDirectory()) {
                                            LOGGER.warn("Recursive directories are not supported, skipping {}", classpathResource.getAbsolutePath());
                                        } else {
                                            additionalClasspath.add(classpathResource.toURI().toURL());
                                        }
                                    }
                                }
                            }
                        } else {
                            throw new MalformedURLException("Path specified does not exist");
                        }
                    } catch (MalformedURLException e) {
                        if (!suppressExceptions) {
                            throw e;
                        }
                    }
                }
            }
        }
        return additionalClasspath.toArray(new URL[additionalClasspath.size()]);
    }

    /**
     * Generate fingerprint from URLs associated with classpath resources
     *
     * @param urls URLs used for generating fingerprint string
     * @return Fingerprint string from provided URLs
     */
    public static String generateAdditionalUrlsFingerprint(final Set urls, final String classloaderIsolationKey) {
        final StringBuilder formattedUrls = new StringBuilder();

        final List sortedUrls = urls.stream().map(Object::toString).sorted().collect(Collectors.toList());
        sortedUrls.forEach(url -> formattedUrls.append(url).append("-").append(getLastModified(url)).append(";"));
        formattedUrls.append(classloaderIsolationKey);
        final byte[] formattedUrlsBinary = formattedUrls.toString().getBytes(StandardCharsets.UTF_8);

        return HexFormat.of().formatHex(MessageDigestUtils.getDigest(formattedUrlsBinary));
    }

    private static long getLastModified(String url) {
        long lastModified = 0;
        try {
            final URI uri = new URI(url);
            if (uri.getScheme().equals("file")) {
                final File file = new File(uri);
                lastModified = file.lastModified();
            }
        } catch (URISyntaxException e) {
            LOGGER.error("Error getting last modified date for {}", url);
        }
        return lastModified;
    }

    protected static ClassLoader createModuleClassLoader(URL[] modules, ClassLoader parentClassLoader) {
        return new URLClassLoader(modules, parentClassLoader);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy