diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProvider.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProvider.java index 900205933515..3c3e4f83fada 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProvider.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProvider.java @@ -107,4 +107,8 @@ public String getAuthSchemeInternalDirectory() { public String getJmesPathInternalDirectory() { return sourceDirectory + "/" + Utils.packageToDirectory(model.getMetadata().getFullInternalJmesPathPackageName()); } + + public String getWarmUpProviderDirectory() { + return sourceDirectory + "/" + Utils.packageToDirectory(model.getMetadata().getFullCracInternalPackageName()); + } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTask.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTask.java new file mode 100644 index 000000000000..9185048cd1ce --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTask.java @@ -0,0 +1,72 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.emitters; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; + +/** + * Appends a generated {@code SdkWarmUpProvider} implementation's fully-qualified class name to the shared + * {@code META-INF/services/software.amazon.awssdk.core.crac.SdkWarmUpProvider} resource file. + * + *

This task writes a plain resource file rather than a {@code .java} file, so it does not use + * {@link SimpleGeneratorTask}. It creates the file once and appends to it for each subsequent service, and + * re-registering the same class name is a no-op. + */ +public final class WarmUpProviderRegistrationTask extends GeneratorTask { + + private static final String SPI_RESOURCE_PATH = + "META-INF/services/software.amazon.awssdk.core.crac.SdkWarmUpProvider"; + + private final String resourcesDirectory; + private final String providerClassName; + + public WarmUpProviderRegistrationTask(String resourcesDirectory, String providerClassName) { + this.resourcesDirectory = resourcesDirectory; + this.providerClassName = providerClassName; + } + + @Override + public void compute() { + try { + Path file = Paths.get(resourcesDirectory, SPI_RESOURCE_PATH); + Path parent = file.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + + if (Files.exists(file)) { + List lines = Files.readAllLines(file, StandardCharsets.UTF_8); + if (lines.contains(providerClassName)) { + return; + } + } + + Files.write(file, + (providerClassName + "\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException e) { + throw new UncheckedIOException("Failed to register warm up provider " + providerClassName, e); + } + } +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AwsGeneratorTasks.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AwsGeneratorTasks.java index a6b3b75e4fcc..888a7cbce01b 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AwsGeneratorTasks.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/AwsGeneratorTasks.java @@ -28,6 +28,7 @@ public AwsGeneratorTasks(GeneratorTaskParams params) { new EventStreamGeneratorTasks(params), new WaitersGeneratorTasks(params), new EndpointProviderTasks(params), - new AuthSchemeGeneratorTasks(params)); + new AuthSchemeGeneratorTasks(params), + new WarmUpProviderTasks(params)); } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/WarmUpProviderTasks.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/WarmUpProviderTasks.java new file mode 100644 index 000000000000..bc947d4e0ee7 --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/WarmUpProviderTasks.java @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.emitters.tasks; + +import java.util.ArrayList; +import java.util.List; +import software.amazon.awssdk.codegen.emitters.GeneratorTask; +import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams; +import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask; +import software.amazon.awssdk.codegen.emitters.WarmUpProviderRegistrationTask; +import software.amazon.awssdk.codegen.poet.crac.WarmUpProviderSpec; + +/** + * Emits the per-service {@code SdkWarmUpProvider} implementation and its {@code META-INF/services} registration. + */ +public final class WarmUpProviderTasks extends BaseGeneratorTasks { + + private final GeneratorTaskParams params; + + public WarmUpProviderTasks(GeneratorTaskParams params) { + super(params); + this.params = params; + } + + @Override + protected List createTasks() throws Exception { + List tasks = new ArrayList<>(); + + WarmUpProviderSpec spec = new WarmUpProviderSpec(model); + + tasks.add(new PoetGeneratorTask(warmUpProviderDir(), model.getFileHeader(), spec)); + tasks.add(new WarmUpProviderRegistrationTask(params.getPathProvider().getResourcesDirectory(), + spec.className().toString())); + return tasks; + } + + private String warmUpProviderDir() { + return params.getPathProvider().getWarmUpProviderDirectory(); + } +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java index 4eb10f6bf105..402febeb86ad 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java @@ -775,6 +775,10 @@ public String getFullInternalPackageName() { return joinPackageNames(getFullClientPackageName(), "internal"); } + public String getFullCracInternalPackageName() { + return joinPackageNames(getFullInternalPackageName(), "crac"); + } + public Metadata setJmesPathPackageName(String jmesPathPackageName) { this.jmesPathPackageName = jmesPathPackageName; return this; diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpec.java new file mode 100644 index 000000000000..482164c30cc0 --- /dev/null +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpec.java @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.poet.crac; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import javax.lang.model.element.Modifier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; +import software.amazon.awssdk.codegen.poet.ClassSpec; +import software.amazon.awssdk.codegen.poet.PoetUtils; +import software.amazon.awssdk.core.crac.SdkWarmUpProvider; + +/** + * Generates an empty {@link SdkWarmUpProvider} implementation per service. The {@code warmUp()} body is intentionally a + * no-op in this stage; the synthetic priming call is added in a later stage. ServiceLoader requires a public no-arg + * constructor, which the default constructor satisfies. + */ +public class WarmUpProviderSpec implements ClassSpec { + + private final IntermediateModel model; + + public WarmUpProviderSpec(IntermediateModel model) { + this.model = model; + } + + @Override + public ClassName className() { + return ClassName.get(model.getMetadata().getFullCracInternalPackageName(), + model.getMetadata().getServiceName() + "WarmUpProvider"); + } + + @Override + public TypeSpec poetSpec() { + return PoetUtils.createClassBuilder(className()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addAnnotation(SdkInternalApi.class) + .addSuperinterface(SdkWarmUpProvider.class) + .addMethod(warmUpMethod()) + .build(); + } + + private MethodSpec warmUpMethod() { + return MethodSpec.methodBuilder("warmUp") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .build(); + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProviderTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProviderTest.java new file mode 100644 index 000000000000..0a495d695cd7 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/GeneratorPathProviderTest.java @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.emitters; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; +import software.amazon.awssdk.codegen.model.intermediate.Metadata; + +public class GeneratorPathProviderTest { + + @Test + public void warmUpProviderDirectory_isUnderSourceDirInCracInternalPackage() { + Metadata metadata = new Metadata(); + metadata.setRootPackageName("software.amazon.awssdk.services"); + metadata.setClientPackageName("query"); + + IntermediateModel model = new IntermediateModel(); + model.setMetadata(metadata); + + GeneratorPathProvider provider = + new GeneratorPathProvider(model, "/src", "/test", "/resources"); + + assertThat(provider.getWarmUpProviderDirectory()) + .isEqualTo("/src/software/amazon/awssdk/services/query/internal/crac"); + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTaskTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTaskTest.java new file mode 100644 index 000000000000..75cce7e5af4f --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/emitters/WarmUpProviderRegistrationTaskTest.java @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.emitters; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.codegen.poet.ClientTestModels.queryServiceModels; +import static software.amazon.awssdk.codegen.poet.ClientTestModels.xmlServiceModels; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import software.amazon.awssdk.codegen.poet.crac.WarmUpProviderSpec; + +public class WarmUpProviderRegistrationTaskTest { + + private static final String SPI_PATH = + "META-INF/services/software.amazon.awssdk.core.crac.SdkWarmUpProvider"; + + private static final String QUERY_PROVIDER = new WarmUpProviderSpec(queryServiceModels()).className().toString(); + private static final String XML_PROVIDER = new WarmUpProviderSpec(xmlServiceModels()).className().toString(); + + @TempDir + Path resourcesDir; + + @Test + public void writesProviderClassName() throws IOException { + new WarmUpProviderRegistrationTask(resourcesDir.toString(), QUERY_PROVIDER).compute(); + + List lines = Files.readAllLines(resourcesDir.resolve(SPI_PATH), StandardCharsets.UTF_8); + assertThat(lines).contains(QUERY_PROVIDER); + } + + @Test + public void appendsSecondProviderWithoutClobbering() throws IOException { + new WarmUpProviderRegistrationTask(resourcesDir.toString(), QUERY_PROVIDER).compute(); + new WarmUpProviderRegistrationTask(resourcesDir.toString(), XML_PROVIDER).compute(); + + List lines = Files.readAllLines(resourcesDir.resolve(SPI_PATH), StandardCharsets.UTF_8); + assertThat(lines).contains(QUERY_PROVIDER).contains(XML_PROVIDER); + } + + @Test + public void doesNotDuplicateOnSecondIdenticalRun() throws IOException { + new WarmUpProviderRegistrationTask(resourcesDir.toString(), QUERY_PROVIDER).compute(); + new WarmUpProviderRegistrationTask(resourcesDir.toString(), QUERY_PROVIDER).compute(); + + List lines = Files.readAllLines(resourcesDir.resolve(SPI_PATH), StandardCharsets.UTF_8); + long count = lines.stream().filter(l -> l.equals(QUERY_PROVIDER)).count(); + assertThat(count).isEqualTo(1); + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MetadataTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MetadataTest.java new file mode 100644 index 000000000000..9541a3fc9386 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/model/intermediate/MetadataTest.java @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.model.intermediate; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +public class MetadataTest { + + @Test + public void fullCracInternalPackageName_appendsInternalCracToClientPackage() { + Metadata metadata = new Metadata(); + metadata.setRootPackageName("software.amazon.awssdk.services"); + metadata.setClientPackageName("query"); + + assertThat(metadata.getFullCracInternalPackageName()) + .isEqualTo("software.amazon.awssdk.services.query.internal.crac"); + } +} diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpecTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpecTest.java new file mode 100644 index 000000000000..2a69773e2a34 --- /dev/null +++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/crac/WarmUpProviderSpecTest.java @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.codegen.poet.crac; + +import static org.hamcrest.MatcherAssert.assertThat; +import static software.amazon.awssdk.codegen.poet.PoetMatchers.generatesTo; + +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.codegen.poet.ClassSpec; +import software.amazon.awssdk.codegen.poet.ClientTestModels; + +public class WarmUpProviderSpecTest { + + @Test + public void warmUpProvider() { + ClassSpec spec = new WarmUpProviderSpec(ClientTestModels.queryServiceModels()); + assertThat(spec, generatesTo("warmup-provider.java")); + } +} diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/crac/warmup-provider.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/crac/warmup-provider.java new file mode 100644 index 000000000000..9b92d51d2d71 --- /dev/null +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/crac/warmup-provider.java @@ -0,0 +1,13 @@ +package software.amazon.awssdk.services.query.internal.crac; + +import software.amazon.awssdk.annotations.Generated; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.crac.SdkWarmUpProvider; + +@Generated("software.amazon.awssdk:codegen") +@SdkInternalApi +public final class QueryWarmUpProvider implements SdkWarmUpProvider { + @Override + public void warmUp() { + } +}