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

org.glassfish.deployment.common.InstalledLibrariesResolver Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.deployment.common;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.logging.annotation.LogMessageInfo;

import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;

/**
 * This class resolves the dependencies between optional packages (installed libraries) and also between
 * apps/stand-alone modules that depend on optional packages (installed libraries)
 *
 * @author Sheetal Vartak
 */

public class InstalledLibrariesResolver {

    /**
     * Installed libraries list (accounts only for "domainRoot/lib/applibs" and not any of "java.ext.dirs" entries)
     */
    private static Map appLibsDirLibsStore = new HashMap<>();

    public static final Logger deplLogger = org.glassfish.deployment.common.DeploymentContextImpl.deplLogger;

    @LogMessageInfo(message = "Optional package {0} does not exist or its Specification-Version does not match. Unable to satisfy dependency for {1}", level = "WARNING")
    private static final String PACKAGE_NOT_FOUND = "NCLS-DEPLOYMENT-00011";

    @LogMessageInfo(message = "Optional package dependency satisfied for {0}", level = "INFO")
    private static final String PACKAGE_SATISFIED = "NCLS-DEPLOYMENT-00012";

    @LogMessageInfo(message = "Error in opening optional package file {0} due to exception: {1}.", level = "WARNING")
    private static final String INVALID_ZIP = "NCLS-DEPLOYMENT-00013";

    @LogMessageInfo(message = "Exception occurred : {0}.", level = "WARNING")
    private static final String EXCEPTION_OCCURRED = "NCLS-DEPLOYMENT-00014";

    @LogMessageInfo(message = "Specification-Version for the optional package [ {0} ] in the jarfile [ {1} ] is not specified. Please provide a valid specification version for this optional package", level = "WARNING")
    private static final String NULL_SPEC_VERS = "NCLS-DEPLOYMENT-00015";

    @LogMessageInfo(message = "Skipping extension processing for {0} due to error: {1}", level = "INFO")
    private static final String SKIPPING_PROCESSING_INFO = "NCLS-DEPLOYMENT-00016";

    /**
     * resolves installed library dependencies
     *
     * @param manifest Manifest File
     * @param archiveUri archive
     * @return status indicating whether all dependencies (transitive) is resolved or not
     */
    public static boolean resolveDependencies(Manifest manifest, String archiveUri) {
        // let us try app-libs directory
        try {
            getInstalledLibraries(archiveUri, manifest, true, appLibsDirLibsStore);
        } catch (MissingResourceException e1) {
            deplLogger.log(WARNING, PACKAGE_NOT_FOUND, new Object[] { e1.getClass(), archiveUri });
            return false;
        }

        deplLogger.log(INFO, PACKAGE_SATISFIED, new Object[] { archiveUri });

        return true;
    }

    /**
     * Check whether the optional packages have all their internal dependencies resolved
     *
     * @param libDir libraryDirectory
     */
    public static void initializeInstalledLibRegistry(String libDir) {
        initializeInstalledLibRegistryForApplibs(libDir);
    }

    public static Set getInstalledLibraries(ReadableArchive archive) throws IOException {
        Set libraries = new HashSet<>();
        if (archive == null) {
            return libraries;
        }

        Manifest manifest = archive.getManifest();

        // We are looking for libraries only in "applibs" directory, hence strict=false
        Set installedLibraries = getInstalledLibraries(archive.getURI().toString(), manifest, false, appLibsDirLibsStore);
        libraries.addAll(installedLibraries);

        // Now check any embedded libraries.
        for (String libUri : getEmbeddedLibraries(archive)) {
            try (InputStream embeddedLibInputStream = archive.getEntry(libUri)) {
                if (embeddedLibInputStream == null) {
                    // embeddedLibInputStream can be null if reading an exploded archive where directories are also exploded. See
                    // FileArchive.getEntry()
                    continue;
                }

                manifest = new JarInputStream(embeddedLibInputStream).getManifest();
                if (manifest != null) {
                    // We are looking for libraries only in "applibs" directory, hence strict=false
                    libraries.addAll(getInstalledLibraries(archive.getURI().toString(), manifest, false, appLibsDirLibsStore));
                }
            }
        }

        return libraries;
    }

