package com.oracle.svm.hosted.config;
import static com.oracle.svm.core.SubstrateOptions.PrintFlags;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.graalvm.nativeimage.impl.ReflectionRegistry;
import com.oracle.svm.core.configure.ConfigurationFiles;
import com.oracle.svm.core.configure.ConfigurationParser;
import com.oracle.svm.core.configure.ReflectionConfigurationParser;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.option.OptionUtils;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.json.JSONParserException;
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.hosted.NativeImageOptions;
public final class ConfigurationParserUtils {
public static ReflectionConfigurationParser<Class<?>> create(ReflectionRegistry registry, ImageClassLoader imageClassLoader) {
return new ReflectionConfigurationParser<>(new ReflectionRegistryAdapter(registry, imageClassLoader), NativeImageOptions.AllowIncompleteClasspath.getValue());
}
public static int parseAndRegisterConfigurations(ConfigurationParser parser, ImageClassLoader classLoader, String featureName,
HostedOptionKey<String[]> configFilesOption, HostedOptionKey<String[]> configResourcesOption, String directoryFileName) {
int parsedCount = 0;
Stream<Path> files = Stream.concat(OptionUtils.flatten(",", configFilesOption.getValue()).stream().map(Paths::get),
ConfigurationFiles.findConfigurationFiles(directoryFileName).stream());
parsedCount += files.map(Path::toAbsolutePath).mapToInt(path -> {
if (!Files.exists(path)) {
throw UserError.abort("The %s configuration file \"%s\" does not exist.", featureName, path);
}
try (Reader reader = new FileReader(path.toFile())) {
doParseAndRegister(parser, reader, featureName, path, configFilesOption);
return 1;
} catch (IOException e) {
throw UserError.abort("Could not open %s: %s", path, e.getMessage());
}
}).sum();
Stream<URL> configResourcesFromOption = OptionUtils.flatten(",", configResourcesOption.getValue()).stream().flatMap(s -> {
Enumeration<URL> urls;
try {
urls = classLoader.findResourcesByName(s);
} catch (IOException e) {
throw UserError.abort(e, "Error while looking for %s in %s", s, classLoader);
}
if (!urls.hasMoreElements()) {
throw UserError.abort("Could not find %s configuration resource \"%s\".", featureName, s);
}
return StreamSupport.stream(new AbstractSpliterator<URL>(Long.MAX_VALUE, Spliterator.ORDERED) {
@Override
public boolean tryAdvance(Consumer<? super URL> action) {
if (!urls.hasMoreElements()) {
return false;
}
action.accept(urls.nextElement());
return true;
}
}, false);
});
Stream<URL> resources = Stream.concat(configResourcesFromOption, ConfigurationFiles.findConfigurationResources(directoryFileName, classLoader.getClassLoader()).stream());
parsedCount += resources.mapToInt(url -> {
try {
URLConnection urlConnection = url.openConnection();
urlConnection.setUseCaches(false);
try (Reader reader = new InputStreamReader(urlConnection.getInputStream())) {
doParseAndRegister(parser, reader, featureName, url, configResourcesOption);
return 1;
}
} catch (IOException e) {
throw UserError.abort("Could not open %s: %s", url, e.getMessage());
}
}).sum();
return parsedCount;
}
private static void doParseAndRegister(ConfigurationParser parser, Reader reader, String featureName, Object location, HostedOptionKey<String[]> option) {
try {
parser.parseAndRegister(reader);
} catch (IOException | JSONParserException e) {
String errorMessage = e.getMessage();
if (errorMessage == null || errorMessage.isEmpty()) {
errorMessage = e.toString();
}
throw UserError.abort("Error parsing %s configuration in %s:%n%s%nVerify that the configuration matches the schema described in the %s output for option %s.",
featureName, location, errorMessage, SubstrateOptionsParser.commandArgument(PrintFlags, "+"), option.getName());
}
}
}