From 7f2c1440d1d2c2ea7b0867463aa71b0ed37d5e4b Mon Sep 17 00:00:00 2001 From: Daniel Kec Date: Mon, 26 Aug 2024 12:56:59 +0200 Subject: [PATCH 1/4] Helidon service Shade maven plugin transformer --- maven-plugins/pom.xml | 1 + maven-plugins/shade-extensions/README.md | 35 ++++++ maven-plugins/shade-extensions/pom.xml | 42 +++++++ .../HelidonServiceTransformer.java | 114 ++++++++++++++++++ pom.xml | 12 ++ 5 files changed, 204 insertions(+) create mode 100644 maven-plugins/shade-extensions/README.md create mode 100644 maven-plugins/shade-extensions/pom.xml create mode 100644 maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java diff --git a/maven-plugins/pom.xml b/maven-plugins/pom.xml index 44c9fb351..04777680c 100644 --- a/maven-plugins/pom.xml +++ b/maven-plugins/pom.xml @@ -42,5 +42,6 @@ sitegen-maven-plugin snakeyaml-codegen-maven-plugin stager-maven-plugin + shade-extensions diff --git a/maven-plugins/shade-extensions/README.md b/maven-plugins/shade-extensions/README.md new file mode 100644 index 000000000..af8e2b2ca --- /dev/null +++ b/maven-plugins/shade-extensions/README.md @@ -0,0 +1,35 @@ +# 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. + +### General usage + +```xml + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + + shade + + + + + + + + + + + + io.helidon.build-tools + helidon-shade-extensions + 4.0.0-SNAPSHOT + + + +``` \ No newline at end of file diff --git a/maven-plugins/shade-extensions/pom.xml b/maven-plugins/shade-extensions/pom.xml new file mode 100644 index 000000000..e015f48c2 --- /dev/null +++ b/maven-plugins/shade-extensions/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + io.helidon.build-tools + helidon-build-tools-project + 4.0.0-SNAPSHOT + ../../pom.xml + + helidon-shade-extensions + Helidon Shade Maven Plugin Extensions + + + + org.apache.maven.plugins + maven-shade-plugin + + + org.eclipse + yasson + + + diff --git a/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java new file mode 100644 index 000000000..1fdf0e9a8 --- /dev/null +++ b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java @@ -0,0 +1,114 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonValue; +import org.apache.maven.plugins.shade.relocation.Relocator; +import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer; + +/** + * Maven Shade plugin custom transformer for merging Helidon service-registry files. + * Usage: + *
{@code
+ * 
+ *    org.apache.maven.plugins
+ *    maven-shade-plugin
+ *    3.5.1
+ *    
+ *        
+ *            package
+ *            
+ *                shade
+ *            
+ *            
+ *                
+ *                    
+ *                    
+ *                
+ *            
+ *        
+ *    
+ *    
+ *        
+ *            io.helidon.build-tools
+ *            helidon-shade-extensions
+ *            4.0.0-SNAPSHOT
+ *        
+ *    
+ * 
+ * }
+ */ +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; + + @Override + public boolean canTransformResource(String resource) { + return resource.equals(PATH); + } + + @Override + public void processResource(String resource, InputStream is, List relocators) throws IOException { + processResource(resource, is, relocators, 0); + } + + @Override + public boolean hasTransformedResource() { + return getResult().isEmpty(); + } + + @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(); + } + + @Override + public void processResource(String resource, InputStream is, List relocators, long time) throws IOException { + JsonArray array = Json.createReader(is).readArray(); + for (JsonValue value : array) { + jsonArrayBuilder.add(value); + } + } + + private JsonArray getResult() { + if (result == null) { + result = jsonArrayBuilder.build(); + } + + return result; + } + +} diff --git a/pom.xml b/pom.xml index 358b100f9..56396e433 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,8 @@ 3.1.12 3.5.3 1.10.0 + 3.5.1 + 3.0.4 + + diff --git a/maven-plugins/shade-extensions/README.md b/maven-plugins/shade-extensions/README.md index af8e2b2ca..3bc375531 100644 --- a/maven-plugins/shade-extensions/README.md +++ b/maven-plugins/shade-extensions/README.md @@ -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 diff --git a/maven-plugins/shade-extensions/pom.xml b/maven-plugins/shade-extensions/pom.xml index e015f48c2..77bb5363b 100644 --- a/maven-plugins/shade-extensions/pom.xml +++ b/maven-plugins/shade-extensions/pom.xml @@ -35,8 +35,24 @@ maven-shade-plugin - org.eclipse - yasson + org.eclipse.parsson + parsson + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + org.hamcrest + hamcrest-all + test diff --git a/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java index 1fdf0e9a8..287766c43 100644 --- a/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java +++ b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/HelidonServiceTransformer.java @@ -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; @@ -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: - *
{@code
+ * 

  *    org.apache.maven.plugins
  *    maven-shade-plugin
@@ -62,17 +67,22 @@
  *        
  *    
  * 
- * }
+ * }]]
*/ 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 serviceLoaderLines = new LinkedHashSet<>(); + private boolean hasTransformedResource; @Override public boolean canTransformResource(String resource) { - return resource.equals(PATH); + return resource.equals(SERVICE_REGISTRY_PATH); } @Override @@ -82,33 +92,65 @@ public void processResource(String resource, InputStream is, List 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 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 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(); + } } diff --git a/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/package-info.java b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/package-info.java new file mode 100644 index 000000000..8c90644fb --- /dev/null +++ b/maven-plugins/shade-extensions/src/main/java/io/helidon/shade/transformers/package-info.java @@ -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; diff --git a/maven-plugins/shade-extensions/src/test/java/io/helidon/shade/transformers/HelidonServiceTransformerTest.java b/maven-plugins/shade-extensions/src/test/java/io/helidon/shade/transformers/HelidonServiceTransformerTest.java new file mode 100644 index 000000000..90130ac1a --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/java/io/helidon/shade/transformers/HelidonServiceTransformerTest.java @@ -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); + } +} diff --git a/maven-plugins/shade-extensions/src/test/resources/config-metadata-1.json b/maven-plugins/shade-extensions/src/test/resources/config-metadata-1.json new file mode 100644 index 000000000..9044d2a71 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/config-metadata-1.json @@ -0,0 +1,118 @@ +[ + { + "module": "io.helidon.scheduling", + "types": [ + { + "type": "io.helidon.scheduling.TaskConfig", + "annotatedType": "io.helidon.scheduling.TaskConfig", + "producers": [ + "io.helidon.scheduling.TaskConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.TaskConfig#builder()" + ], + "options": [] + }, + { + "type": "io.helidon.scheduling.Cron", + "annotatedType": "io.helidon.scheduling.CronConfig", + "inherits": [ + "io.helidon.scheduling.TaskConfig" + ], + "producers": [ + "io.helidon.scheduling.CronConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.CronConfig#builder()", + "io.helidon.scheduling.Cron#create(io.helidon.scheduling.CronConfig)" + ], + "options": [ + { + "key": "concurrent", + "type": "java.lang.Boolean", + "description": "Allow concurrent execution if previous task didn't finish before next execution.\n Default value is `true`.\n\nTrue for allow concurrent execution.", + "defaultValue": "true", + "method": "io.helidon.scheduling.CronConfig.Builder#concurrentExecution(boolean)" + }, + { + "key": "expression", + "description": "Cron expression for specifying period of execution.\n

\n Examples:\n

    \n
  • `0/2 * * * * ? *` - Every 2 seconds
  • \n
  • `0 45 9 ? * *` - Every day at 9:45
  • \n
  • `0 15 8 ? * MON-FRI` - Every workday at 8:15
  • \n
\n\nCron expression", + "required": true, + "method": "io.helidon.scheduling.CronConfig.Builder#expression(java.lang.String)" + } + ] + }, + { + "type": "io.helidon.scheduling.FixedRate", + "annotatedType": "io.helidon.scheduling.FixedRateConfig", + "inherits": [ + "io.helidon.scheduling.TaskConfig" + ], + "producers": [ + "io.helidon.scheduling.FixedRateConfig#create(io.helidon.common.config.Config)", + "io.helidon.scheduling.FixedRateConfig#builder()", + "io.helidon.scheduling.FixedRate#create(io.helidon.scheduling.FixedRateConfig)" + ], + "options": [ + { + "key": "delay", + "type": "java.lang.Long", + "description": "Fixed rate delay between each invocation. Time unit is by default java.util.concurrent.TimeUnit.SECONDS,\n can be specified with io.helidon.scheduling.FixedRateConfig.Builder.timeUnit(java.util.concurrent.TimeUnit).\n\nDelay between each invocation", + "required": true, + "method": "io.helidon.scheduling.FixedRateConfig.Builder#delay(long)" + }, + { + "key": "delay-type", + "type": "io.helidon.scheduling.FixedRate.DelayType", + "description": "Configure whether the delay between the invocations should be calculated from the time when previous task started or ended.\n Delay type is by default FixedRate.DelayType.SINCE_PREVIOUS_START.\n\nDelay type", + "defaultValue": "DelayType.SINCE_PREVIOUS_START", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#delayType(io.helidon.scheduling.FixedRate.DelayType)", + "allowedValues": [ + { + "value": "SINCE_PREVIOUS_START", + "description": "Next invocation delay is measured from the previous invocation task start." + }, + { + "value": "SINCE_PREVIOUS_END", + "description": "Next invocation delay is measured from the previous invocation task end." + } + ] + }, + { + "key": "time-unit", + "type": "java.util.concurrent.TimeUnit", + "description": "java.util.concurrent.TimeUnit TimeUnit used for interpretation of values provided with\n io.helidon.scheduling.FixedRateConfig.Builder.delay(long)\n and io.helidon.scheduling.FixedRateConfig.Builder.initialDelay(long).\n\nTime unit for interpreting values\n in io.helidon.scheduling.FixedRateConfig.Builder.delay(long)\n and io.helidon.scheduling.FixedRateConfig.Builder.initialDelay(long)", + "defaultValue": "TimeUnit.TimeUnit.SECONDS", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#timeUnit(java.util.concurrent.TimeUnit)", + "allowedValues": [ + { + "value": "NANOSECONDS" + }, + { + "value": "MICROSECONDS" + }, + { + "value": "MILLISECONDS" + }, + { + "value": "SECONDS" + }, + { + "value": "MINUTES" + }, + { + "value": "HOURS" + }, + { + "value": "DAYS" + } + ] + }, + { + "key": "initial-delay", + "type": "java.lang.Long", + "description": "Initial delay of the first invocation. Time unit is by default java.util.concurrent.TimeUnit.SECONDS,\n can be specified with\n io.helidon.scheduling.FixedRateConfig.Builder.timeUnit(java.util.concurrent.TimeUnit) timeUnit().\n\nInitial delay value", + "defaultValue": "0", + "method": "io.helidon.scheduling.FixedRateConfig.Builder#initialDelay(long)" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/maven-plugins/shade-extensions/src/test/resources/config-metadata-2.json b/maven-plugins/shade-extensions/src/test/resources/config-metadata-2.json new file mode 100644 index 000000000..946ed8d72 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/config-metadata-2.json @@ -0,0 +1,304 @@ +[ + { + "module": "io.helidon.webclient.api", + "types": [ + { + "type": "io.helidon.webclient.api.WebClient", + "annotatedType": "io.helidon.webclient.api.WebClientConfig", + "standalone": true, + "prefix": "clients", + "inherits": [ + "io.helidon.webclient.api.HttpClientConfig" + ], + "producers": [ + "io.helidon.webclient.api.WebClientConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.WebClientConfig#builder()", + "io.helidon.webclient.api.WebClient#create(io.helidon.webclient.api.WebClientConfig)" + ], + "options": [ + { + "key": "protocol-configs", + "type": "io.helidon.webclient.spi.ProtocolConfig", + "description": "Configuration of client protocols.\n\nClient protocol configurations", + "kind": "LIST", + "provider": true, + "providerType": "io.helidon.webclient.spi.ProtocolConfigProvider", + "method": "io.helidon.webclient.api.WebClientConfig.Builder#protocolConfigs(java.util.List)" + } + ] + }, + { + "type": "io.helidon.webclient.api.HttpClientConfig", + "annotatedType": "io.helidon.webclient.api.HttpClientConfig", + "inherits": [ + "io.helidon.webclient.api.HttpConfigBase" + ], + "producers": [ + "io.helidon.webclient.api.HttpClientConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.HttpClientConfig#builder()" + ], + "options": [ + { + "key": "relative-uris", + "type": "java.lang.Boolean", + "description": "Can be set to `true` to force the use of relative URIs in all requests,\n regardless of the presence or absence of proxies or no-proxy lists.\n\nRelative URIs flag", + "defaultValue": "false", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#relativeUris(boolean)" + }, + { + "key": "default-headers", + "description": "Default headers to be used in every request from configuration.\n\nDefault headers", + "kind": "MAP", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#defaultHeadersMap(java.util.Map)" + }, + { + "key": "content-encoding", + "type": "io.helidon.http.encoding.ContentEncodingContext", + "description": "Configure the listener specific io.helidon.http.encoding.ContentEncodingContext.\n This method discards all previously registered ContentEncodingContext.\n If no content encoding context is registered, default encoding context is used.\n\nContent encoding context", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#contentEncoding(io.helidon.http.encoding.ContentEncodingContext)" + }, + { + "key": "connection-cache-size", + "type": "java.lang.Integer", + "description": "Maximal size of the connection cache.\n For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2).\n This option limits the size. Setting this number lower than the \"usual\" number of target services will cause connections\n to be closed and reopened frequently.", + "defaultValue": "256", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#connectionCacheSize(int)" + }, + { + "key": "services", + "type": "io.helidon.webclient.spi.WebClientService", + "description": "WebClient services.\n\nServices to use with this web client", + "kind": "LIST", + "provider": true, + "providerType": "io.helidon.webclient.spi.WebClientServiceProvider", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#services(java.util.List)" + }, + { + "key": "media-context", + "type": "io.helidon.http.media.MediaContext", + "description": "Configure the listener specific io.helidon.http.media.MediaContext.\n This method discards all previously registered MediaContext.\n If no media context is registered, default media context is used.\n\nMedia context", + "defaultValue": "create()", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#mediaContext(io.helidon.http.media.MediaContext)" + }, + { + "key": "cookie-manager", + "type": "io.helidon.webclient.api.WebClientCookieManager", + "description": "WebClient cookie manager.\n\nCookie manager to use", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#cookieManager(java.util.Optional)" + }, + { + "key": "max-in-memory-entity", + "type": "java.lang.Integer", + "description": "If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance.\n If bigger, streaming will be used.\n

\n Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such\n cases, this option is ignored. Default is 128Kb.\n\nMaximal number of bytes to buffer in memory for supported writers", + "defaultValue": "131072", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#maxInMemoryEntity(int)" + }, + { + "key": "send-expect-continue", + "type": "java.lang.Boolean", + "description": "Whether Expect-100-Continue header is sent to verify server availability before sending an entity.\n

\n Defaults to `true`.\n

\n\nWhether Expect:100-Continue header should be sent on streamed transfers", + "defaultValue": "true", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#sendExpectContinue(boolean)" + }, + { + "key": "socket-options", + "type": "io.helidon.common.socket.SocketOptions", + "description": "Socket options for connections opened by this client.\n If there is a value explicitly configured on this type and on the socket options,\n the one configured on this type's builder will win:\n
    \n
  • readTimeout()
  • \n
  • connectTimeout()
  • \n
\n\nSocket options", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#socketOptions(io.helidon.common.socket.SocketOptions)" + }, + { + "key": "share-connection-cache", + "type": "java.lang.Boolean", + "description": "Whether to share connection cache between all the WebClient instances in JVM.\n\nTrue if connection cache is shared", + "defaultValue": "true", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#shareConnectionCache(boolean)" + }, + { + "key": "media-type-parser-mode", + "type": "io.helidon.common.media.type.ParserMode", + "description": "Configure media type parsing mode for HTTP `Content-Type` header.\n\nMedia type parsing mode", + "defaultValue": "ParserMode.STRICT", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#mediaTypeParserMode(io.helidon.common.media.type.ParserMode)", + "allowedValues": [ + { + "value": "STRICT" + }, + { + "value": "RELAXED" + } + ] + }, + { + "key": "base-uri", + "type": "io.helidon.webclient.api.ClientUri", + "description": "Base uri used by the client in all requests.\n\nBase uri of the client requests", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#baseUri(java.util.Optional)" + }, + { + "key": "read-continue-timeout", + "type": "java.time.Duration", + "description": "Socket 100-Continue read timeout. Default is 1 second.\n This read timeout is used when 100-Continue is sent by the client, before it sends an entity.\n\nRead 100-Continue timeout duration", + "defaultValue": "PT1S", + "method": "io.helidon.webclient.api.HttpClientConfig.Builder#readContinueTimeout(java.time.Duration)" + } + ] + }, + { + "type": "io.helidon.webclient.api.WebClientCookieManager", + "annotatedType": "io.helidon.webclient.api.WebClientCookieManagerConfig", + "producers": [ + "io.helidon.webclient.api.WebClientCookieManagerConfig#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.WebClientCookieManagerConfig#builder()", + "io.helidon.webclient.api.WebClientCookieManager#create(io.helidon.webclient.api.WebClientCookieManagerConfig)" + ], + "options": [ + { + "key": "automatic-store-enabled", + "type": "java.lang.Boolean", + "description": "Whether automatic cookie store is enabled or not.\n\nStatus of cookie store", + "defaultValue": "false", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#automaticStoreEnabled(boolean)" + }, + { + "key": "cookie-policy", + "type": "java.net.CookiePolicy", + "description": "Current cookie policy for this client.\n\nThe cookie policy", + "defaultValue": "java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#cookiePolicy(java.net.CookiePolicy)" + }, + { + "key": "default-cookies", + "description": "Map of default cookies to include in all requests if cookies enabled.\n\nMap of default cookies", + "kind": "MAP", + "method": "io.helidon.webclient.api.WebClientCookieManagerConfig.Builder#defaultCookies(java.util.Map)" + } + ] + }, + { + "type": "io.helidon.webclient.api.HttpConfigBase", + "annotatedType": "io.helidon.webclient.api.HttpConfigBase", + "producers": [ + "io.helidon.webclient.api.HttpConfigBase#create(io.helidon.common.config.Config)", + "io.helidon.webclient.api.HttpConfigBase#builder()" + ], + "options": [ + { + "key": "read-timeout", + "type": "java.time.Duration", + "description": "Read timeout.\n\nRead timeout\n See io.helidon.common.socket.SocketOptions.readTimeout()", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#readTimeout(java.util.Optional)" + }, + { + "key": "keep-alive", + "type": "java.lang.Boolean", + "description": "Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use\n the same connection for multiple requests).\n\nKeep alive for this connection\n See io.helidon.common.socket.SocketOptions.socketKeepAlive()", + "defaultValue": "true", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#keepAlive(boolean)" + }, + { + "key": "proxy", + "type": "io.helidon.webclient.api.Proxy", + "description": "Proxy configuration to be used for requests.\n\nProxy to use, defaults to Proxy.noProxy()", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#proxy(io.helidon.webclient.api.Proxy)" + }, + { + "key": "follow-redirects", + "type": "java.lang.Boolean", + "description": "Whether to follow redirects.\n\nWhether to follow redirects", + "defaultValue": "true", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#followRedirects(boolean)" + }, + { + "key": "connect-timeout", + "type": "java.time.Duration", + "description": "Connect timeout.\n\nConnect timeout\n See io.helidon.common.socket.SocketOptions.connectTimeout()", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#connectTimeout(java.util.Optional)" + }, + { + "key": "max-redirects", + "type": "java.lang.Integer", + "description": "Max number of followed redirects.\n This is ignored if followRedirects() option is `false`.\n\nMax number of followed redirects", + "defaultValue": "10", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#maxRedirects(int)" + }, + { + "key": "tls", + "type": "io.helidon.common.tls.Tls", + "description": "TLS configuration for any TLS request from this client.\n TLS can also be configured per request.\n TLS is used when the protocol is set to `https`.\n\nTLS configuration to use", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#tls(io.helidon.common.tls.Tls)" + }, + { + "key": "properties", + "description": "Properties configured for this client. These properties are propagated through client request, to be used by\n services (and possibly for other purposes).\n\nMap of client properties", + "kind": "MAP", + "method": "io.helidon.webclient.api.HttpConfigBase.Builder#properties(java.util.Map)" + } + ] + }, + { + "type": "io.helidon.webclient.api.Proxy", + "annotatedType": "io.helidon.webclient.api.Proxy.Builder", + "producers": [ + "io.helidon.webclient.api.Proxy.Builder#build()", + "io.helidon.webclient.api.Proxy#create(io.helidon.common.config.Config)" + ], + "options": [ + { + "key": "password", + "description": "Sets a new password for the proxy.", + "method": "io.helidon.webclient.api.Proxy.Builder#password(char[])" + }, + { + "key": "port", + "type": "java.lang.Integer", + "description": "Sets a port value.", + "method": "io.helidon.webclient.api.Proxy.Builder#port(int)" + }, + { + "key": "host", + "description": "Sets a new host value.", + "method": "io.helidon.webclient.api.Proxy.Builder#host(java.lang.String)" + }, + { + "key": "force-http-connect", + "type": "java.lang.Boolean", + "description": "Forces HTTP CONNECT with the proxy server.\n Otherwise it will not execute HTTP CONNECT when the request is\n plain HTTP with no authentication.", + "method": "io.helidon.webclient.api.Proxy.Builder#forceHttpConnect(boolean)" + }, + { + "key": "username", + "description": "Sets a new username for the proxy.", + "method": "io.helidon.webclient.api.Proxy.Builder#username(java.lang.String)" + }, + { + "key": "type", + "type": "io.helidon.webclient.api.Proxy.ProxyType", + "description": "Sets a new proxy type.", + "defaultValue": "HTTP", + "method": "io.helidon.webclient.api.Proxy.Builder#type(io.helidon.webclient.api.Proxy.ProxyType)", + "allowedValues": [ + { + "value": "NONE", + "description": "No proxy." + }, + { + "value": "SYSTEM", + "description": "Proxy obtained from system." + }, + { + "value": "HTTP", + "description": "HTTP proxy." + } + ] + }, + { + "key": "no-proxy", + "description": "Configure a host pattern that is not going through a proxy.\n

\n Options are:\n

    \n
  • IP Address, such as `192.168.1.1`
  • \n
  • IP V6 Address, such as `[2001:db8:85a3:8d3:1319:8a2e:370:7348]`
  • \n
  • Hostname, such as `localhost`
  • \n
  • Domain name, such as `helidon.io`
  • \n
  • Domain name and all sub-domains, such as `.helidon.io` (leading dot)
  • \n
  • Combination of all options from above with a port, such as `.helidon.io:80`
  • \n
", + "kind": "LIST", + "method": "io.helidon.webclient.api.Proxy.Builder#addNoProxy(java.lang.String)" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/maven-plugins/shade-extensions/src/test/resources/service-registry-1.json b/maven-plugins/shade-extensions/src/test/resources/service-registry-1.json new file mode 100644 index 000000000..f0dbbbf26 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/service-registry-1.json @@ -0,0 +1,20 @@ +[ + { + "module": "io.helidon.config", + "services": [ + { + "descriptor": "io.helidon.config.ConfigProvider__ServiceDescriptor", + "contracts": [ + "io.helidon.common.config.Config", + "io.helidon.config.ConfigProvider" + ] + }, + { + "descriptor": "io.helidon.config.MetaConfig__ServiceDescriptor", + "contracts": [ + "io.helidon.config.MetaConfig" + ] + } + ] + } +] \ No newline at end of file diff --git a/maven-plugins/shade-extensions/src/test/resources/service-registry-2.json b/maven-plugins/shade-extensions/src/test/resources/service-registry-2.json new file mode 100644 index 000000000..c1b36f696 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/service-registry-2.json @@ -0,0 +1,25 @@ +[ + { + "module": "io.helidon.integrations.oci.authentication.instance", + "services": [ + { + "weight": 60.0, + "descriptor": "io.helidon.integrations.oci.authentication.instance.AuthenticationMethodInstancePrincipal__ServiceDescriptor", + "contracts": [ + "io.helidon.integrations.oci.authentication.instance.AuthenticationMethodInstancePrincipal", + "io.helidon.integrations.oci.spi.OciAuthenticationMethod" + ] + }, + { + "descriptor": "io.helidon.integrations.oci.authentication.instance.InstancePrincipalBuilderProvider__ServiceDescriptor", + "contracts": [ + "com.oracle.bmc.auth.AbstractFederationClientAuthenticationDetailsProviderBuilder", + "com.oracle.bmc.auth.AbstractRequestingAuthenticationDetailsProvider.Builder", + "com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider.InstancePrincipalsAuthenticationDetailsProviderBuilder", + "io.helidon.integrations.oci.authentication.instance.InstancePrincipalBuilderProvider", + "java.util.function.Supplier" + ] + } + ] + } +] \ No newline at end of file diff --git a/maven-plugins/shade-extensions/src/test/resources/service.loader-1 b/maven-plugins/shade-extensions/src/test/resources/service.loader-1 new file mode 100644 index 000000000..ee833c1c8 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/service.loader-1 @@ -0,0 +1,3 @@ +# List of service contracts we want to support either from service registry, or from service loader +io.helidon.config.spi.ConfigParser +io.helidon.config.spi.ConfigFilter diff --git a/maven-plugins/shade-extensions/src/test/resources/service.loader-2 b/maven-plugins/shade-extensions/src/test/resources/service.loader-2 new file mode 100644 index 000000000..bb56b208d --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/service.loader-2 @@ -0,0 +1,2 @@ +# List of service contracts we want to support either from service registry, or from service loader +io.helidon.config.spi.ConfigMapperProvider \ No newline at end of file diff --git a/maven-plugins/shade-extensions/src/test/resources/service.loader-expected b/maven-plugins/shade-extensions/src/test/resources/service.loader-expected new file mode 100644 index 000000000..d07172ed9 --- /dev/null +++ b/maven-plugins/shade-extensions/src/test/resources/service.loader-expected @@ -0,0 +1,4 @@ +# List of service contracts we want to support either from service registry, or from service loader +io.helidon.config.spi.ConfigParser +io.helidon.config.spi.ConfigFilter +io.helidon.config.spi.ConfigMapperProvider diff --git a/pom.xml b/pom.xml index 56396e433..0030c13ae 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ 3.5.3 1.10.0 3.5.1 - 3.0.4 + 1.1.7