Skip to content

Commit 52d3da6

Browse files
authored
Fix loading of metadata when we have a split package (helidon-io#11170)
* Fix loading of metadata when we have a split package (such as main and test compilation using the same scope) * remove unnecessary configuration from test pom
1 parent eda85e4 commit 52d3da6

7 files changed

Lines changed: 274 additions & 13 deletions

File tree

metadata/metadata/src/main/java/io/helidon/metadata/MetadataDiscoveryImpl.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2025 Oracle and/or its affiliates.
2+
* Copyright (c) 2025, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@
4545
import java.util.concurrent.locks.Lock;
4646
import java.util.concurrent.locks.ReentrantLock;
4747
import java.util.jar.Manifest;
48+
import java.util.stream.Collectors;
4849
import java.util.stream.Stream;
4950

5051
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -284,12 +285,12 @@ private static MetadataDiscovery createFromResources(MetadataDiscoveryContext co
284285
String manifestFile = config.manifestFile();
285286
Set<String> metadataFiles = config.metadataFiles();
286287

287-
var manfiestStream = cl.resources(location + "/" + manifestFile)
288+
var manifestStream = cl.resources(location + "/" + manifestFile)
288289
.distinct()
289290
.flatMap(it -> parseHelidonManifest(cl, it));
290291
var nonManfiestStream = defaultMetadata(cl, location, metadataFiles);
291292

292-
Stream.concat(manfiestStream, nonManfiestStream)
293+
Stream.concat(manifestStream, nonManfiestStream)
293294
// remove duplicates, which may happen
294295
.distinct()
295296
.forEach(it -> metadataMap.computeIfAbsent(it.fileName(), k -> new ArrayList<>()).add(it));
@@ -343,21 +344,30 @@ private static Stream<MetadataFile> parseHelidonManifest(ClassLoader cl,
343344
// the line now contains an exact location of a resource
344345
String resourceLocation = line;
345346
FoundFile fileName = fileName(line);
346-
URL resourceUrl = cl.getResource(resourceLocation);
347-
if (resourceUrl == null) {
347+
348+
List<URL> resources = cl.resources(resourceLocation)
349+
.collect(Collectors.toUnmodifiableList());
350+
351+
352+
if (resources.isEmpty()) {
348353
throw new IllegalArgumentException("Metadata file " + resourceLocation + " not found, it is defined in "
349354
+ "manifest " + url + " at line " + lineNumber);
350355
}
351356

352-
if (LOGGER.isLoggable(Level.DEBUG)) {
353-
LOGGER.log(Level.DEBUG, "Adding file from manifest: " + resourceLocation + " at line " + lineNumber);
354-
}
357+
// we can safely add even resources that are duplicate, as there is a distinct on result of this method call
358+
for (URL resourceUrl : resources) {
359+
if (LOGGER.isLoggable(Level.DEBUG)) {
360+
LOGGER.log(Level.DEBUG,
361+
"Adding file from manifest: " + resourceLocation + " at line "
362+
+ lineNumber + ", location: " + resourceUrl);
363+
}
355364

356-
// only add files that are either in non-default directory, or that we do not know about
357-
// if in default directory, and in metadataFiles, we will look it up later
358-
foundFiles.add(MetadataFileImpl.create(resourceLocation,
359-
fileName.fileName(),
360-
resourceUrl));
365+
// only add files that are either in non-default directory, or that we do not know about
366+
// if in default directory, and in metadataFiles, we will look it up later
367+
foundFiles.add(MetadataFileImpl.create(resourceLocation,
368+
fileName.fileName(),
369+
resourceUrl));
370+
}
361371
}
362372

363373
return foundFiles.stream();

service/tests/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<module>events</module>
5858
<module>maven-plugin</module>
5959
<module>system-exit</module>
60+
<module>splitpackage</module>
6061
</modules>
6162

6263
</project>

service/tests/splitpackage/pom.xml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright (c) 2026 Oracle and/or its affiliates.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
20+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xmlns="http://maven.apache.org/POM/4.0.0"
22+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
23+
<parent>
24+
<groupId>io.helidon.service.tests</groupId>
25+
<artifactId>helidon-service-tests-project</artifactId>
26+
<version>4.4.0-SNAPSHOT</version>
27+
<relativePath>../pom.xml</relativePath>
28+
</parent>
29+
<modelVersion>4.0.0</modelVersion>
30+
31+
<artifactId>helidon-service-tests-packagesplit</artifactId>
32+
<name>Helidon Service Tests Package Split</name>
33+
<description>
34+
Test that we can "survive" service descriptors in the same location referenced from manifest.
35+
The package split will be caused by main and test locations using the same scope
36+
</description>
37+
38+
<properties>
39+
<helidon.services.skip>true</helidon.services.skip>
40+
</properties>
41+
42+
<dependencies>
43+
<dependency>
44+
<groupId>io.helidon.service</groupId>
45+
<artifactId>helidon-service-registry</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>io.helidon.config</groupId>
49+
<artifactId>helidon-config</artifactId>
50+
<scope>test</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.junit.jupiter</groupId>
54+
<artifactId>junit-jupiter-api</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.hamcrest</groupId>
59+
<artifactId>hamcrest-all</artifactId>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>io.helidon.testing</groupId>
64+
<artifactId>helidon-testing-junit5</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
</dependencies>
68+
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-compiler-plugin</artifactId>
74+
<executions>
75+
<execution>
76+
<id>default-compile</id>
77+
<goals>
78+
<goal>compile</goal>
79+
</goals>
80+
<configuration>
81+
<compilerArgument>-Ahelidon.codegen.scope=production</compilerArgument>
82+
</configuration>
83+
</execution>
84+
<execution>
85+
<id>default-testCompile</id>
86+
<goals>
87+
<goal>testCompile</goal>
88+
</goals>
89+
<configuration>
90+
<compilerArgument>-Ahelidon.codegen.scope=production</compilerArgument>
91+
</configuration>
92+
</execution>
93+
</executions>
94+
<configuration>
95+
<annotationProcessorPaths>
96+
<path>
97+
<groupId>io.helidon.codegen</groupId>
98+
<artifactId>helidon-codegen-apt</artifactId>
99+
<version>${project.version}</version>
100+
</path>
101+
<path>
102+
<groupId>io.helidon.service</groupId>
103+
<artifactId>helidon-service-codegen</artifactId>
104+
<version>${project.version}</version>
105+
</path>
106+
</annotationProcessorPaths>
107+
</configuration>
108+
<dependencies>
109+
<dependency>
110+
<groupId>io.helidon.codegen</groupId>
111+
<artifactId>helidon-codegen-apt</artifactId>
112+
<version>${project.version}</version>
113+
</dependency>
114+
<dependency>
115+
<groupId>io.helidon.service</groupId>
116+
<artifactId>helidon-service-codegen</artifactId>
117+
<version>${project.version}</version>
118+
</dependency>
119+
</dependencies>
120+
</plugin>
121+
</plugins>
122+
</build>
123+
</project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.service.tests.splitpackage;
18+
19+
import io.helidon.service.registry.Service;
20+
21+
@Service.Contract
22+
interface MyContract {
23+
String name();
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.service.tests.splitpackage;
18+
19+
import io.helidon.service.registry.Service;
20+
21+
@Service.Singleton
22+
class Service1 implements MyContract {
23+
@Override
24+
public String name() {
25+
return "1";
26+
}
27+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.service.tests.splitpackage;
18+
19+
import io.helidon.common.Weight;
20+
import io.helidon.common.Weighted;
21+
import io.helidon.service.registry.Service;
22+
23+
@Service.Singleton
24+
@Weight(Weighted.DEFAULT_WEIGHT + 100)
25+
class Service2 implements MyContract {
26+
@Override
27+
public String name() {
28+
return "2";
29+
}
30+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.service.tests.splitpackage;
18+
19+
import java.util.List;
20+
21+
import io.helidon.service.registry.ServiceRegistry;
22+
import io.helidon.testing.junit5.Testing;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.hamcrest.CoreMatchers.is;
27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
29+
30+
@Testing.Test
31+
public class SplitPackageTest {
32+
private final ServiceRegistry registry;
33+
34+
public SplitPackageTest(ServiceRegistry registry) {
35+
this.registry = registry;
36+
}
37+
38+
@Test
39+
public void testSplitPackage() {
40+
List<MyContract> services = registry.all(MyContract.class);
41+
42+
assertThat(services, hasSize(2));
43+
assertThat("Test service has higher weight, should be first", services.get(0).name(), is("2"));
44+
assertThat(services.get(1).name(), is("1"));
45+
}
46+
}

0 commit comments

Comments
 (0)