    private static Set getInstalledLibraries(String archiveURI, Manifest manifest, boolean strict, Map libraryStore) {
        Set libraries = new HashSet<>();
        String extensionList = null;
        try {
            if (manifest == null) {
                return libraries;
            }
            extensionList = manifest.getMainAttributes().getValue(Attributes.Name.EXTENSION_LIST);
            if (deplLogger.isLoggable(FINE)) {
                deplLogger.log(FINE, "Extension-List for archive [{0}] : ", new Object[] {archiveURI, extensionList});
            }
        } catch (Exception e) {
            // Ignore this exception
            deplLogger.log(FINE, "InstalledLibrariesResolver : exception occurred.", e);
        }

        if (extensionList != null) {
            StringTokenizer extensionListTokenizer = new StringTokenizer(extensionList, " ");
            while (extensionListTokenizer.hasMoreTokens()) {

                String token = extensionListTokenizer.nextToken().trim();
                String extensionName = manifest.getMainAttributes().getValue(token + "-" + Attributes.Name.EXTENSION_NAME);
                if (extensionName != null) {
                    extensionName = extensionName.trim();
                }

                String specVersion = manifest.getMainAttributes().getValue(token + "-" + Attributes.Name.SPECIFICATION_VERSION);

                Extension extension = new Extension(extensionName);
                if (specVersion != null) {
                    extension.setSpecVersion(specVersion);
                }

                // TODO possible NPE when extension is unspecified
                boolean isLibraryInstalled = libraryStore.containsKey(extension);
                if (isLibraryInstalled) {
                    libraries.add(libraryStore.get(extension));
                } else if (strict) {
                    throw new MissingResourceException(extensionName + " not found", extensionName, null);
                }

                if (deplLogger.isLoggable(FINEST)) {
                    deplLogger.log(FINEST,
                            " is library installed [" + extensionName + "] " + "for archive [" + archiveURI + "]: " + isLibraryInstalled);
                }
            }
        }

        return libraries;
    }

    /**
     * @return a list of libraries included in the archive
     */
    private static List getEmbeddedLibraries(ReadableArchive archive) {
        Enumeration entries = archive.entries();
        if (entries == null) {
            emptyList();
        }

        List libs = new ArrayList<>();
        while (entries.hasMoreElements()) {
            String entryName = entries.nextElement();
            if (entryName.indexOf('/') != -1) {
                continue; // not on the top level
            }

            if (entryName.endsWith(".jar")) {
                libs.add(entryName);
            }
        }

        return libs;
    }

    /**
     * Initialize the "applibs" part of installed libraries ie., any library within "applibs" which represents an extension
     * (EXTENSION_NAME in MANIFEST.MF) that can be used by applications (as EXTENSION_LIST in their MANIFEST.MF)
     *
     * @param domainLibDir library directory (of a domain)
     */
    private static void initializeInstalledLibRegistryForApplibs(String domainLibDir) {
        String applibsDirString = domainLibDir + File.separator + "applibs";
        deplLogger.fine("applib-Dir-String..." + applibsDirString);

        List validApplibsDirLibFiles = new ArrayList<>();
        Map installedLibraries = getInstalledLibraries(applibsDirString, null, validApplibsDirLibFiles);
        appLibsDirLibsStore.putAll(installedLibraries);

        for (File file : validApplibsDirLibFiles) {
            JarFile jarFile = null;
            try {
                jarFile = new JarFile(file);
                Manifest manifest = jarFile.getManifest();
                if (manifest != null) {
                    try {
                        getInstalledLibraries(file.getAbsolutePath(), manifest, true, appLibsDirLibsStore);
                    } catch (MissingResourceException mre) {
                        deplLogger.log(WARNING, PACKAGE_NOT_FOUND, new Object[] { mre.getClass(), file.getAbsolutePath() });
                    }
                }
            } catch (IOException ioe) {
                deplLogger.log(WARNING, INVALID_ZIP, new Object[] { file.getAbsolutePath(), ioe.getMessage() });
            } finally {
                if (jarFile != null) {
                    try {
                        jarFile.close();
                    } catch (IOException e) {
                        deplLogger.log(WARNING, EXCEPTION_OCCURRED, new Object[] { e.getMessage() });
                    }
                }
            }
        }
    }

