Skip to content

Commit c83e559

Browse files
committed
Refactor indirect module directives
1 parent 8cc2192 commit c83e559

7 files changed

Lines changed: 129 additions & 58 deletions

File tree

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ A configuration template with all supported tags:
356356
<Module groupId="..." artifactId="..." version="${config['modVersion']}" active="true">
357357
<Directives>
358358
<Directive type="opens/reads/exports" package="..." layer="..." module="..."/>
359-
<Directive type="allowsOpen/allowsRead/allowsExport" package="..." layer="..." module="..."/>
359+
<Directive type="requestsOpen/requestsRead/requestsExport" layer="..." module="..." package="..."/>
360360
</Directives>
361361
</Module>
362362
</Modules>
@@ -390,20 +390,33 @@ The layer attribute allows specifying the module's layer. If this attribute is n
390390
component is used. To specify the framework and component layer, either the name `foo` or the name and version
391391
`foo:1.0.0` is used. The name of the layer where the framework is located is `alpha-framework`.
392392

393-
The difference between the directives `opens`, `reads`, `exports` and the directives `allowsOpen`, `allowsRead`,
394-
`allowsExport` is that the former are applied directly to the module specified in the `Module` tag, while the latter
393+
The difference between the directives `opens`, `reads`, `exports` and the directives `requestsOpen`, `requestsRead`,
394+
`requestsExport` is that the former are applied directly to the module specified in the `Module` tag, while the latter
395395
are applied to the module specified in the `module` attribute. In other words, the first set of directives is applied
396396
directly to the configured module, while the second set is applied to another module, usually from the parent layer.
397397
For example, such a configuration:
398398

399399
```
400-
<Directive type="allowsRead" package="..." layer="..." module="foo.module"/>
400+
<Directive type="requestsRead" layer="foo" module="bar"/>
401401
```
402-
will result in the `reads` directive being added to the `foo.module` module.
402+
will result in the `reads` directive being added to the `bar` module from `foo` layer.
403403

404404
It is important to note that directives are added through the layer controller. Since JPMS does not provide access
405-
to the boot layer controller, you cannot add directives to the modules of this layer using `allows` directives in
406-
the component configuration.
405+
to the boot layer controller, `requests` directives cannot be applied to modules of the boot layer via the layer
406+
controller.
407+
408+
However, this limitation can be worked around using a relay approach: the boot layer module opens its package to the
409+
framework core module (configured via `--add-opens` at JVM startup), and since all layers are created by the core
410+
module, it relays the access further to the target module using `Module.addOpens()`. For example, the following
411+
configuration will open `java.time` from `java.base` to Gson:
412+
413+
```xml
414+
<Module groupId="com.google.code.gson" artifactId="gson" version="${gson.version}">
415+
<Directives>
416+
<Directive type="requestsOpen" layer="alpha-framework:${project.version}" module="java.base" package="java.time"/>
417+
</Directives>
418+
</Module>
419+
```
407420

408421
For a specific example of working with directives, see the configuration of the demo component — `alpha-console-gui`.
409422

alpha-assembly/alpha-assembly-maven-plugin/src/main/resources/com/techsenger/alpha/assembly/maven/plugin/cli-config.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@
1515
<Module groupId="org.apache.commons" artifactId="commons-lang3" version="${commons.lang.version}"/>
1616
<Module groupId="com.techsenger.toolkit" artifactId="toolkit-ascii" version="${toolkit.version}"/>
1717
<Module groupId="com.techsenger.toolkit" artifactId="toolkit-http" version="${toolkit.version}"/>
18-
<Module groupId="com.google.code.gson" artifactId="gson" version="${gson.version}"/>
18+
<Module groupId="com.google.code.gson" artifactId="gson" version="${gson.version}">
19+
<!--
20+
Gson requires reflective access to java.time classes for serialization/deserialization.
21+
Since java.base is in the boot layer and JPMS does not provide access to the boot layer
22+
controller, we use a relay approach: java.base opens java.time to the framework core module
23+
via the add-opens at JVM startup, and the core module relays the access to Gson using
24+
Module.addOpens().
25+
-->
26+
<Directives>
27+
<Directive type="requestsOpen" layer="alpha-framework:${project.version}" module="java.base" package="java.time"/>
28+
</Directives>
29+
</Module>
1930

