com.hurence.logisland.plugin.PluginManager Maven / Gradle / Ivy
/**
* Copyright (C) 2016 Hurence ([email protected])
*
* 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 com.hurence.logisland.plugin;
import com.hurence.logisland.BannerLoader;
import com.hurence.logisland.classloading.ManifestAttributes;
import com.hurence.logisland.classloading.ModuleInfo;
import com.hurence.logisland.classloading.PluginClassLoader;
import com.hurence.logisland.classloading.PluginLoader;
import com.hurence.logisland.packaging.LogislandPluginLayoutFactory;
import com.hurence.logisland.packaging.LogislandRepackager;
import com.hurence.logisland.util.Tuple;
import org.apache.commons.cli.*;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.DownloadOptions;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.settings.IvySettings;
import org.springframework.boot.loader.tools.Library;
import org.springframework.boot.loader.tools.LibraryScope;
import org.springframework.boot.loader.tools.Repackager;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
/**
* Logisland plugin manager
*
* @author amarziali
*/
public class PluginManager {
private static Set downloadArtifacts(Ivy ivy, ModuleRevisionId moduleRevisionId, String[] confs) throws Exception {
ResolveOptions resolveOptions = new ResolveOptions();
resolveOptions.setDownload(true);
resolveOptions.setTransitive(true);
resolveOptions.setOutputReport(false);
resolveOptions.setConfs(confs);
resolveOptions.setLog(null);
resolveOptions.setValidate(false);
resolveOptions.setCheckIfChanged(true);
ResolveReport report = (ivy.resolve(moduleRevisionId, resolveOptions, true));
Set reports = new LinkedHashSet<>();
reports.addAll(Arrays.asList(report.getAllArtifactsReports()));
resolveOptions.setTransitive(false);
for (ArtifactDownloadReport artifactDownloadReport : report.getFailedArtifactsReports()) {
reports.add(ivy.getResolveEngine().download(
new DefaultArtifact(artifactDownloadReport.getArtifact().getModuleRevisionId(),
artifactDownloadReport.getArtifact().getPublicationDate(),
artifactDownloadReport.getArtifact().getName(),
"jar",
"jar"), new DownloadOptions()));
}
return reports;
}
private static final List excludedArtifactsId = Arrays.asList("logisland-utils", "logisland-api", "commons-logging",
"logisland-scripting-mvel", "logisland-scripting-base", "logisland-hadoop-utils");
private static final List excludeGroupIds = Arrays.asList("org.slf4j", "ch.qos.logback", "log4j", "org.apache.logging.log4j");
public static void main(String... args) throws Exception {
System.out.println(BannerLoader.loadBanner());
String logislandHome = new File(new File(PluginManager.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getParent()).getParent();
System.out.println("Using Logisland home: " + logislandHome);
Options options = new Options();
OptionGroup mainGroup = new OptionGroup()
.addOption(OptionBuilder
.withDescription("Install a component. It can be either a logisland plugin or a kafka connect module.")
.withArgName("artifact")
.hasArgs(1)
.withLongOpt("install")
.create("i"))
.addOption(OptionBuilder
.withDescription("Removes a component. It can be either a logisland plugin or a kafka connect module.")
.withArgName("artifact")
.hasArgs(1)
.withLongOpt("remove")
.create("r"))
.addOption(OptionBuilder
.withDescription("List installed components.")
.withLongOpt("list")
.create("l"));
mainGroup.setRequired(true);
options.addOptionGroup(mainGroup);
options.addOption(OptionBuilder
.withDescription("Print this help.")
.withLongOpt("help")
.create("h"));
try {
CommandLine commandLine = new PosixParser().parse(options, args);
System.out.println(commandLine.getArgList());
if (commandLine.hasOption("l")) {
listPlugins();
} else if (commandLine.hasOption("i")) {
installPlugin(commandLine.getOptionValue("i"), logislandHome);
} else if (commandLine.hasOption("r")) {
removePlugin(commandLine.getOptionValue("r"));
} else {
printUsage(options);
}
} catch (ParseException e) {
if (!options.hasOption("h")) {
System.err.println(e.getMessage());
System.out.println();
}
printUsage(options);
}
}
private static void printUsage(Options options) {
System.out.println();
new HelpFormatter().printHelp(180,
"components.sh", "\n", options, "\n",
true);
}
private static Map> findPluginMeta() {
return PluginLoader.getRegistry().entrySet().stream()
.map(e -> new Tuple<>(((PluginClassLoader) e.getValue()).getModuleInfo(), e.getKey()))
.collect(Collectors.groupingBy(t -> t.getKey().getArtifact()))
.entrySet().stream().collect(Collectors.toMap(
e -> e.getValue().stream().findFirst().get().getKey(),
e -> e.getValue().stream().map(Tuple::getValue).sorted().collect(Collectors.toList())));
}
private static void listPlugins() {
Map> moduleMapping = findPluginMeta();
System.out.println();
System.out.println("Listing details for " + moduleMapping.size() + " installed modules.");
System.out.println("==============================================================");
System.out.println();
moduleMapping.forEach((c, l) -> {
System.out.println("Artifact: " + c.getArtifact());
System.out.println("Name: " + c.getName());
System.out.println("Version: " + c.getVersion());
System.out.println("Location: " + c.getSourceArchive());
System.out.println("Components provided:");
l.forEach(ll -> System.out.println("\t" + ll));
System.out.println("\n-----------------------------------------------------------\n");
});
}
private static void removePlugin(String artifact) {
Optional moduleInfo = findPluginMeta().entrySet().stream().filter(e -> artifact.equals(e.getKey().getArtifact()))
.map(Map.Entry::getKey).findFirst();
if (moduleInfo.isPresent()) {
String filename = moduleInfo.get().getSourceArchive();
System.out.println("Removing component jar: " + filename);
if (!new File(filename).delete()) {
System.err.println("Unable to delete file " + artifact);
System.exit(-1);
}
} else {
System.err.println("Found no installed component matching artifact " + artifact);
System.exit(-1);
}
}
private static void installPlugin(String artifact, String logislandHome) {
Optional moduleInfo = findPluginMeta().entrySet().stream().filter(e -> artifact.equals(e.getKey().getArtifact()))
.map(Map.Entry::getKey).findFirst();
if (moduleInfo.isPresent()) {
System.err.println("A component already matches the artifact " + artifact +
". Please remove it first.");
System.exit(-1);
}
try {
IvySettings settings = new IvySettings();
settings.load(new File(logislandHome, "conf/ivy.xml"));
Ivy ivy = Ivy.newInstance(settings);
ivy.bind();
System.out.println("\nDownloading dependencies. Please hold on...\n");
String parts[] = Arrays.stream(artifact.split(":")).map(String::trim).toArray(a -> new String[a]);
if (parts.length != 3) {
throw new IllegalArgumentException("Unrecognized artifact format. It should be groupId:artifactId:version");
}
ModuleRevisionId revisionId = new ModuleRevisionId(new ModuleId(parts[0], parts[1]), parts[2]);
Set toBePackaged = downloadArtifacts(ivy, revisionId, new String[]{"default", "compile", "runtime"});
ArtifactDownloadReport artifactJar = toBePackaged.stream().filter(a -> a.getArtifact().getModuleRevisionId().equals(revisionId)).findFirst()
.orElseThrow(() -> new IllegalStateException("Unable to find artifact " + artifact +
". Please check the name is correct and the repositories on ivy.xml are correctly configured"));
Manifest manifest = new JarFile(artifactJar.getLocalFile()).getManifest();
File libDir = new File(logislandHome, "lib");
if (manifest.getMainAttributes().containsKey(ManifestAttributes.MODULE_ARTIFACT)) {
org.apache.commons.io.FileUtils.copyFileToDirectory(artifactJar.getLocalFile(), libDir);
//we have a logisland plugin. Just copy it
System.out.println(String.format("Found logisland plugin %s version %s\n" +
"It will provide:",
manifest.getMainAttributes().getValue(ManifestAttributes.MODULE_NAME),
manifest.getMainAttributes().getValue(ManifestAttributes.MODULE_VERSION)));
Arrays.stream(manifest.getMainAttributes().getValue(ManifestAttributes.MODULE_EXPORTS).split(","))
.map(String::trim).forEach(s -> System.out.println("\t" + s));
} else {
System.out.println("Repackaging artifact and its dependencies");
Set environment = downloadArtifacts(ivy, revisionId, new String[]{"provided"});
Set excluded = toBePackaged.stream()
.filter(adr -> excludeGroupIds.stream().anyMatch(s -> s.matches(adr.getArtifact().getModuleRevisionId().getOrganisation())) ||
excludedArtifactsId.stream().anyMatch(s -> s.matches(adr.getArtifact().getModuleRevisionId().getName())))
.collect(Collectors.toSet());
toBePackaged.removeAll(excluded);
environment.addAll(excluded);
Repackager rep = new Repackager(artifactJar.getLocalFile(), new LogislandPluginLayoutFactory());
rep.setMainClass("");
File destFile = new File(libDir, "logisland-component-" + artifactJar.getLocalFile().getName());
rep.repackage(destFile, callback ->
toBePackaged.stream()
.filter(adr -> adr.getLocalFile() != null)
.filter(adr -> !adr.getArtifact().getModuleRevisionId().equals(revisionId))
.map(adr -> new Library(adr.getLocalFile(), LibraryScope.COMPILE))
.forEach(library -> {
try {
callback.library(library);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
);
Thread.currentThread().setContextClassLoader(new URLClassLoader(environment.stream()
.filter(adr -> adr.getLocalFile() != null)
.map(adr -> {
try {
return adr.getLocalFile().toURI().toURL();
} catch (Exception e) {
throw new RuntimeException(e);
}
}).toArray(a -> new URL[a]),
Thread.currentThread().getContextClassLoader()));
//now clean up package and write the manifest
String newArtifact = "com.hurence.logisland.repackaged:" + parts[1] + ":" + parts[2];
LogislandRepackager.execute(destFile.getAbsolutePath(), "BOOT-INF/lib-provided",
parts[2], newArtifact,
"Logisland Component for " + artifact,
"Logisland Component for " + artifact,
new String[]{"org.apache.kafka.*"},
new String[0],
"org.apache.kafka.connect.connector.Connector");
}
System.out.println("Install done!");
} catch (Exception e) {
System.err.println("Unable to install artifact " + artifact);
e.printStackTrace();
System.exit(-1);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy