se.crisp.codekvast.agent.daemon.appversion.ManifestAppVersionStrategy.java Source code

Java tutorial

Introduction

Here is the source code for se.crisp.codekvast.agent.daemon.appversion.ManifestAppVersionStrategy.java

Source

/**
 * Copyright (c) 2015-2016 Crisp AB
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package se.crisp.codekvast.agent.daemon.appversion;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.jar.Attributes;
import java.util.jar.JarFile;

/**
 * A strategy for picking the app version from a jar manifest.
 *
 * It handles the cases {@code manifest some.jar} and {@code manifest some.jar My-Custom-Manifest-Attribute} where some.jar can be either an
 * URI or a file path.
 *
 * The two-arg version uses {@code Implementation-Version} as manifest attribute.
 *
 * @author olle.hallin@crisp.se
 */
@Slf4j
@Component
public class ManifestAppVersionStrategy extends AbstractAppVersionStrategy {

    private static final String DEFAULT_MANIFEST_ATTRIBUTE = "Implementation-Version";

    public ManifestAppVersionStrategy() {
        super("manifest", "search");
    }

    @Override
    public boolean canHandle(String[] args) {
        return args != null && (args.length == 2 || args.length == 3) && recognizes(args[0]);
    }

    @Override
    public String resolveAppVersion(Collection<File> codeBases, String[] args) {
        String jarUri = args[1];
        String manifestAttribute = args.length > 2 ? args[2] : DEFAULT_MANIFEST_ATTRIBUTE;
        for (File codeBaseFile : codeBases) {
            try (JarFile jarFile = new JarFile(getJarFile(codeBaseFile, jarUri))) {
                Attributes attributes = jarFile.getManifest().getMainAttributes();
                String resolvedVersion = attributes.getValue(manifestAttribute);
                if (resolvedVersion != null) {
                    log.debug("{}!/META-INF/MANIFEST.MF:{}={}", jarUri, manifestAttribute, resolvedVersion);
                    return resolvedVersion;
                }
                if (!manifestAttribute.equalsIgnoreCase(DEFAULT_MANIFEST_ATTRIBUTE)) {
                    resolvedVersion = attributes.getValue(DEFAULT_MANIFEST_ATTRIBUTE);
                }
                if (resolvedVersion != null) {
                    log.debug("{}!/META-INF/MANIFEST.MF:{}={}", jarUri, DEFAULT_MANIFEST_ATTRIBUTE,
                            resolvedVersion);
                    return resolvedVersion;
                }
            } catch (Exception e) {
                log.error("Cannot open " + jarUri + ": " + e);
            }
        }
        log.error("Cannot resolve {}!/META-INF/MANIFEST.MF:{}", jarUri, manifestAttribute);
        return UNKNOWN_VERSION;
    }

    private File getJarFile(File codeBaseFile, String jarUri) throws IOException, URISyntaxException {
        URL url = null;
        // try to parse it as a URL...
        try {
            url = new URL(jarUri);
        } catch (MalformedURLException ignore) {
        }

        if (url == null) {
            // Try to treat it as a file...
            File file = new File(jarUri);
            if (file.isFile() && file.canRead() && file.getName().endsWith(".jar")) {
                url = file.toURI().toURL();
            }
        }
        if (url == null) {
            // Search for it in codeBaseFile. Treat it as a regular expression for the basename
            url = search(codeBaseFile, jarUri);
        }

        File result = url == null ? null : new File(url.toURI());
        if (result == null || !result.canRead()) {
            throw new IOException("Cannot read " + jarUri);
        }
        return result;
    }

    private URL search(File dir, String regex) throws MalformedURLException {
        if (!dir.isDirectory()) {
            log.warn("{} is not a directory", dir);
            return null;
        }

        File[] files = dir.listFiles();

        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().matches(regex)) {
                    log.debug("Found {}", file);
                    return new URL(file.toURI().toString());
                }
            }
            for (File file : files) {
                if (file.isDirectory()) {
                    URL url = search(file, regex);
                    if (url != null) {
                        return url;
                    }
                }
            }
        }
        return null;
    }

}