Skip to content

Commit

Permalink
Conditional proxy configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
vjovanov committed Sep 14, 2021
1 parent 8ae3815 commit 5721b9b
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ private static void doTestMethods(TypeConfiguration typeConfig) {
}

private static void doTestProxyConfig(ProxyConfiguration proxyConfig) {
Assert.assertFalse(proxyConfig.contains("testProxySeenA", "testProxySeenB", "testProxySeenC"));
Assert.assertTrue(proxyConfig.contains("testProxyUnseen"));
ConfigurationCondition condition = ConfigurationCondition.objectReachable();
Assert.assertFalse(proxyConfig.contains(condition, "testProxySeenA", "testProxySeenB", "testProxySeenC"));
Assert.assertTrue(proxyConfig.contains(condition, "testProxyUnseen"));
}

private static void doTestResourceConfig(ResourceConfiguration resourceConfig) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
Expand Down Expand Up @@ -112,7 +112,7 @@ public TypeConfiguration loadReflectConfig(Function<IOException, Exception> exce

public ProxyConfiguration loadProxyConfig(Function<IOException, Exception> exceptionHandler) throws Exception {
ProxyConfiguration proxyConfiguration = new ProxyConfiguration();
loadConfig(proxyConfigPaths, new ProxyConfigurationParser(types -> proxyConfiguration.add(Arrays.asList(types)), true), exceptionHandler);
loadConfig(proxyConfigPaths, new ProxyConfigurationParser(types -> proxyConfiguration.add(types.getCondition(), new ArrayList<>(types.getElement())), true), exceptionHandler);
return proxyConfiguration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,34 @@
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.graalvm.nativeimage.impl.ConfigurationCondition;

import com.oracle.svm.configure.ConfigurationBase;
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.configure.ConditionalElement;

public class ProxyConfiguration implements ConfigurationBase {
private final ConcurrentHashMap.KeySetView<List<String>, Boolean> interfaceLists = ConcurrentHashMap.newKeySet();
private final ConcurrentHashMap.KeySetView<ConditionalElement<List<String>>, Boolean> interfaceLists = ConcurrentHashMap.newKeySet();

public ProxyConfiguration() {
}

public ProxyConfiguration(ProxyConfiguration other) {
for (List<String> interfaceList : other.interfaceLists) {
interfaceLists.add(new ArrayList<>(interfaceList));
for (ConditionalElement<List<String>> interfaceList : other.interfaceLists) {
interfaceLists.add(new ConditionalElement<>(interfaceList.getCondition(), new ArrayList<>(interfaceList.getElement())));
}
}

public void add(List<String> interfaceList) {
interfaceLists.add(interfaceList);
public void add(ConfigurationCondition condition, List<String> interfaceList) {
interfaceLists.add(new ConditionalElement<>(condition, interfaceList));
}

public boolean contains(List<String> interfaceList) {
return interfaceLists.contains(interfaceList);
public boolean contains(ConfigurationCondition condition, List<String> interfaceList) {
return interfaceLists.contains(new ConditionalElement<>(condition, interfaceList));
}

public boolean contains(String... interfaces) {
return contains(Arrays.asList(interfaces));
public boolean contains(ConfigurationCondition condition, String... interfaces) {
return contains(condition, Arrays.asList(interfaces));
}

public void removeAll(ProxyConfiguration other) {
Expand All @@ -63,29 +66,25 @@ public void removeAll(ProxyConfiguration other) {

@Override
public void printJson(JsonWriter writer) throws IOException {
List<String[]> lists = new ArrayList<>(interfaceLists.size());
for (List<String> list : interfaceLists) {
lists.add(list.toArray(new String[0]));
}
lists.sort((a, b) -> {
int c = 0;
for (int i = 0; c == 0 && i < a.length && i < b.length; i++) {
c = a[i].compareTo(b[i]);
}
return (c != 0) ? c : (a.length - b.length);
});
List<ConditionalElement<List<String>>> lists = new ArrayList<>(interfaceLists.size());
lists.addAll(interfaceLists);
lists.sort(ConditionalElement.comparator(ProxyConfiguration::compareList));

writer.append('[');
writer.indent();
String prefix = "";
for (String[] list : lists) {
writer.append(prefix).newline().append('[');
for (ConditionalElement<List<String>> list : lists) {
writer.append(prefix).newline();
writer.append('{').indent().newline();
ConfigurationConditionPrintable.printConditionAttribute(list.getCondition(), writer);
writer.quote("interfaces").append(":").append('[');
String typePrefix = "";
for (String type : list) {
for (String type : list.getElement()) {
writer.append(typePrefix).quote(type);
typePrefix = ",";
}
writer.append(']');
writer.append('}').unindent().newline();
prefix = ",";
}
writer.unindent().newline();
Expand All @@ -97,4 +96,14 @@ public boolean isEmpty() {
return interfaceLists.isEmpty();
}

private static <T extends Comparable<T>> int compareList(List<T> l1, List<T> l2) {
for (int i = 0; i < l1.size() && i < l2.size(); i++) {
int c = l1.get(i).compareTo(l2.get(i));
if (c != 0) {
return c;
}
}
return l1.size() - l2.size();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ private void addDynamicProxy(List<?> interfaceList, LazyValue<String> callerClas
return;
}
}
proxyConfiguration.add(interfaces);
proxyConfiguration.add(ConfigurationCondition.objectReachable(), interfaces);
}

private void addDynamicProxyUnchecked(List<?> checkedInterfaceList, List<?> uncheckedInterfaceList, LazyValue<String> callerClass) {
Expand All @@ -285,6 +285,6 @@ private void addDynamicProxyUnchecked(List<?> checkedInterfaceList, List<?> unch
List<String> interfaces = new ArrayList<>();
interfaces.addAll(checkedInterfaces);
interfaces.addAll(uncheckedInterfaces);
proxyConfiguration.add(interfaces);
proxyConfiguration.add(ConfigurationCondition.objectReachable(), interfaces);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,11 @@ public static <T extends Comparable<T>> Comparator<ConditionalElement<T>> compar
.thenComparing(ConditionalElement::getCondition)
.compare(o1, o2);
}

public static <T> Comparator<ConditionalElement<T>> comparator(Comparator<T> elementComparator) {
return (o1, o2) -> Comparator
.comparing((Function<ConditionalElement<T>, T>) ConditionalElement::getElement, elementComparator)
.thenComparing(ConditionalElement::getCondition)
.compare(o1, o2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.graalvm.nativeimage.impl.ConfigurationCondition;

import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.util.json.JSONParser;
Expand All @@ -41,9 +44,9 @@
* Parses JSON describing lists of interfaces and register them in the {@link DynamicProxyRegistry}.
*/
public final class ProxyConfigurationParser extends ConfigurationParser {
private final Consumer<String[]> interfaceListConsumer;
private final Consumer<ConditionalElement<List<String>>> interfaceListConsumer;

public ProxyConfigurationParser(Consumer<String[]> interfaceListConsumer, boolean strictConfiguration) {
public ProxyConfigurationParser(Consumer<ConditionalElement<List<String>>> interfaceListConsumer, boolean strictConfiguration) {
super(strictConfiguration);
this.interfaceListConsumer = interfaceListConsumer;
}
Expand All @@ -61,10 +64,10 @@ private void parseTopLevelArray(List<Object> proxyConfiguration) {
for (Object proxyConfigurationObject : proxyConfiguration) {
if (proxyConfigurationObject instanceof List) {
foundInterfaceLists = true;
parseInterfaceList(asList(proxyConfigurationObject, "<shouldn't reach here>"));
parseInterfaceList(ConfigurationCondition.objectReachable(), asList(proxyConfigurationObject, "<shouldn't reach here>"));
} else if (proxyConfigurationObject instanceof Map) {
foundProxyConfigurationObjects = true;
parseWithPredicatedConfig(asMap(proxyConfigurationObject, "<shouldn't reach here>"));
parseWithConditionalConfig(asMap(proxyConfigurationObject, "<shouldn't reach here>"));
} else {
throw new JSONParserException("second level must be composed of either interface lists or proxy configuration objects");
}
Expand All @@ -74,23 +77,20 @@ private void parseTopLevelArray(List<Object> proxyConfiguration) {
}
}

private void parseInterfaceList(List<?> data) {
String[] interfaces = new String[data.size()];
int i = 0;
for (Object value : data) {
interfaces[i] = asString(value);
i++;
}
private void parseInterfaceList(ConfigurationCondition condition, List<?> data) {
List<String> interfaces = data.stream().map(ConfigurationParser::asString).collect(Collectors.toList());

try {
interfaceListConsumer.accept(interfaces);
interfaceListConsumer.accept(new ConditionalElement<>(condition, interfaces));
} catch (Exception e) {
throw new JSONParserException(e.toString());
}
}

private void parseWithPredicatedConfig(Map<String, Object> proxyConfigObject) {
checkAttributes(proxyConfigObject, "proxy descriptor object", Collections.singleton("interfaces"));
parseInterfaceList(asList(proxyConfigObject.get("interfaces"), "\"interfaces\" must be an array of fully qualified interface names"));
private void parseWithConditionalConfig(Map<String, Object> proxyConfigObject) {
checkAttributes(proxyConfigObject, "proxy descriptor object", Collections.singleton("interfaces"), Collections.singletonList(CONDITIONAL_KEY));
ConfigurationCondition condition = parseCondition(proxyConfigObject);
parseInterfaceList(condition, asList(proxyConfigObject.get("interfaces"), "\"interfaces\" must be an array of fully qualified interface names"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@
*/
package com.oracle.svm.hosted.jdk;

import java.awt.*;

import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
import org.graalvm.nativeimage.impl.InternalPlatform;

import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.configure.ResourcesRegistry;
Expand All @@ -43,6 +33,15 @@
import com.oracle.svm.core.jni.JNIRuntimeAccess;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.c.NativeLibraries;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
import org.graalvm.nativeimage.impl.InternalPlatform;

import java.awt.GraphicsEnvironment;

@Platforms({InternalPlatform.PLATFORM_JNI.class})
@AutomaticFeature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@

import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

import com.oracle.svm.core.configure.ConfigurationFile;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.configure.ConfigurationFile;
import com.oracle.svm.core.configure.ConfigurationFiles;
import com.oracle.svm.core.configure.ProxyConfigurationParser;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.ConfigurationTypeResolver;
import com.oracle.svm.hosted.FallbackFeature;
import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl;
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.hosted.NativeImageOptions;
import com.oracle.svm.hosted.config.ConfigurationParserUtils;
import com.oracle.svm.reflect.hosted.ReflectionFeature;
import com.oracle.svm.reflect.proxy.DynamicProxySupport;
Expand All @@ -60,29 +60,29 @@ public void duringSetup(DuringSetupAccess a) {
ImageClassLoader imageClassLoader = access.getImageClassLoader();
DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader());
ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport);

Consumer<String[]> adapter = interfaceNames -> {
Class<?>[] interfaces = new Class<?>[interfaceNames.length];
for (int i = 0; i < interfaceNames.length; i++) {
String className = interfaceNames[i];
Class<?> clazz = imageClassLoader.findClass(className).get();
if (clazz == null) {
throw UserError.abort("Class " + className + " not found");
}
if (!clazz.isInterface()) {
throw UserError.abort("The class \"" + className + "\" is not an interface.");
}
interfaces[i] = clazz;
}
/* The interfaces array can be empty. The java.lang.reflect.Proxy API allows it. */
dynamicProxySupport.addProxyClass(interfaces);
};
ProxyConfigurationParser parser = new ProxyConfigurationParser(adapter, ConfigurationFiles.Options.StrictConfiguration.getValue());
ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue());
ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader);
ImageSingletons.add(ProxyRegistry.class, proxyRegistry);
ProxyConfigurationParser parser = new ProxyConfigurationParser(proxyRegistry, ConfigurationFiles.Options.StrictConfiguration.getValue());
loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, imageClassLoader, "dynamic proxy",
ConfigurationFiles.Options.DynamicProxyConfigurationFiles, ConfigurationFiles.Options.DynamicProxyConfigurationResources,
ConfigurationFile.DYNAMIC_PROXY.getFileName());
}

private static ProxyRegistry proxyRegistry() {
return ImageSingletons.lookup(ProxyRegistry.class);
}

@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
proxyRegistry().flushConditionalConfiguration(access);
}

@Override
public void duringAnalysis(DuringAnalysisAccess access) {
proxyRegistry().flushConditionalConfiguration(access);
}

@Override
public void beforeCompilation(BeforeCompilationAccess access) {
if (!ImageSingletons.contains(FallbackFeature.class)) {
Expand Down
Loading

0 comments on commit 5721b9b

Please sign in to comment.