Skip to content

Commit

Permalink
Review issues
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkec committed Aug 26, 2024
1 parent 7f2c144 commit dd505a6
Show file tree
Hide file tree
Showing 14 changed files with 749 additions and 30 deletions.
5 changes: 4 additions & 1 deletion etc/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@
<module name="JavadocVariable">
<property name="scope" value="protected"/>
</module>
<module name="JavadocStyle"/>
<module name="JavadocStyle">
<!-- https://github.com/checkstyle/checkstyle/issues/3351 -->
<property name="checkHtml" value="false"/>
</module>

<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
Expand Down
7 changes: 6 additions & 1 deletion maven-plugins/shade-extensions/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Helidon Shade Maven Plugin Extensions

Allows shading Helidon artefacts with[ Maven Shade plugin](https://maven.apache.org/plugins/maven-shade-plugin/)
by providing transformer for combining `META-INF/helidon/service-registry.json` files to single one.
by providing transformer for aggregating service registry files.

Aggregated files:
* `META-INF/helidon/service-registry.json`
* `META-INF/helidon/config-metadata.json`
* `META-INF/helidon/service.loader`

### General usage

Expand Down
20 changes: 18 additions & 2 deletions maven-plugins/shade-extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,24 @@
<artifactId>maven-shade-plugin</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@

package io.helidon.shade.transformers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
Expand All @@ -32,10 +35,12 @@
import org.apache.maven.plugins.shade.relocation.Relocator;
import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
* Maven Shade plugin custom transformer for merging Helidon service-registry files.
* Usage:
* <pre>{@code
* <pre><![CDATA[{@code
* <plugin>
* <groupId>org.apache.maven.plugins</groupId>
* <artifactId>maven-shade-plugin</artifactId>
Expand All @@ -62,17 +67,22 @@
* </dependency>
* </dependencies>
* </plugin>
* }</pre>
* }]]</pre>
*/
public class HelidonServiceTransformer implements ReproducibleResourceTransformer {

private static final String PATH = "META-INF/helidon/service-registry.json";
private final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
private JsonArray result = null;
static final String SERVICE_REGISTRY_PATH = "META-INF/helidon/service-registry.json";
static final String CONFIG_METADATA_PATH = "META-INF/helidon/config-metadata.json";
static final String SERVICE_LOADER_PATH = "META-INF/helidon/service.loader";

private final JsonArrayBuilder serviceRegistryArrayBuilder = Json.createArrayBuilder();
private final JsonArrayBuilder configMetadataArrayBuilder = Json.createArrayBuilder();
private final LinkedHashSet<String> serviceLoaderLines = new LinkedHashSet<>();
private boolean hasTransformedResource;

@Override
public boolean canTransformResource(String resource) {
return resource.equals(PATH);
return resource.equals(SERVICE_REGISTRY_PATH);
}

@Override
Expand All @@ -82,33 +92,65 @@ public void processResource(String resource, InputStream is, List<Relocator> rel

@Override
public boolean hasTransformedResource() {
return getResult().isEmpty();
return hasTransformedResource;
}

@Override
public void modifyOutputStream(JarOutputStream jarOutputStream) throws IOException {
JarEntry jarEntry = new JarEntry(PATH);
jarEntry.setTime(Long.MIN_VALUE);
jarOutputStream.putNextEntry(jarEntry);
Writer writer = new OutputStreamWriter(jarOutputStream, StandardCharsets.UTF_8);
Json.createWriter(writer).write(getResult());
writer.close();
writeJson(SERVICE_REGISTRY_PATH, serviceRegistryArrayBuilder.build(), jarOutputStream);
writeJson(CONFIG_METADATA_PATH, configMetadataArrayBuilder.build(), jarOutputStream);
writeLines(SERVICE_LOADER_PATH, serviceLoaderLines, jarOutputStream);
}

@Override
public void processResource(String resource, InputStream is, List<Relocator> relocators, long time) throws IOException {
JsonArray array = Json.createReader(is).readArray();
for (JsonValue value : array) {
jsonArrayBuilder.add(value);
if (SERVICE_REGISTRY_PATH.equals(resource)) {
JsonArray array = Json.createReader(is).readArray();
for (JsonValue value : array) {
serviceRegistryArrayBuilder.add(value);
}
}

if (CONFIG_METADATA_PATH.equals(resource)) {
JsonArray array = Json.createReader(is).readArray();
for (JsonValue value : array) {
configMetadataArrayBuilder.add(value);
}
}
}

private JsonArray getResult() {
if (result == null) {
result = jsonArrayBuilder.build();
if (SERVICE_LOADER_PATH.equals(resource)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, UTF_8))) {
reader.lines().forEach(serviceLoaderLines::add);
}
}
}

return result;
private void writeLines(String path, HashSet<String> lines, JarOutputStream jos) throws IOException {
if (lines.isEmpty()) {
return;
}
hasTransformedResource = true;
JarEntry entry = new JarEntry(path);
entry.setTime(Long.MIN_VALUE);
jos.putNextEntry(entry);
try (Writer writer = new OutputStreamWriter(jos, UTF_8)) {
for (String line : lines) {
writer.append(line);
writer.append('\n');
}
}
}

private void writeJson(String path, JsonArray jsonArray, JarOutputStream jos) throws IOException {
if (jsonArray.isEmpty()) {
return;
}
hasTransformedResource = true;
JarEntry entry = new JarEntry(path);
entry.setTime(Long.MIN_VALUE);
jos.putNextEntry(entry);
Writer writer = new OutputStreamWriter(jos, UTF_8);
Json.createWriter(writer).write(jsonArray);
writer.flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Maven Shade plugin custom transformer for merging Helidon service-registry, config-metadata and service.loader files.
*/
package io.helidon.shade.transformers;
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.shade.transformers;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;

import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonWriterFactory;
import jakarta.json.stream.JsonGenerator;
import org.junit.jupiter.api.Test;

import static io.helidon.shade.transformers.HelidonServiceTransformer.CONFIG_METADATA_PATH;
import static io.helidon.shade.transformers.HelidonServiceTransformer.SERVICE_LOADER_PATH;
import static io.helidon.shade.transformers.HelidonServiceTransformer.SERVICE_REGISTRY_PATH;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class HelidonServiceTransformerTest {

static JsonWriterFactory JSON_WRITER = Json.createWriterFactory(Map.of(JsonGenerator.PRETTY_PRINTING, false));

@Test
void testAll() throws IOException {
String jarName = "target/output.jar";
HelidonServiceTransformer transformer = new HelidonServiceTransformer();
transformer.processResource(SERVICE_REGISTRY_PATH,
ClassLoader.getSystemResourceAsStream("service-registry-1.json"),
null);
transformer.processResource(SERVICE_REGISTRY_PATH,
ClassLoader.getSystemResourceAsStream("service-registry-2.json"),
null);
transformer.processResource(CONFIG_METADATA_PATH,
ClassLoader.getSystemResourceAsStream("config-metadata-1.json"),
null);
transformer.processResource(CONFIG_METADATA_PATH,
ClassLoader.getSystemResourceAsStream("config-metadata-2.json"),
null);
transformer.processResource(SERVICE_LOADER_PATH,
ClassLoader.getSystemResourceAsStream("service.loader-1"),
null);
transformer.processResource(SERVICE_LOADER_PATH,
ClassLoader.getSystemResourceAsStream("service.loader-2"),
null);

JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarName));
transformer.modifyOutputStream(jarOutputStream);
jarOutputStream.close();

// Resulting service-registry.json
JsonArray svcRegArray = Json.createReader(new StringReader(readFromJar(jarName, SERVICE_REGISTRY_PATH))).readArray();
JsonObject svcRegObj1 = svcRegArray.getJsonObject(0);
JsonObject svcRegObj2 = svcRegArray.getJsonObject(1);

assertEqualJson(Json.createArrayBuilder().add(svcRegObj1).build(), "service-registry-1.json");
assertEqualJson(Json.createArrayBuilder().add(svcRegObj2).build(), "service-registry-2.json");

// Resulting config-metadata.json
JsonArray cfgMetaArray = Json.createReader(new StringReader(readFromJar(jarName, CONFIG_METADATA_PATH))).readArray();
JsonObject cfgMetaObj1 = cfgMetaArray.getJsonObject(0);
JsonObject cfgMetaObj2 = cfgMetaArray.getJsonObject(1);

assertEqualJson(Json.createArrayBuilder().add(cfgMetaObj1).build(), "config-metadata-1.json");
assertEqualJson(Json.createArrayBuilder().add(cfgMetaObj2).build(), "config-metadata-2.json");

// Resulting service.loader
String expected = new String(ClassLoader.getSystemResourceAsStream("service.loader-expected").readAllBytes());
String actual = readFromJar(jarName, SERVICE_LOADER_PATH);
assertEquals(expected, actual);
}

@Test
void testSingles() throws IOException {
String jarName = "target/output-singles.jar";
HelidonServiceTransformer transformer = new HelidonServiceTransformer();
transformer.processResource(SERVICE_REGISTRY_PATH,
ClassLoader.getSystemResourceAsStream("service-registry-1.json"),
null);
transformer.processResource(CONFIG_METADATA_PATH,
ClassLoader.getSystemResourceAsStream("config-metadata-1.json"),
null);
transformer.processResource(SERVICE_LOADER_PATH,
ClassLoader.getSystemResourceAsStream("service.loader-1"),
null);

JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarName));
transformer.modifyOutputStream(jarOutputStream);
jarOutputStream.close();

// Resulting service-registry.json
JsonArray svcRegArray = Json.createReader(new StringReader(readFromJar(jarName, SERVICE_REGISTRY_PATH))).readArray();

assertEqualJson(svcRegArray, "service-registry-1.json");

// Resulting config-metadata.json
JsonArray cfgMetaArray = Json.createReader(new StringReader(readFromJar(jarName, CONFIG_METADATA_PATH))).readArray();

assertEqualJson(cfgMetaArray, "config-metadata-1.json");

// Resulting service.loader
String expected = new String(ClassLoader.getSystemResourceAsStream("service.loader-1").readAllBytes());
String actual = readFromJar(jarName, SERVICE_LOADER_PATH);
assertEquals(expected, actual);
}

private void assertEqualJson(JsonArray actual, String expectedResourceName) throws IOException {
String expected = new String(ClassLoader.getSystemResourceAsStream(expectedResourceName).readAllBytes());
StringWriter actStringWriter = new StringWriter();
StringWriter expStringWriter = new StringWriter();
JSON_WRITER.createWriter(actStringWriter).writeArray(actual);
JSON_WRITER.createWriter(expStringWriter).writeArray(Json.createReader(new StringReader(expected)).readArray());
assertEquals(expStringWriter.toString(), actStringWriter.toString());
}

private String readFromJar(String jarPath, String fileName) throws IOException {
JarInputStream jis = new JarInputStream(new FileInputStream(jarPath));
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
if (fileName.equals(entry.getName())) {
break;
}
}
assertNotNull(entry);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int n;
while (-1 != (n = jis.read(buffer))) {
baos.write(buffer, 0, n);
}
return baos.toString(StandardCharsets.UTF_8);
}
}
Loading

0 comments on commit dd505a6

Please sign in to comment.