2031
<!--### ALPHA ###-->
2132
<Module groupId="com.techsenger.alpha.net" artifactId="alpha-net-shared" version="${project.version}"/>

alpha-assembly/alpha-assembly-maven-plugin/src/main/resources/com/techsenger/alpha/assembly/maven/plugin/server-config.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@
77

88
<Modules>
99
<!--### TOOLS ###-->
10-
<Module groupId="com.google.code.gson" artifactId="gson" version="${gson.version}"/>
10+
<Module groupId="com.google.code.gson" artifactId="gson" version="${gson.version}">
11+
<!--
12+
Gson requires reflective access to java.time classes for serialization/deserialization.
13+
Since java.base is in the boot layer and JPMS does not provide access to the boot layer
14+
controller, we use a relay approach: java.base opens java.time to the framework core module
15+
via the add-opens at JVM startup, and the core module relays the access to Gson using
16+
Module.addOpens().
17+
-->
18+
<Directives>
19+
<Directive type="requestsOpen" layer="alpha-framework:${project.version}" module="java.base" package="java.time"/>
20+
</Directives>
21+
</Module>
1122

1223
<!--### ALPHA ###-->
1324
<Module groupId="com.techsenger.alpha.net" artifactId="alpha-net-shared" version="${project.version}"/>

alpha-core/src/main/java/com/techsenger/alpha/core/api/module/DirectiveType.java

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,56 +17,82 @@
1717
package com.techsenger.alpha.core.api.module;
1818