    private static Map getInstalledLibraries(String libraryDirectoryName, Set processedLibraryNames, List processedLibraries) {
        Map installedLibraries = new HashMap<>();

        File dir = new File(libraryDirectoryName);

        if (deplLogger.isLoggable(FINE)) {
            deplLogger.log(FINE, "installed library directory : " + dir);
        }

        File[] libraries = dir.listFiles();
        if (libraries != null) {
            try {
                for (File element : libraries) {

                    if (deplLogger.isLoggable(FINE)) {
                        deplLogger.log(FINE, "installed library : " + element);
                    }

                    /*
                     * Skip any candidate that does not end with .jar or is a directory.
                     */
                    if (element.isDirectory()) {
                        deplLogger.log(FINE, "Skipping installed library processing on " + element.getAbsolutePath() + "; it is a directory");
                        continue;
                    }

                    if (!element.getName().toLowerCase(Locale.getDefault()).endsWith(".jar")) {
                        deplLogger.log(FINE, "Skipping installed library processing on " + element.getAbsolutePath()
                                + "; it does not appear to be a JAR file based on its file type");
                        continue;
                    }

                    JarFile jarFile = null;
                    try {
                        jarFile = new JarFile(element);

                        Manifest manifest = jarFile.getManifest();
                        if (processedLibraryNames != null) {
                            processedLibraryNames.add(element.toString());
                        }
                        if (processedLibraries != null) {
                            processedLibraries.add(element);
                        }

                        // Extension-Name of optional package
                        if (manifest != null) {
                            String extName = manifest.getMainAttributes().getValue(Attributes.Name.EXTENSION_NAME);
                            String specVersion = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION);
                            deplLogger.fine("Extension " + element.getAbsolutePath() + ", extNameOfOPtionalPkg..." + extName
                                    + ", specVersion..." + specVersion);
                            if (extName != null) {
                                if (specVersion == null) {
                                    deplLogger.log(WARNING, NULL_SPEC_VERS, new Object[] { extName, jarFile.getName() });
                                    specVersion = "";
                                }

                                Extension extension = new Extension(extName);
                                extension.setSpecVersion(specVersion);

                                installedLibraries.put(extension, element.getName());
                            }
                        }
                    } catch (Throwable thr) {
                        String msg = deplLogger.getResourceBundle().getString("enterprise.deployment.backend.optionalpkg.dependency.error");
                        if (deplLogger.isLoggable(FINE)) {
                            deplLogger.log(FINE, MessageFormat.format(msg, element.getAbsolutePath(), thr.getMessage()), thr);
                        } else {
                            LogRecord lr = new LogRecord(INFO, SKIPPING_PROCESSING_INFO);
                            lr.setParameters(new Object[] { element.getAbsolutePath(), thr.getMessage() });
                            deplLogger.log(lr);
                        }
                    } finally {
                        if (jarFile != null) {
                            jarFile.close();
                        }
                    }
                }
            } catch (IOException e) {
                deplLogger.log(WARNING, "enterprise.deployment.backend.optionalpkg.dependency.exception",
                        new Object[] { e.getMessage() });
            }
        }

        return installedLibraries;
    }

    static class Extension {
        private String extensionName;
        private String specVersion = "";
        private String specVendor = "";
        private String implVersion = "";
        private String implVendor = "";

        public Extension(String name) {
            this.extensionName = name;
        }

        public String getExtensionName() {
            return extensionName;
        }

        public void setExtensionName(String extensionName) {
            this.extensionName = extensionName;
        }

        public String getSpecVersion() {
            return specVersion;
        }

        public void setSpecVersion(String specVersion) {
            this.specVersion = specVersion;
        }

        public String getSpecVendor() {
            return specVendor;
        }

        public void setSpecVendor(String specVendor) {
            this.specVendor = specVendor;
        }

        public String getImplVersion() {
            return implVersion;
        }

        public void setImplVersion(String implVersion) {
            this.implVersion = implVersion;
        }

        public String getImplVendor() {
            return implVendor;
        }

        public void setImplVendor(String implVendor) {
            this.implVendor = implVendor;
        }

        @Override
        public boolean equals(Object o) {
            if (o != null && o instanceof Extension) {
                Extension e = (Extension) o;
                if ((o == this) || (e.getExtensionName().equals(extensionName)
                        && (e.getImplVendor().equals(implVendor) || (e.getImplVendor().equals("")))
                        && (e.getImplVersion().equals(implVersion) || (e.getImplVersion().equals("")))
                        && (e.getSpecVendor().equals(specVendor) || (e.getSpecVendor().equals("")))
                        && (e.getSpecVersion().equals(specVersion) || (e.getSpecVersion().equals(""))))) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            int result = 17;
            result = 37 * result + extensionName.hashCode();
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy