package com.github.atdi.gboot.loader; import com.github.atdi.gboot.loader.archive.Archive; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * Base class for executable archive {@link Launcher}s. * */ public abstract class ExecutableArchiveLauncher extends Launcher { private final Archive archive; private final JavaAgentDetector javaAgentDetector; private static final Logger logger = Logger.getLogger(ExecutableArchiveLauncher.class.getName()); public ExecutableArchiveLauncher() { this(new InputArgumentsJavaAgentDetector()); } public ExecutableArchiveLauncher(JavaAgentDetector javaAgentDetector) { try { this.archive = createArchive(); } catch (Exception ex) { throw new IllegalStateException(ex); } this.javaAgentDetector = javaAgentDetector; } ExecutableArchiveLauncher(Archive archive) { this.javaAgentDetector = new InputArgumentsJavaAgentDetector(); this.archive = archive; } protected final Archive getArchive() { return this.archive; } @Override protected String getMainClass() throws Exception { return this.archive.getMainClass(); } @Override protected List<Archive> getClassPathArchives() throws Exception { List<Archive> archives = new ArrayList<Archive>( this.archive.getNestedArchives(new Archive.EntryFilter() { @Override public boolean matches(Archive.Entry entry) { return isNestedArchive(entry); } })); postProcessClassPathArchives(archives); return archives; } @Override protected ClassLoader createClassLoader(URL[] urls) throws Exception { Set<URL> copy = new LinkedHashSet<URL>(urls.length); ClassLoader loader = getDefaultClassLoader(); if (loader instanceof URLClassLoader) { for (URL url : ((URLClassLoader) loader).getURLs()) { if (addDefaultClassloaderUrl(urls, url)) { copy.add(url); } } } Collections.addAll(copy, urls); return super.createClassLoader(copy.toArray(new URL[copy.size()])); } private boolean addDefaultClassloaderUrl(URL[] urls, URL url) throws URISyntaxException { String jarUrl = "jar:" + url + "!/"; for (URL nestedUrl : urls) { if (nestedUrl.toURI().equals(url.toURI()) || nestedUrl.toString().equals(jarUrl)) { return false; } } return !this.javaAgentDetector.isJavaAgentJar(url.toURI()); } /** * Determine if the specified {@link java.util.jar.JarEntry} is a nested item that should be added * to the classpath. The method is called once for each entry. * @param entry the jar entry * @return {@code true} if the entry is a nested item (jar or folder) */ protected abstract boolean isNestedArchive(Archive.Entry entry); /** * Called to post-process archive entries before they are used. Implementations can * add and remove entries. * @param archives the archives * @throws Exception */ protected void postProcessClassPathArchives(List<Archive> archives) throws Exception { } private static ClassLoader getDefaultClassLoader() { ClassLoader classloader = null; try { classloader = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex) { logger.log(Level.WARNING, "Cannot access thread context ClassLoader - " + "falling back to system class loader...", ex); } if (classloader == null) { classloader = ExecutableArchiveLauncher.class.getClassLoader(); } return classloader; } }