1919
/**
20+
* Defines the types of directives that can be applied to a module. Directives are divided into two groups.
21+
*
22+
* <p>Direct directives ({@link #OPENS}, {@link #READS}, {@link #EXPORTS}) are applied to the configured module itself.
23+
* <p>Indirect directives ({@link #REQUESTS_OPEN}, {@link #REQUESTS_READ}, {@link #REQUESTS_EXPORT}) are applied to
24+
* another module, making it open, read, or export something to the configured module.
2025
*
2126
* @author Pavel Castornii
2227
*/
2328
public enum DirectiveType {
2429

2530
/**
26-
* Indicates that this module will open its package to another module.
31+
* Indicates that the configured module opens its package to another module.
2732
*/
2833
OPENS,
2934

3035
/**
31-
* Indicates that this directive if this module will read a package from another module.
36+
* Indicates that the configured module reads another module.
3237
*/
3338
READS,
3439

3540
/**
36-
* Indicates that this module will export its package to another module.
41+
* Indicates that the configured module exports its package to another module.
3742
*/
3843
EXPORTS,
3944

4045
/**
41-
* Indicates that another module will open its package to this module.
46+
* Indicates that another module is requested to open its package to the configured module.
4247
*/
43-
ALLOWS_OPEN,
48+
REQUESTS_OPEN,
4449

4550
/**
46-
* Indicates that another module will read a package from this module.
51+
* Indicates that another module is requested to read the configured module.
4752
*/
48-
ALLOWS_READ,
53+
REQUESTS_READ,
4954

5055
/**
51-
* Indicates that another module will export its package to this module.
56+
* Indicates that another module is requested to export its package to the configured module.
5257
*/
53-
ALLOWS_EXPORT;
58+
REQUESTS_EXPORT;
5459

5560
static {
56-
OPENS.opposite = ALLOWS_OPEN;
57-
READS.opposite = ALLOWS_READ;
58-
EXPORTS.opposite = ALLOWS_EXPORT;
59-
ALLOWS_OPEN.opposite = OPENS;
60-
ALLOWS_READ.opposite = READS;
61-
ALLOWS_EXPORT.opposite = EXPORTS;
61+
OPENS.opposite = REQUESTS_OPEN;
62+
READS.opposite = REQUESTS_READ;
63+
EXPORTS.opposite = REQUESTS_EXPORT;
64+
REQUESTS_OPEN.opposite = OPENS;
65+
REQUESTS_READ.opposite = READS;
66+
REQUESTS_EXPORT.opposite = EXPORTS;
6267
}
6368

6469
private DirectiveType opposite;
6570

6671
/**
67-
* Returns opposite value. For example, ALLOWS_OPEN -> OPEN, OPEN -> ALLOWS_OPEN etc.
72+
* Returns {@code true} if this is a direct directive, i.e. it is applied to the configured
73+
* module itself.
74+
*
75+
* @return {@code true} if direct, {@code false} if indirect
76+
*/
77+
public boolean isDirect() {
78+
return this == OPENS || this == READS || this == EXPORTS;
79+
}
80+
81+
/**
82+
* Returns {@code true} if this is an indirect directive, i.e. it is applied to another
83+
* module.
84+
*
85+
* @return {@code true} if indirect, {@code false} if direct
86+
*/
87+
public boolean isIndirect() {
88+
return !isDirect();
89+
}
90+
91+
/**
92+
* Returns the opposite directive type. For example, {@link #OPENS} returns
93+
* {@link #REQUESTS_OPEN} and vice versa.
6894
*
69-
* @return
95+
* @return the opposite directive type
7096
*/
7197
public DirectiveType getOpposite() {
7298
return opposite;

alpha-core/src/main/java/com/techsenger/alpha/core/impl/LayerBuilder.java

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -266,61 +266,63 @@ private void addModuleDirectives(DefaultComponent component) {
266266

267267
private void addDirectModuleDirective(DefaultComponent component, Module module, ModuleDirective directive,
268268
List<ResolvedModuleDirective> resolvedDirectives) {
269-
int directiveCount = 0;
270269
var layers = findLayers(component, directive.getLayer());
271270
for (var layer : layers.values()) {
272271
var otherModule = ModuleUtils.findModule(directive.getModule(), layer);
273-
if (directive.getType() == DirectiveType.EXPORTS) {
274-
component.getLayerController().addExports(module, directive.getPackage(), otherModule);
275-
} else if (directive.getType() == DirectiveType.OPENS) {
276-
component.getLayerController().addOpens(module, directive.getPackage(), otherModule);
277-
} else if (directive.getType() == DirectiveType.READS) {
278-
component.getLayerController().addReads(module, otherModule);
279-
}
272+
logger.debug("Processing direct directive {} for module {} in {}",
273+
directive.getType(), module.getName(), component.getDescriptor().getConfig().getFullName());
274+
applyModuleDirective(component.getLayerController(), module, directive.getType(),
275+
directive.getPackage(), otherModule);
280276
var resolvedDirective = new DefaultResolvedModuleDirective(directive.getType(),
281277
module, directive.getPackage(), otherModule);
282278
resolvedDirectives.add(resolvedDirective);
283-
directiveCount++;
284-
}
285-
if (logger.isDebugEnabled()) {
286-
logger.debug("Added directive: {} to module: {} in layer: {}, about package: {}, for module: {} "
287-
+ "in layers: {} {} times", directive.getType(), module.getName(), component.getDescriptor().getConfig()
288-
.getFullName(), directive.getPackage(), directive.getModule(), layers.keySet(), directiveCount);
289279
}
290280
}
291281

292282
private void addIndirectModuleDirective(DefaultComponent component, Module module, ModuleDirective directive,
293283
List<ResolvedModuleDirective> resolvedDirectives) {
294-
int directiveCount = 0;
295284
var layers = findLayers(component, directive.getLayer());
296285
for (var layer : layers.values()) {
297286
var otherModule = ModuleUtils.findModule(directive.getModule(), layer);
298287
ModuleLayer.Controller layerController = null;
299288
DefaultComponent layerComponent = (DefaultComponent) this.componentManager.findComponent(layer);
300-
if (layerComponent != null) { //not boot layer
289+
if (layerComponent != null) {
301290
layerController = layerComponent.getLayerController();
302-
} else {
303-
logger.warn("Couldn't add directive {} to module {} because layer controller not available",
304-
directive.getType(), otherModule.getName());
305-
continue;
306-
}
307-
if (directive.getType() == DirectiveType.ALLOWS_EXPORT) {
308-
layerController.addExports(otherModule, directive.getPackage(), module);
309-
} else if (directive.getType() == DirectiveType.ALLOWS_OPEN) {
310-
layerController.addOpens(otherModule, directive.getPackage(), module);
311-
} else if (directive.getType() == DirectiveType.ALLOWS_READ) {
312-
layerController.addReads(otherModule, module);
313291
}
292+
logger.debug("Processing indirect directive {} for module {} in {}",
293+
directive.getType(), module.getName(), component.getDescriptor().getConfig().getFullName());
294+
applyModuleDirective(layerController, otherModule, directive.getType().getOpposite(),
295+
directive.getPackage(), module);
314296
var resolvedDirective = new DefaultResolvedModuleDirective(directive.getType(),
315297
module, directive.getPackage(), otherModule);
316298
resolvedDirectives.add(resolvedDirective);
317-
directiveCount++;
299+
}
300+
}
301+
302+
private void applyModuleDirective(ModuleLayer.Controller controller, Module module, DirectiveType type,
303+
String pkg, Module otherModule) {
304+
if (controller != null) {
305+
if (type == DirectiveType.EXPORTS) {
306+
controller.addExports(module, pkg, otherModule);
307+
} else if (type == DirectiveType.OPENS) {
308+
controller.addOpens(module, pkg, otherModule);
309+
} else if (type == DirectiveType.READS) {
310+
controller.addReads(module, otherModule);
311+
}
312+
} else {
313+
logger.debug("No layer controller available for module: {}, applying directive {} via module API",
314+
module.getName(), type);
315+
if (type == DirectiveType.EXPORTS) {
316+
module.addExports(pkg, otherModule);
317+
} else if (type == DirectiveType.OPENS) {
318+
module.addOpens(pkg, otherModule);
319+
} else if (type == DirectiveType.READS) {
320+
module.addReads(otherModule);
321+
}
318322
}
319323
if (logger.isDebugEnabled()) {
320-
logger.debug("Added directive: {} to module: {} in layers: {}, about package: {}, for module: {} "
321-
+ "in layer: {} {} times", directive.getType(), directive.getModule(), layers.keySet(),
322-
directive.getPackage(), module.getName(), component.getDescriptor().getConfig().getFullName(),
323-
directiveCount);
324+
logger.debug("Applied directive: {} to module: {}, package: {}, for module: {}",
325+
type, module.getName(), pkg, otherModule.getName());
324326
}
325327
}
326328

alpha-it/alpha-it-core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<name>Alpha - IT Core</name>
1414
<description>Integration tests for framework core</description>
1515

16+
<properties>
17+
<failsafe.plugin.arguments>--add-opens java.base/java.time=com.techsenger.alpha.core</failsafe.plugin.arguments>
18+
</properties>
19+
1620
<dependencies>
1721
<dependency>
1822
<groupId>com.techsenger.toolkit</groupId>

alpha-it/alpha-it-net/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<name>Alpha - IT Net</name>
1414
<description>Integration tests for client-server interaction</description>
1515

16+
<properties>
17+
<failsafe.plugin.arguments>--add-opens java.base/java.time=com.techsenger.alpha.core</failsafe.plugin.arguments>
18+
</properties>
19+
1620
<dependencies>
1721
<dependency>
1822
<groupId>com.techsenger.toolkit</groupId>

0 commit comments

Comments
 (0)