io.micronaut.aot.cli.Main Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of micronaut-aot-cli Show documentation
Show all versions of micronaut-aot-cli Show documentation
Build time optimizations for Micronaut
/*
* Copyright 2017-2021 original authors
*
* 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
*
* https://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 io.micronaut.aot.cli;
import io.micronaut.aot.ConfigKeys;
import io.micronaut.aot.MicronautAotOptimizer;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
/**
* Entry point for the Micronaut AOT command line interface.
*/
@Command(name = "micronaut-aot",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class,
description = "Generates classes for Micronaut AOT (build time optimizations)")
public class Main implements Runnable, ConfigKeys {
@Option(names = {"--classpath", "-cp"}, description = "The Micronaut application classpath", required = true)
private String classpathString;
@Option(names = {"--package", "-p"}, description = "The target package for generated classes", required = true)
private String packageName;
@Option(names = {"--runtime"}, description = "The target runtime. Possible values: jit, native")
private String runtime = "jit";
@Option(names = {"--config"}, description = "The configuration file (.properties)", required = true)
private File config;
@Option(names = {"--output", "-o"}, description = "The output directory", required = false)
private File outputDirectory;
@Override
public void run() {
List classpath = toURLs(classpathString);
Properties props = new Properties();
if (config.exists()) {
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(config))) {
props.load(reader);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
props.put(CLASSPATH, classpath.stream().map(url -> {
try {
return new File(url.toURI()).getAbsolutePath();
} catch (URISyntaxException e) {
return null;
}
}).collect(Collectors.joining(",")));
props.put(GENERATED_PACKAGE, packageName);
if (outputDirectory != null) {
props.put(OUTPUT_DIRECTORY, outputDirectory.getAbsolutePath());
}
props.put(RUNTIME, runtime);
URL[] urls = classpath.toArray(new URL[0]);
executeInIsolatedLoader(props, urls, Thread.currentThread().getContextClassLoader());
}
/**
* The Micronaut AOT runtime needs to be in the same classloader as the
* application classes. This method ensures that we create an isolated
* loader which can still see the bootstrap loader classes, but still
* isolates from the AOT classes which were loaded by this main class.
* @param props the configuration of the AOT optimizer
* @param urls the URLs to add to the classpath
* @param ctxClassLoader the current context classloader
*/
private void executeInIsolatedLoader(Properties props, URL[] urls, ClassLoader ctxClassLoader) {
URLClassLoader cl = new URLClassLoader(urls, new FilteringClassLoader(ctxClassLoader));
try {
Thread.currentThread().setContextClassLoader(cl);
Class> runnerClass = cl.loadClass("io.micronaut.aot.MicronautAotOptimizer");
assert runnerClass != MicronautAotOptimizer.class;
if (outputDirectory != null) {
runnerClass.getDeclaredMethod("execute", Properties.class)
.invoke(null, props);
} else {
runnerClass.getDeclaredMethod("exportConfiguration", String.class, File.class)
.invoke(null, runtime, config);
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
} finally {
Thread.currentThread().setContextClassLoader(ctxClassLoader);
}
}
private static List toURLs(String classpathString) {
return Arrays.stream(classpathString.split("[,;" + File.pathSeparator + "]"))
.map(File::new)
.map(File::toURI).map(uri -> {
try {
return uri.toURL();
} catch (MalformedURLException e) {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
public static int execute(String[] args) {
return new CommandLine(new Main()).execute(args);
}
public static void main(String[] args) {
System.exit(execute(args));
}
private static class FilteringClassLoader extends ClassLoader {
public FilteringClassLoader(ClassLoader ctxClassLoader) {
super(ctxClassLoader);
}
private Class> filter(String name, ClassFinder finder) throws ClassNotFoundException {
if (name.startsWith("io.micronaut")) {
throw new ClassNotFoundException(name);
}
return finder.find(name);
}
@Override
public Class> loadClass(String name) throws ClassNotFoundException {
return filter(name, super::loadClass);
}
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return filter(name, n -> super.loadClass(n, resolve));
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
//noinspection Convert2MethodRef
return filter(name, n -> super.findClass(n));
}
@FunctionalInterface
private interface ClassFinder {
Class> find(String name) throws ClassNotFoundException;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy