blob: 6d3a73927dafe961a1f99df26af17f85d23348f7 [file] [log] [blame]
/*
* 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.creadur.tentacles;
import static org.apache.creadur.tentacles.LicenseType.loadLicensesFrom;
import static org.apache.creadur.tentacles.RepositoryType.HTTP;
import static org.apache.creadur.tentacles.RepositoryType.LOCAL_FILE_SYSTEM;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.logging.log4j.*;
public class Main {
static {
/* TENTACLES-12: disabled root logger configuration
final Logger root = LogManager.getRootLogger();
root.addAppender(new ConsoleAppender(new PatternLayout(
PatternLayout.TTCC_CONVERSION_PATTERN)));
root.setLevel(Level.INFO);
*/
}
private static final Logger log = LogManager.getLogger(Main.class);
private static final String CRAWL_PATTERN = ".*\\.(jar|zip|war|ear|rar|tar.gz)";
private final Reports reports;
private final Licenses licenses;
private final Layout layout;
private final Platform platform;
private final Configuration configuration;
private final FileSystem fileSystem;
private final IOSystem ioSystem;
private final TentaclesResources tentaclesResources;
private final Templates templates;
public Main(final String... args) throws Exception {
this(new Configuration(args), Platform.aPlatform());
}
public Main(final Configuration configuration, final Platform platform)
throws Exception {
this(configuration, platform, new Templates(platform), new Layout(
platform, configuration));
}
public Main(final Configuration configuration, final Platform platform,
final Templates templates, final Layout layout) throws Exception {
this.platform = platform;
this.configuration = configuration;
this.layout = layout;
this.fileSystem = platform.getFileSystem();
this.ioSystem = platform.getIoSystem();
this.tentaclesResources = platform.getTentaclesResources();
this.templates = templates;
this.reports = new Reports();
log.info("Remote repository: "
+ this.configuration.getStagingRepositoryURI());
log.info("Local root directory: " + this.layout.getLocalRootDirectory());
this.tentaclesResources.copyTo("legal/style.css",
new File(this.layout.getOutputDirectory(), "style.css"));
this.licenses = loadLicensesFrom(platform);
}
public static void main(final String[] args) throws Exception {
log.info("Launching Apache Tentacles ...");
if(args == null || args.length < 1) {
log.error("Error: Input parameter missing - you did not specify any component to run Apache Tentacles on.");
log.error("Please launch Apache Tentacles with an URI to work on such as 'https://repository.apache.org/content/repositories/orgapachecreadur-1000/'.");
} else {
new Main(args).main();
}
}
private void main() throws Exception {
unpackContents(mirrorRepositoryFrom(this.configuration));
reportOn(archivesIn(this.layout.getRepositoryDirectory()));
}
private List<Archive> archivesIn(final File repository) {
final List<File> jars = this.fileSystem.documentsFrom(repository);
final List<Archive> archives = new ArrayList<Archive>();
for (final File file : jars) {
final Archive archive =
new Archive(file, this.fileSystem, this.layout);
archives.add(archive);
}
return archives;
}
private void reportOn(final List<Archive> archives) throws IOException {
this.templates
.template("legal/archives.vm")
.add("archives", archives)
.add("reports", this.reports)
.write(new File(this.layout.getOutputDirectory(),
"archives.html"));
reportLicenses(archives);
reportNotices(archives);
reportDeclaredLicenses(archives);
reportDeclaredNotices(archives);
}
private void reportLicenses(final List<Archive> archives)
throws IOException {
initLicenses(archives);
this.templates
.template("legal/licenses.vm")
.add("licenses", getLicenses(archives))
.add("reports", this.reports)
.write(new File(this.layout.getOutputDirectory(),
"licenses.html"));
}
private void initLicenses(final List<Archive> archives) throws IOException {
final Map<License, License> licenses = new HashMap<License, License>();
for (final Archive archive : archives) {
final List<File> files =
this.fileSystem.licensesFrom(archive.contentsDirectory());
for (final File file : files) {
final License license = this.licenses.from(file);
License existing = licenses.get(license);
if (existing == null) {
licenses.put(license, license);
existing = license;
}
existing.getLocations().add(file);
existing.getArchives().add(archive);
archive.getLicenses().add(existing);
}
}
}
private Collection<License> getLicenses(final List<Archive> archives) {
final Set<License> licenses = new LinkedHashSet<License>();
for (final Archive archive : archives) {
licenses.addAll(archive.getLicenses());
}
return licenses;
}
private void reportDeclaredLicenses(final List<Archive> archives)
throws IOException {
for (final Archive archive : archives) {
classifyLicenses(archive);
}
for (final Archive archive : archives) {
this.templates
.template("legal/archive-licenses.vm")
.add("archive", archive)
.add("reports", this.reports)
.write(new File(this.layout.getOutputDirectory(),
this.reports.licenses(archive)));
}
}
private void classifyLicenses(final Archive archive) throws IOException {
final Set<License> undeclared =
new HashSet<License>(archive.getLicenses());
final File contents = archive.contentsDirectory();
final List<File> files = this.fileSystem.licensesDeclaredIn(contents);
for (final File file : files) {
undeclared.remove(this.licenses.from(file));
}
archive.getOtherLicenses().addAll(undeclared);
final Set<License> declared =
new HashSet<License>(archive.getLicenses());
declared.removeAll(undeclared);
archive.getDeclaredLicenses().addAll(declared);
for (final License license : undeclared) {
for (final License declare : declared) {
if (license.implies(declare)) {
archive.getOtherLicenses().remove(license);
}
}
}
}
private void reportDeclaredNotices(final List<Archive> archives)
throws IOException {
for (final Archive archive : archives) {
final Set<Notice> undeclared =
new HashSet<Notice>(archive.getNotices());
final File contents = archive.contentsDirectory();
final List<File> files =
this.fileSystem.noticesDeclaredIn(contents);
for (final File file : files) {
final Notice notice = new Notice(this.ioSystem.slurp(file));
undeclared.remove(notice);
}
archive.getOtherNotices().addAll(undeclared);
final Set<Notice> declared =
new HashSet<Notice>(archive.getNotices());
declared.removeAll(undeclared);
archive.getDeclaredNotices().addAll(declared);
for (final Notice notice : undeclared) {
for (final Notice declare : declared) {
if (notice.implies(declare)) {
archive.getOtherLicenses().remove(notice);
}
}
}
this.templates
.template("legal/archive-notices.vm")
.add("archive", archive)
.add("reports", this.reports)
.write(new File(this.layout.getOutputDirectory(),
this.reports.notices(archive)));
}
}
private void reportNotices(final List<Archive> archives) throws IOException {
final Map<Notice, Notice> notices = new HashMap<Notice, Notice>();
for (final Archive archive : archives) {
final List<File> noticeDocuments =
this.fileSystem.noticesOnly(archive.contentsDirectory());
for (final File file : noticeDocuments) {
final Notice notice = new Notice(this.ioSystem.slurp(file));
Notice existing = notices.get(notice);
if (existing == null) {
notices.put(notice, notice);
existing = notice;
}
existing.getLocations().add(file);
existing.getArchives().add(archive);
archive.getNotices().add(existing);
}
}
this.templates
.template("legal/notices.vm")
.add("notices", notices.values())
.add("reports", this.reports)
.write(new File(this.layout.getOutputDirectory(),
"notices.html"));
}
private void unpackContents(final Set<File> files) throws IOException {
for (final File file : files) {
unpack(file);
}
}
private Set<File> mirrorRepositoryFrom(final Configuration configuration)
throws IOException {
final Set<File> files = new HashSet<File>();
if (HTTP.isRepositoryFor(configuration)) {
final NexusClient client = new NexusClient(this.platform);
final Set<URI> resources =
client.crawl(configuration.getStagingRepositoryURI());
for (final URI uri : resources) {
if (!uri.getPath().matches(CRAWL_PATTERN)) {
continue;
}
files.add(client.download(uri, mirroredFrom(uri)));
}
} else if (LOCAL_FILE_SYSTEM.isRepositoryFor(configuration)) {
final File file = new File(configuration.getStagingRepositoryURI());
final List<File> collect =
this.platform.getFileSystem().archivesInPath(file,
configuration.getFileRepositoryPathNameFilter());
for (final File f : collect) {
files.add(copyToMirror(f));
}
}
return files;
}
private void unpack(final File archive) throws IOException {
log.info("Unpack " + archive);
try {
final ZipInputStream zip = this.ioSystem.unzip(archive);
final File contents =
new Archive(archive, this.fileSystem, this.layout)
.contentsDirectory();
try {
ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
if (entry.isDirectory()) {
continue;
}
final String path = entry.getName();
final File fileEntry = new File(contents, path);
this.fileSystem.mkparent(fileEntry);
// Open the output file
this.ioSystem.copy(zip, fileEntry);
if (fileEntry.getName().endsWith(".jar")) {
unpack(fileEntry);
}
}
} finally {
this.ioSystem.close(zip);
}
} catch (final IOException e) {
log.error("Not a zip " + archive);
}
}
private File copyToMirror(final File src) throws IOException {
final URI uri = src.toURI();
final File file = mirroredFrom(uri);
log.info("Copy " + uri);
this.fileSystem.mkparent(file);
this.ioSystem.copy(this.ioSystem.read(src), file);
return file;
}
private File mirroredFrom(final URI uri) {
final String name =
uri.toString()
.replace(
this.configuration.getStagingRepositoryURI()
.toString(), "").replaceFirst("^/", "");
return new File(this.layout.getRepositoryDirectory(), name);
}
}