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

org.aposin.licensescout.finder.JavaJarFinder Maven / Gradle / Ivy

Go to download

Maven plug-in using the LicenseScout Core for standard Maven builds. Can write reports as CSV, Text or HTML using configurable templates and can write results to a database.

There is a newer version: 1.4.0.RC28
Show newest version
/**
 * Copyright 2019 Association for the promotion of open-source insurance software and for the establishment of open interface standards in the insurance industry (Verein zur Förderung quelloffener Versicherungssoftware und Etablierung offener Schnittstellenstandards in der Versicherungsbranche)
 *
 * Licensed 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.aposin.licensescout.finder;

import static org.aposin.licensescout.archive.ArchiveType.JAVA;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.commons.io.IOUtils;
import org.aposin.licensescout.archive.Archive;
import org.aposin.licensescout.configuration.RunParameters;
import org.aposin.licensescout.license.ArtifactServerUtil;
import org.aposin.licensescout.license.License;
import org.aposin.licensescout.license.LicenseStoreData;
import org.aposin.licensescout.license.LicenseUtil;
import org.aposin.licensescout.util.ArchiveMetaInformation;
import org.aposin.licensescout.util.CryptUtil;
import org.aposin.licensescout.util.ILFLog;
import org.aposin.licensescout.util.JarUtil;

/**
 * Scan for licenses in an Eclipse plugins directory.
 * 
 * 

The implementation does a recursive search through the file system starting with the directory set with {@link #setScanDirectory(File)} as * a starting point.

* */ public class JavaJarFinder extends AbstractFinder { private final List specialArchiveNames = new ArrayList<>(); private final FinderHandler fileSystemFinderHandler; private final FinderHandler jarFinderHandler; private final ArtifactServerUtil artifactServerUtil; /** * Constructor. * * @param licenseStoreData * @param runParameters * @param log the logger */ public JavaJarFinder(final LicenseStoreData licenseStoreData, final RunParameters runParameters, final ILFLog log) { super(licenseStoreData, log); artifactServerUtil = new ArtifactServerUtil(runParameters.getNexusCentralBaseUrl(), runParameters.getConnectTimeout(), log); fileSystemFinderHandler = new FilesystemFinderHandler(log); jarFinderHandler = new JarFinderHandler(log); initSpecialArchiveNames(); } private void initSpecialArchiveNames() { specialArchiveNames.add("ckeditor"); } /** * {@inheritDoc} */ @Override protected boolean isCandidateLicenseFile(final String fileName) { final String fileNameLowerCase = fileName.toLowerCase(); final boolean isTextOrHtmlFile = fileNameLowerCase.endsWith("txt") || fileNameLowerCase.endsWith("html") || fileNameLowerCase.endsWith("htm"); final boolean containsLicenseInFilename = fileNameLowerCase.contains("license") || fileNameLowerCase.contains("licence"); final boolean containsNoticeInFilename = fileNameLowerCase.contains("notice"); return (isTextOrHtmlFile || containsLicenseInFilename || containsNoticeInFilename) && !fileNameLowerCase.endsWith(".class"); } /** * {@inheritDoc} */ @Override protected void findLicensesImpl() throws Exception { final File root = getScanDirectory(); final String filePath = ""; parseFile(root, filePath); } /** *

This method is called recursively as long as no file or directory is detected as being an archive. For archives, the recursion continues with {@link #parsePackedJarArchive(Archive, InputStream, File, String)} or * {@link #parseUnpackedJarArchive(Archive, File, String)}.

* * @param file * @param filePath * * @throws Exception */ private void parseFile(final File file, final String filePath) throws Exception { getLog().debug("parseFile(): processing " + file.getAbsolutePath()); final FinderHandler finderHandler = fileSystemFinderHandler; final String entryName = file.getName(); final FileSystemEntryContainer entryContainer = finderHandler.createEntryContainer(file); if (finderHandler.isDirectory(file)) { if (isArchiveDirectory(file)) { getLog().debug("parseFile(): recognized as archive directory"); final File manifestFile = getManifestFile(file); String version = null; String vendor = null; if (manifestFile != null) { final ArchiveMetaInformation archiveMetaInformation = JarUtil .getArchiveMetaInformationFromManifestFile(manifestFile, getLog()); version = archiveMetaInformation.getVersion(); vendor = archiveMetaInformation.getVendor(); } version = getVersionNotNull(version); final Archive foundArchive = new Archive(JAVA, entryName, version, filePath); foundArchive.setVendor(vendor); archiveFiles.add(foundArchive); parseUnpackedJarArchive(foundArchive, file, filePath); } else { getLog().debug("parseFile(): recognized as normal directory"); final File[] children = file.listFiles(); for (final File child : children) { final String newFilePath = filePath + "/" + child.getName(); parseFile(child, newFilePath); } } } else { // is file if (isArchiveName(entryName)) { getLog().debug("parseFile(): recognized as archive file"); final ArchiveMetaInformation archiveMetaInformation = finderHandler .getArchiveMetaInformationFromManifest(entryContainer); String version = archiveMetaInformation.getVersion(); version = getVersionNotNull(version); final Archive foundArchive = new Archive(JAVA, entryName, version, filePath); foundArchive.setVendor(archiveMetaInformation.getVendor()); archiveFiles.add(foundArchive); addMessageDigest(finderHandler, entryContainer, foundArchive); final String newFilePath = filePath + "!"; try (final FileInputStream archiveFileInputStream = new FileInputStream(file)) { addLicenseFromManifest(foundArchive, archiveMetaInformation, newFilePath); parsePackedJarArchive(foundArchive, archiveFileInputStream, file, newFilePath); } } else { getLog().debug("parseFile(): recognized as normal file - ignored"); } } } private void parseUnpackedJarArchive(final Archive archive, final File parent, final String filePath) throws Exception { getLog().debug("parseUnpackedJarArchive(): processing " + parent.getAbsolutePath()); final FinderHandler finderHandler = fileSystemFinderHandler; final File[] entries = parent.listFiles(); for (final File entry : entries) { final String entryName = finderHandler.getEntryName(entry); final FileSystemEntryContainer entryContainer = finderHandler.createEntryContainer(entry); final String newFilePath = filePath + "/" + entryName; getLog().debug("parseUnpackedJarArchive(): processing " + newFilePath); if (isSpecialArchive(entryName)) { getLog().debug("parseUnpackedJarArchive(): recognized as special archive"); final Archive foundArchive = new Archive(JAVA, entryName, "0.0.0", newFilePath); archiveFiles.add(foundArchive); parseUnpackedJarArchive(foundArchive, entryContainer.getFile(), newFilePath); continue; } if (finderHandler.isDirectory(entry)) { getLog().debug("parseUnpackedJarArchive(): recognized as directory"); if (finderHandler.isUseDirectoryRecursion()) { parseUnpackedJarArchive(archive, entryContainer.getFile(), newFilePath); } } else { // is file if (isArchiveName(entryName)) { getLog().debug("parseUnpackedJarArchive(): recognized as archive file"); final ArchiveMetaInformation archiveMetaInformation = finderHandler .getArchiveMetaInformationFromManifest(entryContainer); String version = archiveMetaInformation.getVersion(); version = getVersionNotNull(version); final Archive foundArchive = new Archive(JAVA, entryName, version, newFilePath); foundArchive.setVendor(archiveMetaInformation.getVendor()); archiveFiles.add(foundArchive); addMessageDigest(finderHandler, entry, entryContainer, foundArchive); final String newFilePath2 = newFilePath + "!"; addLicenseFromManifest(foundArchive, archiveMetaInformation, newFilePath2); try (final FileInputStream archiveInputStream = new FileInputStream(entryContainer.getFile())) { parsePackedJarArchive(foundArchive, archiveInputStream, entry, newFilePath2); } } else { getLog().debug("parseUnpackedJarArchive(): recognized as normal file"); if (isCandidateLicenseFile(entryName)) { archive.addLicenseCandidateFile(newFilePath); } try (final InputStream inputStream = entryContainer.getInputStream()) { final Collection licenses = checkFileForLicenses(inputStream, entryName, getLicenseStoreData()); addLicenses(archive, licenses, entry, newFilePath); } if (isPomFile(entryName)) { addLicensesFromPom(entryContainer, archive, newFilePath, getLog()); } } } } } private void parsePackedJarArchive(final Archive archive, final InputStream fileInputStream, final File parent, final String filePath) throws Exception { final FinderHandler finderHandler = jarFinderHandler; getLog().debug("parsePackedJarArchive(): processing " + parent.getAbsolutePath()); try (final JarInputStream jarInputStream = new JarInputStream(fileInputStream)) { JarEntry entry; while ((entry = jarInputStream.getNextJarEntry()) != null) { final String entryName = finderHandler.getEntryName(entry); final String newFilePath = filePath + "/" + entryName; getLog().debug("parsePackedJarArchive(): processing " + newFilePath); if (isArchiveName(entryName)) { final String newFilePath2 = newFilePath + "!"; final JarEntryContainer entryContainer = finderHandler.createEntryContainer(jarInputStream); final ArchiveMetaInformation archiveMetaInformation = finderHandler .getArchiveMetaInformationFromManifest(entryContainer); final File file = new File(parent, entryName); String version = null; if (finderHandler.isFile(entry)) { version = archiveMetaInformation.getVersion(); } version = getVersionNotNull(version); final String simpleName = getSimpleName(entryName); final Archive foundArchive = new Archive(JAVA, simpleName, version, newFilePath); foundArchive.setVendor(archiveMetaInformation.getVendor()); archiveFiles.add(foundArchive); addMessageDigest(finderHandler, entry, entryContainer, foundArchive); addLicenseFromManifest(foundArchive, archiveMetaInformation, newFilePath2); parsePackedJarArchive(foundArchive, entryContainer.getInputStream(), file, newFilePath2); } else { if (finderHandler.isFile(entry)) { if (isCandidateLicenseFile(entryName)) { archive.addLicenseCandidateFile(newFilePath); } final File file = new File(parent, entryName); final Collection licenses = checkFileForLicenses(jarInputStream, entryName, getLicenseStoreData()); addLicenses(archive, licenses, file, newFilePath); final JarEntryContainer entryContainer = finderHandler.createEntryContainer(jarInputStream); if (isPomFile(entryName)) { addLicensesFromPom(entryContainer, archive, newFilePath, getLog()); } } } } } } private void addLicensesFromPom(final EntryContainer entryContainer, final Archive archive, final String filePath, final ILFLog log) throws Exception { try (final InputStream inputStream = entryContainer.getInputStream()) { artifactServerUtil.addLicensesFromPom(inputStream, archive, filePath, getLicenseStoreData()); } } private String getSimpleName(final String entryName) { final int pos = entryName.lastIndexOf('/'); if (pos >= 0) { return entryName.substring(pos + 1); } return entryName; } private void addMessageDigest(final FinderHandler finderHandler, final File entry, final FileSystemEntryContainer entryContainer, final Archive archive) throws IOException { if (finderHandler.isFile(entry)) { final byte[] md = finderHandler.calculateMessageDigest(entryContainer); archive.setMessageDigest(md); } } private void addMessageDigest(final FinderHandler finderHandler, final FileSystemEntryContainer entryContainer, final Archive archive) throws IOException { if (finderHandler.isFile(entryContainer.getFile())) { final byte[] md = CryptUtil.calculateMessageDigest(entryContainer.getFile()); archive.setMessageDigest(md); } } private void addMessageDigest(final FinderHandler finderHandler, final JarEntry entry, final JarEntryContainer entryContainer, final Archive archive) throws IOException { if (finderHandler.isFile(entry)) { final byte[] md = finderHandler.calculateMessageDigest(entryContainer); archive.setMessageDigest(md); } } private String getVersionNotNull(String version) { if (version == null) { return ""; } return version; } private void addLicenseFromManifest(final Archive archive, final ArchiveMetaInformation archiveMetaInformation, final String filePath) { String licenseUrl = archiveMetaInformation.getLicenseUrl(); if (licenseUrl != null) { licenseUrl = licenseUrl.trim(); final String[] licenseUrls = licenseUrl.split(","); boolean licenseFound = false; final String licenseFileName = filePath + "/META-INF/MANIFEST.MF"; for (String licenseUrl2 : licenseUrls) { licenseUrl2 = licenseUrl2.trim(); licenseFound |= LicenseUtil.handleLicenseUrl(licenseUrl2, archive, licenseFileName, getLicenseStoreData(), getLog()); } if (!licenseFound) { getLog().warn("License not found by URL: " + licenseUrl); } } } /** * Checks if is archive by checking if the name contains "jar". * *

Note: this method only relies on the filename. It does not check containing files that indicate an archive.

* * @param fileName the file name * @return true, if is archive */ private static boolean isArchiveName(final String fileName) { return fileName.endsWith(".jar"); } /** * Checks if passed file is an unpacked JAR file. * * @param dir the file name * @return true, if is archive */ private static boolean isArchiveDirectory(final File dir) { if (!dir.isDirectory()) { return false; } final String[] entries = dir.list(); final List entriesList = Arrays.asList(entries); return entriesList.contains("META-INF"); } /** * Obtains a file object for "META-INF/MANIFEST.MF" in an unpacked JAR. * * @param dir the file name * @return true, if is archive */ private static File getManifestFile(final File dir) { final File[] entries1 = dir.listFiles(); for (final File entry1 : entries1) { if ("META-INF".equalsIgnoreCase(entry1.getName())) { final File[] entries2 = entry1.listFiles(); for (final File entry2 : entries2) { if ("MANIFEST.MF".equalsIgnoreCase(entry2.getName())) { return entry2; } } } } return null; } private boolean isSpecialArchive(final String name) { return specialArchiveNames.contains(name); } private boolean isPomFile(final String name) { return name.endsWith("pom.xml"); } /** * {@inheritDoc} */ @Override public boolean isPomResolutionUsed() { return artifactServerUtil.isCachedCheckAccess(); } @FunctionalInterface private static interface EntryContainer { /** * Obtains an Input stream. * * @return an Input stream * @throws IOException */ public InputStream getInputStream() throws IOException; } private static class FileSystemEntryContainer implements EntryContainer { private final File file; /** * @param file */ public FileSystemEntryContainer(final File file) { this.file = file; } /** * @return the file */ public final File getFile() { return file; } /** * {@inheritDoc} */ @Override public final InputStream getInputStream() throws IOException { return new FileInputStream(getFile()); } } private static class JarEntryContainer implements EntryContainer { private final byte[] contents; /** * @param contents */ public JarEntryContainer(final byte[] contents) { this.contents = contents; } /** * {@inheritDoc} */ @Override public final InputStream getInputStream() { return new ByteArrayInputStream(contents); } } private static interface FinderHandler { /** * Checks if the algorithm should go into a recursion for non-archive directories. * * @return true if directory recursion should be used, false otherwise */ public boolean isUseDirectoryRecursion(); public String getEntryName(F entry); public boolean isFile(F file); public boolean isDirectory(F file); public C createEntryContainer(I ecBase) throws IOException; public ArchiveMetaInformation getArchiveMetaInformationFromManifest(C entryContainer) throws IOException; public byte[] calculateMessageDigest(C entryContainer) throws IOException; } private abstract static class AbstractFinderHandler implements FinderHandler { private final ILFLog log; /** * Constructor. * * @param log the logger */ public AbstractFinderHandler(final ILFLog log) { this.log = log; } /** * @return the log */ protected final ILFLog getLog() { return log; } } private static class FilesystemFinderHandler extends AbstractFinderHandler { /** * Constructor. * * @param log the logger */ public FilesystemFinderHandler(ILFLog log) { super(log); } /** * {@inheritDoc} */ @Override public boolean isUseDirectoryRecursion() { return true; } /** * {@inheritDoc} */ @Override public String getEntryName(final File entry) { return entry.getName(); } /** * {@inheritDoc} */ @Override public boolean isFile(final File file) { return file.isFile(); } /** * {@inheritDoc} */ @Override public boolean isDirectory(final File file) { return file.isDirectory(); } /** * {@inheritDoc} */ @Override public FileSystemEntryContainer createEntryContainer(final File ecBase) throws IOException { return new FileSystemEntryContainer(ecBase); } /** * {@inheritDoc} */ @Override public ArchiveMetaInformation getArchiveMetaInformationFromManifest(final FileSystemEntryContainer entryContainer) throws IOException { return JarUtil.getArchiveMetaInformationFromManifest(entryContainer.getFile(), getLog()); } /** * {@inheritDoc} */ @Override public byte[] calculateMessageDigest(FileSystemEntryContainer entryContainer) throws IOException { return CryptUtil.calculateMessageDigest(entryContainer.getFile()); } } private static class JarFinderHandler extends AbstractFinderHandler { /** * @param log the logger */ public JarFinderHandler(final ILFLog log) { super(log); } /** * {@inheritDoc} */ @Override public boolean isUseDirectoryRecursion() { return true; } /** * {@inheritDoc} */ @Override public String getEntryName(final JarEntry entry) { return entry.getName(); } /** * {@inheritDoc} */ @Override public boolean isFile(final JarEntry entry) { return !entry.isDirectory(); } /** * {@inheritDoc} */ @Override public boolean isDirectory(final JarEntry entry) { return entry.isDirectory(); } /** * {@inheritDoc} */ @Override public JarEntryContainer createEntryContainer(final JarInputStream ecBase) throws IOException { final byte[] archiveBytes = IOUtils.toByteArray(ecBase); return new JarEntryContainer(archiveBytes); } /** * {@inheritDoc} */ @Override public ArchiveMetaInformation getArchiveMetaInformationFromManifest(final JarEntryContainer entryContainer) throws IOException { return JarUtil.getArchiveMetaInformationFromManifest(entryContainer.getInputStream(), getLog()); } /** * {@inheritDoc} */ @Override public byte[] calculateMessageDigest(final JarEntryContainer entryContainer) throws IOException { return CryptUtil.calculateMessageDigest(entryContainer.getInputStream()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy