Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow qualified exports to extended modules #95243

Merged
merged 8 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
rework to not require plugins to implement anything
  • Loading branch information
rjernst committed Apr 26, 2023
commit 8b44d57ecee9f41b31165b2d12c01c0f81bb0008
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@
*/
public abstract class ModuleQualifiedExportsService {

protected final Module selfModule = getClass().getModule();

protected final Module module;
private final Map<String, List<String>> qualifiedExports;
private final Map<String, List<String>> qualifiedOpens;
private final Set<String> targets;

protected ModuleQualifiedExportsService() {
this.qualifiedExports = invert(selfModule.getDescriptor().exports(), Exports::isQualified, Exports::source, Exports::targets);
this.qualifiedOpens = invert(selfModule.getDescriptor().opens(), Opens::isQualified, Opens::source, Opens::targets);
this(null);
}

protected ModuleQualifiedExportsService(Module module) {
this.module = module == null ? getClass().getModule() : module;
this.qualifiedExports = invert(this.module.getDescriptor().exports(), Exports::isQualified, Exports::source, Exports::targets);
this.qualifiedOpens = invert(this.module.getDescriptor().opens(), Opens::isQualified, Opens::source, Opens::targets);
this.targets = Stream.concat(qualifiedExports.keySet().stream(), qualifiedOpens.keySet().stream())
.collect(Collectors.toUnmodifiableSet());
}
Expand All @@ -62,6 +66,7 @@ private <T> Map<String, List<String>> invert(
targetsToSources.computeIfAbsent(target, k -> new ArrayList<>()).add(source);
}
}
targetsToSources.replaceAll((k, v) -> List.copyOf(v));
return Map.copyOf(targetsToSources);
}

Expand All @@ -80,7 +85,7 @@ public void addExportsAndOpens(Module target) {
String targetName = target.getName();
if (targets.contains(targetName) == false) {
throw new IllegalArgumentException(
"Module " + selfModule.getName() + " does not contain qualified exports or opens for module " + targetName
"Module " + module.getName() + " does not contain qualified exports or opens for module " + targetName
);
}
List<String> exports = qualifiedExports.getOrDefault(targetName, List.of());
Expand All @@ -96,15 +101,15 @@ public void addExportsAndOpens(Module target) {
/**
* Add a qualified export. This should always be implemented as follows:
* <code>
* selfModule.addExports(pkg, target);
* module.addExports(pkg, target);
* </code>
*/
protected abstract void addExports(String pkg, Module target);

/**
* Add a qualified open. This should always be implemented as follows:
* <code>
* selfModule.addOpens(pkg, target);
* module.addOpens(pkg, target);
* </code>
*/
protected abstract void addOpens(String pkg, Module target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
import org.elasticsearch.jdk.ModuleQualifiedExportsService;

public class PreallocateModuleExportsService extends ModuleQualifiedExportsService {

@Override
protected void addExports(String pkg, Module target) {
selfModule.addExports(pkg, target);
module.addExports(pkg, target);
}

@Override
protected void addOpens(String pkg, Module target) {
selfModule.addOpens(pkg, target);
module.addOpens(pkg, target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import java.io.UncheckedIOException;
import java.lang.ModuleLayer.Controller;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.reflect.Constructor;
import java.net.URI;
Expand Down Expand Up @@ -131,6 +130,7 @@ public PluginsService(Settings settings, Path configPath, Path modulesDirectory,

Map<String, List<ModuleQualifiedExportsService>> qualifiedExports = new HashMap<>();
loadExportsServices(qualifiedExports, PluginsService.class.getClassLoader());
addServerExportsService(qualifiedExports);

Set<PluginBundle> seenBundles = new LinkedHashSet<>();

Expand Down Expand Up @@ -481,9 +481,6 @@ private void loadBundle(
// and initialize them appropriately.
privilegedSetContextClassLoader(pluginClassLoader);

// load any qualified exports/opens to other modules/plugins
loadExportsServices(qualifiedExports, pluginClassLoader);

Plugin plugin;
if (bundle.pluginDescriptor().isStable()) {
stablePluginsRegistry.scanBundleForStablePlugins(bundle, pluginClassLoader);
Expand Down Expand Up @@ -754,7 +751,10 @@ static LayerAndLoader createModuleLayer(
var controller = privilegedDefineModulesWithOneLoader(configuration, parentLayersOrBoot(parentLayers), parentLoader);
var pluginModule = controller.layer().findModule(moduleName).get();
ensureEntryPointAccessible(controller, pluginModule, className);
addQualifiedExportsAndOpens(pluginModule, qualifiedExports);
// export/open upstream modules to this plugin module
exposeQualifiedExportsAndOpens(pluginModule, qualifiedExports);
// configure qualified exports/opens to other modules/plugins
addPluginExportsServices(qualifiedExports, controller);
logger.debug(() -> "Loading bundle: created module layer and loader for module " + moduleName);
return new LayerAndLoader(controller.layer(), privilegedFindLoader(controller.layer(), moduleName));
}
Expand Down Expand Up @@ -783,37 +783,62 @@ private static void ensureEntryPointAccessible(Controller controller, Module plu
}

/**
* Adds qualified exports and opens declared in the server module descriptor to the target module.
* Adds qualified exports and opens declared in other upstream modules to the target module.
* This is required since qualified statements targeting yet-to-be-created modules, i.e. plugins,
* are silently dropped when the boot layer is created.
*/
private static void addQualifiedExportsAndOpens(Module target, Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
// TODO: implement server module exports with ModuleQualifiedExportsService
serverModule.getDescriptor()
.exports()
.stream()
.filter(ModuleDescriptor.Exports::isQualified)
.filter(exports -> exports.targets().contains(target.getName()))
.forEach(exports -> serverModule.addExports(exports.source(), target));
serverModule.getDescriptor()
.opens()
.stream()
.filter(ModuleDescriptor.Opens::isQualified)
.filter(opens -> opens.targets().contains(target.getName()))
.forEach(opens -> serverModule.addOpens(opens.source(), target));

private static void exposeQualifiedExportsAndOpens(Module target, Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
qualifiedExports.getOrDefault(target.getName(), List.of()).forEach(exportService -> exportService.addExportsAndOpens(target));
}

private static void loadExportsServices(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports, ClassLoader classLoader) {
var loader = ServiceLoader.load(ModuleQualifiedExportsService.class, classLoader);
for (var exportService : loader) {
for (String targetName : exportService.getTargets()) {
logger.debug(
"Registered qualified export from module " + exportService.getClass().getModule().getName() + " to " + targetName
);
qualifiedExports.computeIfAbsent(targetName, k -> new ArrayList<>()).add(exportService);
for (var exportsService : loader) {
addExportsService(qualifiedExports, exportsService, exportsService.getClass().getModule().getName());
}
}

private static void addExportsService(
Map<String, List<ModuleQualifiedExportsService>> qualifiedExports,
ModuleQualifiedExportsService exportsService,
String moduleName
) {
for (String targetName : exportsService.getTargets()) {
logger.debug("Registered qualified export from module " + moduleName + " to " + targetName);
qualifiedExports.computeIfAbsent(targetName, k -> new ArrayList<>()).add(exportsService);
}
}

private static void addServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
final Module serverModule = PluginsService.class.getModule();
var exportsService = new ModuleQualifiedExportsService(PluginsService.class.getModule()) {
@Override
protected void addExports(String pkg, Module target) {
serverModule.addExports(pkg, target);
}

@Override
protected void addOpens(String pkg, Module target) {
serverModule.addOpens(pkg, target);
}
};
addExportsService(qualifiedExports, exportsService, serverModule.getName());
}

private static void addPluginExportsServices(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports, Controller controller) {
for (Module module : controller.layer().modules()) {
var exportsService = new ModuleQualifiedExportsService(module) {
@Override
protected void addExports(String pkg, Module target) {
controller.addExports(module, pkg, target);
}

@Override
protected void addOpens(String pkg, Module target) {
controller.addOpens(module, pkg, target);
}
};
addExportsService(qualifiedExports, exportsService, module.getName());
}
}

Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugin/core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,4 @@
exports org.elasticsearch.xpack.core.watcher.trigger;
exports org.elasticsearch.xpack.core.watcher.watch;
exports org.elasticsearch.xpack.core.watcher;

provides org.elasticsearch.jdk.ModuleQualifiedExportsService with org.elasticsearch.xpack.core.XPackCoreModuleExportsService;
}

This file was deleted.