Java tutorial
/** * Copyright (C) 2016 Hurence (support@hurence.com) * * 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<ArtifactDownloadReport> 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<ArtifactDownloadReport> 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<String> excludedArtifactsId = Arrays.asList("logisland-utils", "logisland-api", "commons-logging", "logisland-scripting-mvel", "logisland-scripting-base", "logisland-hadoop-utils"); private static final List<String> 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<ModuleInfo, List<String>> 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<ModuleInfo, List<String>> 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> 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> 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<ArtifactDownloadReport> 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<ArtifactDownloadReport> environment = downloadArtifacts(ivy, revisionId, new String[] { "provided" }); Set<ArtifactDownloadReport> 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); } } }