Jar in Jar处理逻辑优化
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.JarLauncher;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.SpringBootJarClassLoader;
|
||||
import fun.asgc.neutrino.core.base.GlobalConfig;
|
||||
import fun.asgc.neutrino.core.util.CollectionUtil;
|
||||
import fun.asgc.neutrino.core.util.FileUtil;
|
||||
@@ -55,6 +55,7 @@ public class AsgcCompiler {
|
||||
private boolean isSaveClassFile;
|
||||
private String generatorCodeSavePath;
|
||||
private DynamicClassLoader dynamicClassLoader;
|
||||
private SpringBootJarClassLoader springBootJarClassLoader;
|
||||
|
||||
private final List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<Diagnostic<? extends JavaFileObject>>();
|
||||
private final List<Diagnostic<? extends JavaFileObject>> warnings = new ArrayList<Diagnostic<? extends JavaFileObject>>();
|
||||
@@ -74,23 +75,22 @@ public class AsgcCompiler {
|
||||
if (null == javaCompiler) {
|
||||
throw new RuntimeException("Can not load JavaCompiler from javax.tools.ToolProvider#getSystemJavaCompiler(),\n please confirm the application running in JDK not JRE.");
|
||||
}
|
||||
ClassLoader parent = classLoader;
|
||||
if (SystemUtil.isStartupFromJar() && GlobalConfig.isIsUseSpringBootJar()) {
|
||||
try {
|
||||
log.debug("使用LaunchedURLClassLoader jar路径:{}", SystemUtil.getCurrentJarFilePath());
|
||||
JarLauncher jarLauncher = new JarLauncher(parent, SystemUtil.getCurrentJarFilePath());
|
||||
parent = jarLauncher.createClassLoader();
|
||||
log.debug("使用LaunchedURLClassLoader instance:{}", parent);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
log.error("使用LaunchedURLClassLoader异常", e);
|
||||
}
|
||||
}
|
||||
this.springBootJarClassLoader = new SpringBootJarClassLoader(classLoader);
|
||||
// if (SystemUtil.isStartupFromJar() && GlobalConfig.isIsUseSpringBootJar()) {
|
||||
// try {
|
||||
// log.debug("使用LaunchedURLClassLoader jar路径:{}", SystemUtil.getCurrentJarFilePath());
|
||||
// parent = new SpringBootJarClassLoader(classLoader);
|
||||
// log.debug("使用LaunchedURLClassLoader instance:{}", parent);
|
||||
// } catch (Exception e) {
|
||||
// // ignore
|
||||
// log.error("使用LaunchedURLClassLoader异常", e);
|
||||
// }
|
||||
// }
|
||||
this.collector = new DiagnosticCollector<>();
|
||||
this.standardJavaFileManager = javaCompiler.getStandardFileManager(collector, null, null);
|
||||
this.isSaveClassFile = false;
|
||||
this.generatorCodeSavePath = GlobalConfig.getGeneratorCodeSavePath();
|
||||
this.dynamicClassLoader = new DynamicClassLoader(parent, this);
|
||||
this.dynamicClassLoader = new DynamicClassLoader(springBootJarClassLoader, this);
|
||||
|
||||
addOption("-Xlint:unchecked");
|
||||
addOption("-implicit:class");
|
||||
@@ -182,6 +182,14 @@ public class AsgcCompiler {
|
||||
compilationUnits.add(javaFileObject);
|
||||
}
|
||||
|
||||
public void addDependSpringBootJar(String path) {
|
||||
try {
|
||||
this.springBootJarClassLoader.addJar(path);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编译代码
|
||||
* @param pkg 包名
|
||||
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A class path index file that provides ordering information for JARs.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class ClassPathIndexFile {
|
||||
|
||||
private final File root;
|
||||
|
||||
private final List<String> lines;
|
||||
|
||||
private ClassPathIndexFile(File root, List<String> lines) {
|
||||
this.root = root;
|
||||
this.lines = lines.stream().map(this::extractName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String extractName(String line) {
|
||||
if (line.startsWith("- \"") && line.endsWith("\"")) {
|
||||
return line.substring(3, line.length() - 1);
|
||||
}
|
||||
throw new IllegalStateException("Malformed classpath index line [" + line + "]");
|
||||
}
|
||||
|
||||
int size() {
|
||||
return this.lines.size();
|
||||
}
|
||||
|
||||
boolean containsEntry(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.lines.contains(name);
|
||||
}
|
||||
|
||||
List<URL> getUrls() {
|
||||
return this.lines.stream().map(this::asUrl).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private URL asUrl(String line) {
|
||||
try {
|
||||
return new File(this.root, line).toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static ClassPathIndexFile loadIfPossible(URL root, String location) throws IOException {
|
||||
return loadIfPossible(asFile(root), location);
|
||||
}
|
||||
|
||||
private static ClassPathIndexFile loadIfPossible(File root, String location) throws IOException {
|
||||
return loadIfPossible(root, new File(root, location));
|
||||
}
|
||||
|
||||
private static ClassPathIndexFile loadIfPossible(File root, File indexFile) throws IOException {
|
||||
if (indexFile.exists() && indexFile.isFile()) {
|
||||
try (InputStream inputStream = new FileInputStream(indexFile)) {
|
||||
return new ClassPathIndexFile(root, loadLines(inputStream));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<String> loadLines(InputStream inputStream) throws IOException {
|
||||
List<String> lines = new ArrayList<>();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
if (!line.trim().isEmpty()) {
|
||||
lines.add(line);
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
return Collections.unmodifiableList(lines);
|
||||
}
|
||||
|
||||
private static File asFile(URL url) {
|
||||
if (!"file".equals(url.getProtocol())) {
|
||||
throw new IllegalArgumentException("URL does not reference a file");
|
||||
}
|
||||
try {
|
||||
return new File(url.toURI());
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
return new File(url.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-199
@@ -1,199 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.Archive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.ExplodedArchive;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* Base class for executable archive {@link Launcher}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class ExecutableArchiveLauncher extends Launcher {
|
||||
|
||||
private static final String START_CLASS_ATTRIBUTE = "Start-Class";
|
||||
|
||||
protected static final String BOOT_CLASSPATH_INDEX_ATTRIBUTE = "Spring-Boot-Classpath-Index";
|
||||
|
||||
protected static final String DEFAULT_CLASSPATH_INDEX_FILE_NAME = "classpath.idx";
|
||||
|
||||
private Archive archive;
|
||||
|
||||
private ClassPathIndexFile classPathIndex;
|
||||
|
||||
public ExecutableArchiveLauncher() {
|
||||
// try {
|
||||
// this.archive = createArchive();
|
||||
// this.classPathIndex = getClassPathIndex(this.archive);
|
||||
// }
|
||||
// catch (Exception ex) {
|
||||
// throw new IllegalStateException(ex);
|
||||
// }
|
||||
}
|
||||
|
||||
protected ExecutableArchiveLauncher(Archive archive) {
|
||||
try {
|
||||
this.archive = archive;
|
||||
this.classPathIndex = getClassPathIndex(this.archive);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected ClassPathIndexFile getClassPathIndex(Archive archive) throws IOException {
|
||||
// Only needed for exploded archives, regular ones already have a defined order
|
||||
if (archive instanceof ExplodedArchive) {
|
||||
String location = getClassPathIndexFileLocation(archive);
|
||||
return ClassPathIndexFile.loadIfPossible(archive.getUrl(), location);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getClassPathIndexFileLocation(Archive archive) throws IOException {
|
||||
Manifest manifest = archive.getManifest();
|
||||
Attributes attributes = (manifest != null) ? manifest.getMainAttributes() : null;
|
||||
String location = (attributes != null) ? attributes.getValue(BOOT_CLASSPATH_INDEX_ATTRIBUTE) : null;
|
||||
return (location != null) ? location : getArchiveEntryPathPrefix() + DEFAULT_CLASSPATH_INDEX_FILE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainClass() throws Exception {
|
||||
Manifest manifest = this.archive.getManifest();
|
||||
String mainClass = null;
|
||||
if (manifest != null) {
|
||||
mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE);
|
||||
}
|
||||
if (mainClass == null) {
|
||||
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
|
||||
}
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader createClassLoader(Iterator<Archive> archives) throws Exception {
|
||||
List<URL> urls = new ArrayList<>(guessClassPathSize());
|
||||
while (archives.hasNext()) {
|
||||
urls.add(archives.next().getUrl());
|
||||
}
|
||||
if (this.classPathIndex != null) {
|
||||
urls.addAll(this.classPathIndex.getUrls());
|
||||
}
|
||||
return createClassLoader(urls.toArray(new URL[0]));
|
||||
}
|
||||
|
||||
private int guessClassPathSize() {
|
||||
if (this.classPathIndex != null) {
|
||||
return this.classPathIndex.size() + 10;
|
||||
}
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
|
||||
Archive.EntryFilter searchFilter = this::isSearchCandidate;
|
||||
Iterator<Archive> archives = this.archive.getNestedArchives(searchFilter,
|
||||
(entry) -> isNestedArchive(entry) && !isEntryIndexed(entry));
|
||||
if (isPostProcessingClassPathArchives()) {
|
||||
archives = applyClassPathArchivePostProcessing(archives);
|
||||
}
|
||||
return archives;
|
||||
}
|
||||
|
||||
private boolean isEntryIndexed(Archive.Entry entry) {
|
||||
if (this.classPathIndex != null) {
|
||||
return this.classPathIndex.containsEntry(entry.getName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Iterator<Archive> applyClassPathArchivePostProcessing(Iterator<Archive> archives) throws Exception {
|
||||
List<Archive> list = new ArrayList<>();
|
||||
while (archives.hasNext()) {
|
||||
list.add(archives.next());
|
||||
}
|
||||
postProcessClassPathArchives(list);
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified entry is a candidate for further searching.
|
||||
* @param entry the entry to check
|
||||
* @return {@code true} if the entry is a candidate for further searching
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected boolean isSearchCandidate(Archive.Entry entry) {
|
||||
if (getArchiveEntryPathPrefix() == null) {
|
||||
return true;
|
||||
}
|
||||
return entry.getName().startsWith(getArchiveEntryPathPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified entry is a nested item that should be added to the
|
||||
* classpath.
|
||||
* @param entry the entry to check
|
||||
* @return {@code true} if the entry is a nested item (jar or directory)
|
||||
*/
|
||||
protected abstract boolean isNestedArchive(Archive.Entry entry);
|
||||
|
||||
/**
|
||||
* Return if post-processing needs to be applied to the archives. For back
|
||||
* compatibility this method returns {@code true}, but subclasses that don't override
|
||||
* {@link #postProcessClassPathArchives(List)} should provide an implementation that
|
||||
* returns {@code false}.
|
||||
* @return if the {@link #postProcessClassPathArchives(List)} method is implemented
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected boolean isPostProcessingClassPathArchives() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to post-process archive entries before they are used. Implementations can
|
||||
* add and remove entries.
|
||||
* @param archives the archives
|
||||
* @throws Exception if the post-processing fails
|
||||
* @see #isPostProcessingClassPathArchives()
|
||||
*/
|
||||
protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path prefix for entries in the archive.
|
||||
* @return the path prefix
|
||||
*/
|
||||
protected String getArchiveEntryPathPrefix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExploded() {
|
||||
return this.archive.isExploded();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Archive getArchive() {
|
||||
return this.archive;
|
||||
}
|
||||
|
||||
|
||||
protected void setArchive(Archive archive) {
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
protected void setClassPathIndex(ClassPathIndexFile classPathIndex) {
|
||||
this.classPathIndex = classPathIndex;
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.Archive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.ExplodedArchive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.JarFileArchive;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* {@link Launcher} for JAR based archives. This launcher assumes that dependency jars are
|
||||
* included inside a {@code /BOOT-INF/lib} directory and that application classes are
|
||||
* included inside a {@code /BOOT-INF/classes} directory.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class JarLauncher extends ExecutableArchiveLauncher {
|
||||
|
||||
private String path;
|
||||
|
||||
static final Archive.EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
|
||||
if (entry.isDirectory()) {
|
||||
return entry.getName().equals("BOOT-INF/classes/");
|
||||
}
|
||||
return entry.getName().startsWith("BOOT-INF/lib/");
|
||||
};
|
||||
|
||||
public JarLauncher(String path) {
|
||||
this.path = path;
|
||||
try {
|
||||
this.setArchive(createArchive(this.path));
|
||||
this.setClassPathIndex(getClassPathIndex(this.getArchive()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public JarLauncher(ClassLoader classLoader, String path) {
|
||||
this.path = path;
|
||||
try {
|
||||
this.setArchive(createArchive(this.path));
|
||||
this.setClassPathIndex(getClassPathIndex(this.getArchive()));
|
||||
this.setClassLoader(classLoader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader createClassLoader() throws Exception {
|
||||
return createClassLoader(getClassPathArchivesIterator());
|
||||
}
|
||||
|
||||
protected final Archive createArchive(String path) throws Exception {
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Unable to determine code source archive");
|
||||
}
|
||||
File root = new File(path);
|
||||
if (!root.exists()) {
|
||||
throw new IllegalStateException("Unable to determine code source archive from " + root);
|
||||
}
|
||||
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
|
||||
}
|
||||
|
||||
protected JarLauncher(Archive archive) {
|
||||
super(archive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPostProcessingClassPathArchives() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getArchiveEntryPathPrefix() {
|
||||
return "BOOT-INF/";
|
||||
}
|
||||
|
||||
}
|
||||
-354
@@ -1,354 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.Archive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.jar.Handler;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* {@link ClassLoader} used by the {@link Launcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LaunchedURLClassLoader extends URLClassLoader {
|
||||
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
private final boolean exploded;
|
||||
|
||||
private final Archive rootArchive;
|
||||
|
||||
private final Object packageLock = new Object();
|
||||
|
||||
private volatile DefinePackageCallType definePackageCallType;
|
||||
|
||||
/**
|
||||
* Create a new {@link LaunchedURLClassLoader} instance.
|
||||
* @param urls the URLs from which to load classes and resources
|
||||
* @param parent the parent class loader for delegation
|
||||
*/
|
||||
public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {
|
||||
this(false, urls, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link LaunchedURLClassLoader} instance.
|
||||
* @param exploded if the underlying archive is exploded
|
||||
* @param urls the URLs from which to load classes and resources
|
||||
* @param parent the parent class loader for delegation
|
||||
*/
|
||||
public LaunchedURLClassLoader(boolean exploded, URL[] urls, ClassLoader parent) {
|
||||
this(exploded, null, urls, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link LaunchedURLClassLoader} instance.
|
||||
* @param exploded if the underlying archive is exploded
|
||||
* @param rootArchive the root archive or {@code null}
|
||||
* @param urls the URLs from which to load classes and resources
|
||||
* @param parent the parent class loader for delegation
|
||||
* @since 2.3.1
|
||||
*/
|
||||
public LaunchedURLClassLoader(boolean exploded, Archive rootArchive, URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
this.exploded = exploded;
|
||||
this.rootArchive = rootArchive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findResource(String name) {
|
||||
if (this.exploded) {
|
||||
return super.findResource(name);
|
||||
}
|
||||
Handler.setUseFastConnectionExceptions(true);
|
||||
try {
|
||||
return super.findResource(name);
|
||||
}
|
||||
finally {
|
||||
Handler.setUseFastConnectionExceptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
if (this.exploded) {
|
||||
return super.findResources(name);
|
||||
}
|
||||
Handler.setUseFastConnectionExceptions(true);
|
||||
try {
|
||||
return new UseFastConnectionExceptionsEnumeration(super.findResources(name));
|
||||
}
|
||||
finally {
|
||||
Handler.setUseFastConnectionExceptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (name.startsWith("org.springframework.boot.loader.jarmode.")) {
|
||||
try {
|
||||
Class<?> result = loadClassInLaunchedClassLoader(name);
|
||||
if (resolve) {
|
||||
resolveClass(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
if (this.exploded) {
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
Handler.setUseFastConnectionExceptions(true);
|
||||
try {
|
||||
try {
|
||||
definePackageIfNecessary(name);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// Tolerate race condition due to being parallel capable
|
||||
// if (getDefinedPackage(name) == null) {
|
||||
// // This should never happen as the IllegalArgumentException indicates
|
||||
// // that the package has already been defined and, therefore,
|
||||
// // getDefinedPackage(name) should not return null.
|
||||
// throw new AssertionError("Package " + name + " has already been defined but it could not be found");
|
||||
// }
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
finally {
|
||||
Handler.setUseFastConnectionExceptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void addURL(URL url) {
|
||||
addURL(url);
|
||||
}
|
||||
|
||||
private Class<?> loadClassInLaunchedClassLoader(String name) throws ClassNotFoundException {
|
||||
String internalName = name.replace('.', '/') + ".class";
|
||||
InputStream inputStream = getParent().getResourceAsStream(internalName);
|
||||
if (inputStream == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
inputStream.close();
|
||||
byte[] bytes = outputStream.toByteArray();
|
||||
Class<?> definedClass = defineClass(name, bytes, 0, bytes.length);
|
||||
definePackageIfNecessary(name);
|
||||
return definedClass;
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a package before a {@code findClass} call is made. This is necessary to
|
||||
* ensure that the appropriate manifest for nested JARs is associated with the
|
||||
* package.
|
||||
* @param className the class name being found
|
||||
*/
|
||||
private void definePackageIfNecessary(String className) {
|
||||
int lastDot = className.lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
String packageName = className.substring(0, lastDot);
|
||||
// if (getDefinedPackage(packageName) == null) {
|
||||
try {
|
||||
definePackage(className, packageName);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// Tolerate race condition due to being parallel capable
|
||||
// if (getDefinedPackage(packageName) == null) {
|
||||
// // This should never happen as the IllegalArgumentException
|
||||
// // indicates that the package has already been defined and,
|
||||
// // therefore, getDefinedPackage(name) should not have returned
|
||||
// // null.
|
||||
// throw new AssertionError(
|
||||
// "Package " + packageName + " has already been defined but it could not be found");
|
||||
// }
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private void definePackage(String className, String packageName) {
|
||||
String packageEntryName = packageName.replace('.', '/') + "/";
|
||||
String classEntryName = className.replace('.', '/') + ".class";
|
||||
for (URL url : getURLs()) {
|
||||
try {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
JarFile jarFile = ((JarURLConnection)connection).getJarFile();
|
||||
if (jarFile.getEntry(classEntryName) != null && jarFile.getEntry(packageEntryName) != null
|
||||
&& jarFile.getManifest() != null) {
|
||||
definePackage(packageName, jarFile.getManifest(), url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException {
|
||||
if (!this.exploded) {
|
||||
return super.definePackage(name, man, url);
|
||||
}
|
||||
synchronized (this.packageLock) {
|
||||
return doDefinePackage(DefinePackageCallType.MANIFEST, () -> super.definePackage(name, man, url));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Package definePackage(String name, String specTitle, String specVersion, String specVendor,
|
||||
String implTitle, String implVersion, String implVendor, URL sealBase) throws IllegalArgumentException {
|
||||
if (!this.exploded) {
|
||||
return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor,
|
||||
sealBase);
|
||||
}
|
||||
synchronized (this.packageLock) {
|
||||
if (this.definePackageCallType == null) {
|
||||
// We're not part of a call chain which means that the URLClassLoader
|
||||
// is trying to define a package for our exploded JAR. We use the
|
||||
// manifest version to ensure package attributes are set
|
||||
Manifest manifest = getManifest(this.rootArchive);
|
||||
if (manifest != null) {
|
||||
return definePackage(name, manifest, sealBase);
|
||||
}
|
||||
}
|
||||
return doDefinePackage(DefinePackageCallType.ATTRIBUTES, () -> super.definePackage(name, specTitle,
|
||||
specVersion, specVendor, implTitle, implVersion, implVendor, sealBase));
|
||||
}
|
||||
}
|
||||
|
||||
private Manifest getManifest(Archive archive) {
|
||||
try {
|
||||
return (archive != null) ? archive.getManifest() : null;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T doDefinePackage(DefinePackageCallType type, Supplier<T> call) {
|
||||
DefinePackageCallType existingType = this.definePackageCallType;
|
||||
try {
|
||||
this.definePackageCallType = type;
|
||||
return call.get();
|
||||
}
|
||||
finally {
|
||||
this.definePackageCallType = existingType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear URL caches.
|
||||
*/
|
||||
public void clearCache() {
|
||||
if (this.exploded) {
|
||||
return;
|
||||
}
|
||||
for (URL url : getURLs()) {
|
||||
try {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
clearCache(connection);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void clearCache(URLConnection connection) throws IOException {
|
||||
Object jarFile = ((JarURLConnection) connection).getJarFile();
|
||||
if (jarFile instanceof fun.asgc.neutrino.core.aop.compiler.internal.jar.JarFile) {
|
||||
((fun.asgc.neutrino.core.aop.compiler.internal.jar.JarFile) jarFile).clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UseFastConnectionExceptionsEnumeration implements Enumeration<URL> {
|
||||
|
||||
private final Enumeration<URL> delegate;
|
||||
|
||||
UseFastConnectionExceptionsEnumeration(Enumeration<URL> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
Handler.setUseFastConnectionExceptions(true);
|
||||
try {
|
||||
return this.delegate.hasMoreElements();
|
||||
}
|
||||
finally {
|
||||
Handler.setUseFastConnectionExceptions(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL nextElement() {
|
||||
Handler.setUseFastConnectionExceptions(true);
|
||||
try {
|
||||
return this.delegate.nextElement();
|
||||
}
|
||||
finally {
|
||||
Handler.setUseFastConnectionExceptions(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The different types of call made to define a package. We track these for exploded
|
||||
* jars so that we can detect packages that should have manifest attributes applied.
|
||||
*/
|
||||
private enum DefinePackageCallType {
|
||||
|
||||
/**
|
||||
* A define package call from a resource that has a manifest.
|
||||
*/
|
||||
MANIFEST,
|
||||
|
||||
/**
|
||||
* A define package call with a direct set of attributes.
|
||||
*/
|
||||
ATTRIBUTES
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.Archive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.ExplodedArchive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.JarFileArchive;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for launchers that can start an application with a fully configured
|
||||
* classpath backed by one or more {@link Archive}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class Launcher {
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Create a classloader for the specified archives.
|
||||
* @param archives the archives
|
||||
* @return the classloader
|
||||
* @throws Exception if the classloader cannot be created
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected ClassLoader createClassLoader(Iterator<Archive> archives) throws Exception {
|
||||
List<URL> urls = new ArrayList<>(50);
|
||||
while (archives.hasNext()) {
|
||||
urls.add(archives.next().getUrl());
|
||||
}
|
||||
return createClassLoader(urls.toArray(new URL[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classloader for the specified URLs.
|
||||
* @param urls the URLs
|
||||
* @return the classloader
|
||||
* @throws Exception if the classloader cannot be created
|
||||
*/
|
||||
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
||||
return new LaunchedURLClassLoader(isExploded(),
|
||||
getArchive(),
|
||||
urls,
|
||||
(null == classLoader) ? getClass().getClassLoader() : classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the main class that should be launched.
|
||||
* @return the name of the main class
|
||||
* @throws Exception if the main class cannot be obtained
|
||||
*/
|
||||
protected abstract String getMainClass() throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the archives that will be used to construct the class path.
|
||||
* @return the class path archives
|
||||
* @throws Exception if the class path archives cannot be obtained
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected abstract Iterator<Archive> getClassPathArchivesIterator() throws Exception;
|
||||
|
||||
protected final Archive createArchive() throws Exception {
|
||||
ProtectionDomain protectionDomain = getClass().getProtectionDomain();
|
||||
CodeSource codeSource = protectionDomain.getCodeSource();
|
||||
URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;
|
||||
String path = (location != null) ? location.getSchemeSpecificPart() : null;
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Unable to determine code source archive");
|
||||
}
|
||||
File root = new File(path);
|
||||
if (!root.exists()) {
|
||||
throw new IllegalStateException("Unable to determine code source archive from " + root);
|
||||
}
|
||||
return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the launcher is running in an exploded mode. If this method returns
|
||||
* {@code true} then only regular JARs are supported and the additional URL and
|
||||
* ClassLoader support infrastructure can be optimized.
|
||||
* @return if the jar is exploded.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected boolean isExploded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root archive.
|
||||
* @return the root archive
|
||||
* @since 2.3.1
|
||||
*/
|
||||
protected Archive getArchive() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.Archive;
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.archive.JarFileArchive;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link ClassLoader} used by the {@link Launcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SpringBootJarClassLoader extends URLClassLoader {
|
||||
protected static final String BOOT_CLASSPATH_INDEX_ATTRIBUTE = "Spring-Boot-Classpath-Index";
|
||||
protected static final String DEFAULT_CLASSPATH_INDEX_FILE_NAME = "classpath.idx";
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringBootJarClassLoader} instance.
|
||||
* @param parent the parent class loader for delegation
|
||||
*/
|
||||
public SpringBootJarClassLoader(ClassLoader parent) {
|
||||
super(new URL[0], parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringBootJarClassLoader} instance.
|
||||
*/
|
||||
public SpringBootJarClassLoader() {
|
||||
super(new URL[0], ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public void addJar(String path) throws Exception {
|
||||
Archive archive = createArchive(path);
|
||||
Iterator<Archive> archives = getClassPathArchivesIterator(archive);
|
||||
List<URL> urls = new ArrayList<>(50);
|
||||
while (archives.hasNext()) {
|
||||
urls.add(archives.next().getUrl());
|
||||
}
|
||||
urls.forEach(url -> addURL(url));
|
||||
}
|
||||
|
||||
private Archive createArchive(String path) throws Exception {
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Unable to determine code source archive");
|
||||
}
|
||||
File root = new File(path);
|
||||
if (!root.exists()) {
|
||||
throw new IllegalStateException("Unable to determine code source archive from " + root);
|
||||
}
|
||||
return new JarFileArchive(root);
|
||||
}
|
||||
|
||||
protected Iterator<Archive> getClassPathArchivesIterator(Archive archive) throws Exception {
|
||||
Archive.EntryFilter searchFilter = this::isSearchCandidate;
|
||||
Iterator<Archive> archives = archive.getNestedArchives(searchFilter,
|
||||
(entry) -> isNestedArchive(entry));
|
||||
archives = applyClassPathArchivePostProcessing(archives);
|
||||
return archives;
|
||||
}
|
||||
|
||||
private Iterator<Archive> applyClassPathArchivePostProcessing(Iterator<Archive> archives) throws Exception {
|
||||
List<Archive> list = new ArrayList<>();
|
||||
while (archives.hasNext()) {
|
||||
list.add(archives.next());
|
||||
}
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
static final Archive.EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
|
||||
if (entry.isDirectory()) {
|
||||
return entry.getName().equals("BOOT-INF/classes/");
|
||||
}
|
||||
return entry.getName().startsWith("BOOT-INF/lib/");
|
||||
};
|
||||
|
||||
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified entry is a candidate for further searching.
|
||||
* @param entry the entry to check
|
||||
* @return {@code true} if the entry is a candidate for further searching
|
||||
* @since 2.3.0
|
||||
*/
|
||||
protected boolean isSearchCandidate(Archive.Entry entry) {
|
||||
if (getArchiveEntryPathPrefix() == null) {
|
||||
return true;
|
||||
}
|
||||
return entry.getName().startsWith(getArchiveEntryPathPrefix());
|
||||
}
|
||||
|
||||
protected String getArchiveEntryPathPrefix() {
|
||||
return "BOOT-INF/";
|
||||
}
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper class for resolving placeholders in texts. Usually applied to file paths.
|
||||
* <p>
|
||||
* A text may contain {@code $ ...} placeholders, to be resolved as system properties:
|
||||
* e.g. {@code $ user.dir}. Default values can be supplied using the ":" separator between
|
||||
* key and value.
|
||||
* <p>
|
||||
* Adapted from Spring.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
* @see System#getProperty(String)
|
||||
*/
|
||||
public abstract class SystemPropertyUtils {
|
||||
|
||||
/**
|
||||
* Prefix for system property placeholders: "${".
|
||||
*/
|
||||
public static final String PLACEHOLDER_PREFIX = "${";
|
||||
|
||||
/**
|
||||
* Suffix for system property placeholders: "}".
|
||||
*/
|
||||
public static final String PLACEHOLDER_SUFFIX = "}";
|
||||
|
||||
/**
|
||||
* Value separator for system property placeholders: ":".
|
||||
*/
|
||||
public static final String VALUE_SEPARATOR = ":";
|
||||
|
||||
private static final String SIMPLE_PREFIX = PLACEHOLDER_PREFIX.substring(1);
|
||||
|
||||
/**
|
||||
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
||||
* system property values.
|
||||
* @param text the String to resolve
|
||||
* @return the resolved String
|
||||
* @throws IllegalArgumentException if there is an unresolvable placeholder
|
||||
* @see #PLACEHOLDER_PREFIX
|
||||
* @see #PLACEHOLDER_SUFFIX
|
||||
*/
|
||||
public static String resolvePlaceholders(String text) {
|
||||
if (text == null) {
|
||||
return text;
|
||||
}
|
||||
return parseStringValue(null, text, text, new HashSet<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
||||
* system property values.
|
||||
* @param properties a properties instance to use in addition to System
|
||||
* @param text the String to resolve
|
||||
* @return the resolved String
|
||||
* @throws IllegalArgumentException if there is an unresolvable placeholder
|
||||
* @see #PLACEHOLDER_PREFIX
|
||||
* @see #PLACEHOLDER_SUFFIX
|
||||
*/
|
||||
public static String resolvePlaceholders(Properties properties, String text) {
|
||||
if (text == null) {
|
||||
return text;
|
||||
}
|
||||
return parseStringValue(properties, text, text, new HashSet<>());
|
||||
}
|
||||
|
||||
private static String parseStringValue(Properties properties, String value, String current,
|
||||
Set<String> visitedPlaceholders) {
|
||||
|
||||
StringBuilder buf = new StringBuilder(current);
|
||||
|
||||
int startIndex = current.indexOf(PLACEHOLDER_PREFIX);
|
||||
while (startIndex != -1) {
|
||||
int endIndex = findPlaceholderEndIndex(buf, startIndex);
|
||||
if (endIndex != -1) {
|
||||
String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
|
||||
String originalPlaceholder = placeholder;
|
||||
if (!visitedPlaceholders.add(originalPlaceholder)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
|
||||
}
|
||||
// Recursive invocation, parsing placeholders contained in the
|
||||
// placeholder
|
||||
// key.
|
||||
placeholder = parseStringValue(properties, value, placeholder, visitedPlaceholders);
|
||||
// Now obtain the value for the fully resolved key...
|
||||
String propVal = resolvePlaceholder(properties, value, placeholder);
|
||||
if (propVal == null) {
|
||||
int separatorIndex = placeholder.indexOf(VALUE_SEPARATOR);
|
||||
if (separatorIndex != -1) {
|
||||
String actualPlaceholder = placeholder.substring(0, separatorIndex);
|
||||
String defaultValue = placeholder.substring(separatorIndex + VALUE_SEPARATOR.length());
|
||||
propVal = resolvePlaceholder(properties, value, actualPlaceholder);
|
||||
if (propVal == null) {
|
||||
propVal = defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (propVal != null) {
|
||||
// Recursive invocation, parsing placeholders contained in the
|
||||
// previously resolved placeholder value.
|
||||
propVal = parseStringValue(properties, value, propVal, visitedPlaceholders);
|
||||
buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);
|
||||
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, startIndex + propVal.length());
|
||||
}
|
||||
else {
|
||||
// Proceed with unprocessed value.
|
||||
startIndex = buf.indexOf(PLACEHOLDER_PREFIX, endIndex + PLACEHOLDER_SUFFIX.length());
|
||||
}
|
||||
visitedPlaceholders.remove(originalPlaceholder);
|
||||
}
|
||||
else {
|
||||
startIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String resolvePlaceholder(Properties properties, String text, String placeholderName) {
|
||||
String propVal = getProperty(placeholderName, null, text);
|
||||
if (propVal != null) {
|
||||
return propVal;
|
||||
}
|
||||
return (properties != null) ? properties.getProperty(placeholderName) : null;
|
||||
}
|
||||
|
||||
public static String getProperty(String key) {
|
||||
return getProperty(key, null, "");
|
||||
}
|
||||
|
||||
public static String getProperty(String key, String defaultValue) {
|
||||
return getProperty(key, defaultValue, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the System properties and environment variables for a value with the
|
||||
* provided key. Environment variables in {@code UPPER_CASE} style are allowed where
|
||||
* System properties would normally be {@code lower.case}.
|
||||
* @param key the key to resolve
|
||||
* @param defaultValue the default value
|
||||
* @param text optional extra context for an error message if the key resolution fails
|
||||
* (e.g. if System properties are not accessible)
|
||||
* @return a static property value or null of not found
|
||||
*/
|
||||
public static String getProperty(String key, String defaultValue, String text) {
|
||||
try {
|
||||
String propVal = System.getProperty(key);
|
||||
if (propVal == null) {
|
||||
// Fall back to searching the system environment.
|
||||
propVal = System.getenv(key);
|
||||
}
|
||||
if (propVal == null) {
|
||||
// Try with underscores.
|
||||
String name = key.replace('.', '_');
|
||||
propVal = System.getenv(name);
|
||||
}
|
||||
if (propVal == null) {
|
||||
// Try uppercase with underscores as well.
|
||||
String name = key.toUpperCase(Locale.ENGLISH).replace('.', '_');
|
||||
propVal = System.getenv(name);
|
||||
}
|
||||
if (propVal != null) {
|
||||
return propVal;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
System.err.println("Could not resolve key '" + key + "' in '" + text
|
||||
+ "' as system property or in environment: " + ex);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
||||
int index = startIndex + PLACEHOLDER_PREFIX.length();
|
||||
int withinNestedPlaceholder = 0;
|
||||
while (index < buf.length()) {
|
||||
if (substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
|
||||
if (withinNestedPlaceholder > 0) {
|
||||
withinNestedPlaceholder--;
|
||||
index = index + PLACEHOLDER_SUFFIX.length();
|
||||
}
|
||||
else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
else if (substringMatch(buf, index, SIMPLE_PREFIX)) {
|
||||
withinNestedPlaceholder++;
|
||||
index = index + SIMPLE_PREFIX.length();
|
||||
}
|
||||
else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
|
||||
for (int j = 0; j < substring.length(); j++) {
|
||||
int i = index + j;
|
||||
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-2
@@ -1,7 +1,5 @@
|
||||
package fun.asgc.neutrino.core.aop.compiler.internal.archive;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.Launcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
+2
-3
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.aop.compiler;
|
||||
|
||||
import fun.asgc.neutrino.core.aop.compiler.internal.JarLauncher;
|
||||
import fun.asgc.neutrino.core.util.ReflectUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -122,8 +121,8 @@ public class AsgcCompilerTest {
|
||||
*/
|
||||
@Test
|
||||
public void compileAndLoadClass5() throws Exception {
|
||||
JarLauncher jarLauncher = new JarLauncher(AsgcCompilerTest.class.getResource("/asgc-package-lab2.jar").getPath());
|
||||
AsgcCompiler compiler = new AsgcCompiler(jarLauncher.createClassLoader());
|
||||
AsgcCompiler compiler = new AsgcCompiler();
|
||||
compiler.addDependSpringBootJar(AsgcCompilerTest.class.getResource("/asgc-package-lab2.jar").getPath());
|
||||
String code = "package a.b;\n" +
|
||||
"import fun.asgc.lab.pkg.lab2.Console;\n" +
|
||||
"import fun.asgc.lab.pkg.lab1.Dog;\n" +
|
||||
|
||||
Reference in New Issue
Block a user