diff --git a/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/another/reachability-metadata.json b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/another/reachability-metadata.json new file mode 100644 index 000000000..df490f246 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/another/reachability-metadata.json @@ -0,0 +1,15 @@ +{ + "reflection" : [ { + "type" : "reachabilitymetadata.com.palantir.another.TestEmptyService", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.another.TestService", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + } ] +} \ No newline at end of file diff --git a/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/datasets/reachability-metadata.json b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/datasets/reachability-metadata.json new file mode 100644 index 000000000..6bb683c21 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/datasets/reachability-metadata.json @@ -0,0 +1,15 @@ +{ + "reflection" : [ { + "type" : "reachabilitymetadata.com.palantir.product.datasets.BackingFileSystem", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.datasets.Dataset", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + } ] +} \ No newline at end of file diff --git a/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/reachability-metadata.json b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/reachability-metadata.json new file mode 100644 index 000000000..5ac8e84dc --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/reachabilitymetadata/com/palantir/product/reachability-metadata.json @@ -0,0 +1,453 @@ +{ + "reflection" : [ { + "type" : "reachabilitymetadata.com.palantir.product.AliasAsMapKeyExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.AliasedBinary", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.AliasedString", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.AnyExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.AnyMapExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BearerTokenAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BearerTokenExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BinaryAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BinaryAliasOne", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BinaryAliasTwo", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BinaryExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BooleanAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.BooleanExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.CovariantListExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.CovariantOptionalExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.CreateDatasetRequest", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.DateTimeAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.DateTimeExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.DoubleAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.DoubleExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.EmptyObjectExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.EmptyUnionTypeExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.EnumExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.EnumFieldExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.EnumNameTestExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalLongAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalLongAliasOne", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalLongAliasTwo", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalLongExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalLongUnionExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ExternalStringAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.IntegerAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.IntegerExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.LargeEnumExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.LargeUnionExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ListAlias", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ListExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ManyFieldExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.MapAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.MapExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.NestedAliasedBinary", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.NestedOptionalSetExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.NestedStringAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalAlias", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalDoubleAlias", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalListAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalMapAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.OptionalSetAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.PrimitiveOptionalsExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ReferenceAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.ReservedKeyExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.RidAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.RidExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.RiskyNames", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SafeDoubleAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SafeExternalLongAlias", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SafeExternalLongExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SafeLongAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SafeLongExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SetAlias", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SetExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SimpleEnum", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.SingleUnion", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.StringAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.StringAliasOne", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.StringAliasThree", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.StringAliasTwo", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.StringExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.Union", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.UnionTypeExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.UnionWithUnknownString", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.UuidAliasExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + }, { + "type" : "reachabilitymetadata.com.palantir.product.UuidExample", + "allDeclaredMethods" : true, + "allPublicMethods" : true, + "allDeclaredConstructors" : true, + "allPublicConstructors" : true + } ] +} \ No newline at end of file diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/GeneratedFile.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/GeneratedFile.java new file mode 100644 index 000000000..bb7432866 --- /dev/null +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/GeneratedFile.java @@ -0,0 +1,33 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. 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. + * 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 com.palantir.conjure.java; + +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedReachabilityMetadataFile; +import com.palantir.conjure.java.types.ReachabilityMetadata; +import com.palantir.javapoet.JavaFile; + +public sealed interface GeneratedFile permits GeneratedJavaFile, GeneratedReachabilityMetadataFile { + record GeneratedJavaFile(JavaFile javaFile) implements GeneratedFile { + public static GeneratedJavaFile of(JavaFile javaFile) { + return new GeneratedJavaFile(javaFile); + } + } + + record GeneratedReachabilityMetadataFile(String packageName, ReachabilityMetadata reachabilityMetadata) + implements GeneratedFile {} +} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/GenerationCoordinator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/GenerationCoordinator.java index 91eaa5304..761c2cd72 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/GenerationCoordinator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/GenerationCoordinator.java @@ -16,18 +16,26 @@ package com.palantir.conjure.java; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Splitter; import com.palantir.common.streams.MoreStreams; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedReachabilityMetadataFile; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.goethe.Goethe; +import com.palantir.logsafe.SafeArg; +import com.palantir.logsafe.exceptions.SafeRuntimeException; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Collectors; -public class GenerationCoordinator { - +public final class GenerationCoordinator { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Executor executor; private final Set generators; private final Options options; @@ -50,9 +58,65 @@ public List emit(ConjureDefinition conjureDefinition, File outputDir) { ConjureDefinition definition = new ExternalImportFilter(options).filter(conjureDefinition); return MoreStreams.inCompletionOrder( generators.stream().flatMap(generator -> generator.generate(definition)), - f -> Goethe.formatAndEmit(f, outputDir.toPath()), + f -> formatAndEmit(f, outputDir.toPath()), executor, Runtime.getRuntime().availableProcessors()) .collect(Collectors.toList()); } + + public List emitForTests(ConjureDefinition conjureDefinition, File outputDir) { + ConjureDefinition definition = new ExternalImportFilter(options).filter(conjureDefinition); + return MoreStreams.inCompletionOrder( + generators.stream().flatMap(generator -> generator.generate(definition)), + f -> formatAndEmitForTests(f, outputDir.toPath()), + executor, + Runtime.getRuntime().availableProcessors()) + .collect(Collectors.toList()); + } + + private static Path formatAndEmit(GeneratedFile generatedFile, Path outputDirectoryPath) { + if (generatedFile instanceof GeneratedJavaFile generatedJavaFile) { + return Goethe.formatAndEmit(generatedJavaFile.javaFile(), outputDirectoryPath); + } else if (generatedFile instanceof GeneratedReachabilityMetadataFile generatedReachabilityMetadataFile) { + try { + return generateReachabilityMetadata( + outputDirectoryPath.resolve("META-INF").resolve("native-image"), + generatedReachabilityMetadataFile); + } catch (IOException e) { + throw new SafeRuntimeException("Failed to emit reachability metadata file", e); + } + } else { + throw new SafeRuntimeException( + "Unknown generated file type", SafeArg.of("class", generatedFile.getClass())); + } + } + + private static Path formatAndEmitForTests(GeneratedFile generatedFile, Path outputDirectoryPath) { + if (generatedFile instanceof GeneratedJavaFile generatedJavaFile) { + return Goethe.formatAndEmit(generatedJavaFile.javaFile(), outputDirectoryPath); + } else if (generatedFile instanceof GeneratedReachabilityMetadataFile generatedReachabilityMetadataFile) { + try { + return generateReachabilityMetadata(outputDirectoryPath, generatedReachabilityMetadataFile); + } catch (IOException e) { + throw new SafeRuntimeException("Failed to emit reachability metadata file", e); + } + } else { + throw new SafeRuntimeException( + "Unknown generated file type", SafeArg.of("class", generatedFile.getClass())); + } + } + + private static Path generateReachabilityMetadata( + Path outputDir, GeneratedReachabilityMetadataFile generatedReachabilityMetadataFile) throws IOException { + Path outputDirectory = outputDir; + for (String packageComponent : Splitter.on(".").split(generatedReachabilityMetadataFile.packageName())) { + outputDirectory = outputDirectory.resolve(packageComponent); + } + Files.createDirectories(outputDirectory); + Path output = outputDirectory.resolve("reachability-metadata.json"); + OBJECT_MAPPER + .writerWithDefaultPrettyPrinter() + .writeValue(output.toFile(), generatedReachabilityMetadataFile.reachabilityMetadata()); + return output; + } } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/Generator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Generator.java index d0732d7df..ea39d5290 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/Generator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/Generator.java @@ -17,9 +17,8 @@ package com.palantir.conjure.java; import com.palantir.conjure.spec.ConjureDefinition; -import com.palantir.javapoet.JavaFile; import java.util.stream.Stream; public interface Generator { - Stream generate(ConjureDefinition definition); + Stream generate(ConjureDefinition definition); } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java index ec7ab6183..c6c83796a 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/Options.java @@ -202,6 +202,15 @@ default boolean generateDialogueEndpointErrorResultTypes() { return false; } + /** + * If set to true, generates reachability-metadata.json files for all types in the Conjure definition. These are + * needed by Graal's native-image feature to support reflection. + */ + @Value.Default + default boolean generateReachabilityMetadata() { + return false; + } + Optional packagePrefix(); Optional apiVersion(); diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java index 82bb3fc2f..7d577220a 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/JerseyServiceGenerator.java @@ -20,6 +20,8 @@ import com.palantir.conjure.java.ConjureAnnotations; import com.palantir.conjure.java.ConjureMarkers; import com.palantir.conjure.java.ConjureTags; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.services.ServiceGenerators.EndpointErrorsJavaDoc; @@ -86,7 +88,7 @@ public JerseyServiceGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition conjureDefinition) { + public Stream generate(ConjureDefinition conjureDefinition) { ClassName binaryReturnType = options.jerseyBinaryAsResponse() ? binaryReturnTypeResponse : binaryReturnTypeOutput; @@ -105,7 +107,8 @@ public Stream generate(ConjureDefinition conjureDefinition) { types, new SpecializeBinaryClassNameVisitor(defaultVisitor, types, BINARY_ARGUMENT_TYPE)); return conjureDefinition.getServices().stream() - .map(serviceDef -> generateService(serviceDef, safetyEvaluator, returnTypeMapper, argumentTypeMapper)); + .map(serviceDef -> generateService(serviceDef, safetyEvaluator, returnTypeMapper, argumentTypeMapper)) + .map(GeneratedJavaFile::of); } private static boolean hasEndpointErrorsDefined(ServiceDefinition serviceDefinition) { diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceGenerator.java index c3fd39685..a795ef897 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/UndertowServiceGenerator.java @@ -16,6 +16,8 @@ package com.palantir.conjure.java.services; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.types.ClassNameVisitor; @@ -29,7 +31,6 @@ import com.palantir.conjure.spec.TypeDefinition; import com.palantir.conjure.spec.TypeName; import com.palantir.javapoet.ClassName; -import com.palantir.javapoet.JavaFile; import java.io.InputStream; import java.util.Map; import java.util.stream.Stream; @@ -43,7 +44,7 @@ public UndertowServiceGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition conjureDefinition) { + public Stream generate(ConjureDefinition conjureDefinition) { Map types = TypeFunctions.toTypesMap(conjureDefinition); ClassNameVisitor defaultVisitor = new DefaultClassNameVisitor(types.keySet(), options); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); @@ -61,6 +62,7 @@ public Stream generate(ConjureDefinition conjureDefinition) { interfaceGenerator.generateServiceInterface( serviceDef, safetyEvaluator, typeMapper, returnTypeMapper), handlerGenerator.generateServiceHandler( - serviceDef, types, typeMapper, returnTypeMapper, safetyEvaluator))); + serviceDef, types, typeMapper, returnTypeMapper, safetyEvaluator))) + .map(GeneratedJavaFile::of); } } diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java index 64e693107..583e5a0ca 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/services/dialogue/DialogueServiceGenerator.java @@ -16,9 +16,13 @@ package com.palantir.conjure.java.services.dialogue; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.types.DefaultClassNameVisitor; +import com.palantir.conjure.java.types.ReachabilityMetadataGenerator; +import com.palantir.conjure.java.types.ReachabilityMetadataGenerator.GenerationMode; import com.palantir.conjure.java.types.SafetyEvaluator; import com.palantir.conjure.java.types.SpecializeBinaryClassNameVisitor; import com.palantir.conjure.java.types.TypeMapper; @@ -37,6 +41,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Stream; // TODO(rfink): Add unit tests for misc edge cases, e.g.: docs/no-docs, auth/no-auth, binary return type. @@ -49,7 +54,7 @@ public DialogueServiceGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition conjureDefinition) { + public Stream generate(ConjureDefinition conjureDefinition) { Map types = TypeFunctions.toTypesMap(conjureDefinition); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); DialogueEndpointsGenerator endpoints = new DialogueEndpointsGenerator(options); @@ -87,7 +92,7 @@ public Stream generate(ConjureDefinition conjureDefinition) { new ReturnTypeMapper(returnTypes), StaticFactoryMethodType.BLOCKING); - return conjureDefinition.getServices().stream() + Stream generatedDialogueServices = conjureDefinition.getServices().stream() .flatMap(serviceDef -> generateFilesForService( options.excludeDialogueAsyncInterfaces(), options.generateDialogueEndpointErrorResultTypes(), @@ -95,7 +100,16 @@ public Stream generate(ConjureDefinition conjureDefinition) { endpoints, interfaceGenerator, blockingGenerator, - asyncGenerator)); + asyncGenerator)) + .map(GeneratedJavaFile::of); + if (options.generateReachabilityMetadata()) { + return Stream.concat( + generatedDialogueServices, + new ReachabilityMetadataGenerator( + options.packagePrefix(), Set.of(GenerationMode.DIALOGUE_INTERFACES)) + .generate(conjureDefinition)); + } + return generatedDialogueServices; } private static Stream generateFilesForService( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java index 533ba95f2..aa77cfc20 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/CheckedErrorGenerator.java @@ -18,6 +18,8 @@ import com.google.common.base.CaseFormat; import com.palantir.conjure.java.ConjureAnnotations; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.api.errors.CheckedServiceException; @@ -53,7 +55,7 @@ public CheckedErrorGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition definition) { + public Stream generate(ConjureDefinition definition) { Map types = TypeFunctions.toTypesMap(definition); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); TypeMapper typeMapper = new TypeMapper(types, options); @@ -72,7 +74,8 @@ public Stream generate(ConjureDefinition definition) { Packages.getPrefixedPackage(namespacedErrors.javaPackage(), options.packagePrefix()), namespacedErrors.namespace(), filteredErrorDefinitions)); - }); + }) + .map(GeneratedJavaFile::of); } private JavaFile generateErrorExceptionsForNamespace( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java index 5f8629e64..ca7f8a285 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ErrorGenerator.java @@ -19,6 +19,8 @@ import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableList; import com.palantir.conjure.java.ConjureAnnotations; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; import com.palantir.conjure.java.api.errors.ErrorType; @@ -58,7 +60,7 @@ public ErrorGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition definition) { + public Stream generate(ConjureDefinition definition) { Map types = TypeFunctions.toTypesMap(definition); TypeMapper typeMapper = new TypeMapper(types, options); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(types); @@ -70,7 +72,8 @@ public Stream generate(ConjureDefinition definition) { endpointErrors, Packages.getPrefixedPackage(namespacedErrors.javaPackage(), options.packagePrefix()), namespacedErrors.namespace(), - namespacedErrors.errors()))); + namespacedErrors.errors()))) + .map(GeneratedJavaFile::of); } private static ImmutableList generateErrorTypeFields( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ObjectGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ObjectGenerator.java index 37bdf76cd..c2f0660ff 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ObjectGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ObjectGenerator.java @@ -16,8 +16,11 @@ package com.palantir.conjure.java.types; +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedJavaFile; import com.palantir.conjure.java.Generator; import com.palantir.conjure.java.Options; +import com.palantir.conjure.java.types.ReachabilityMetadataGenerator.GenerationMode; import com.palantir.conjure.java.util.TypeFunctions; import com.palantir.conjure.spec.ConjureDefinition; import com.palantir.conjure.spec.TypeDefinition; @@ -26,6 +29,7 @@ import com.palantir.javapoet.JavaFile; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Stream; public final class ObjectGenerator implements Generator { @@ -36,12 +40,21 @@ public ObjectGenerator(Options options) { } @Override - public Stream generate(ConjureDefinition definition) { + public Stream generate(ConjureDefinition definition) { List types = definition.getTypes(); Map typesMap = TypeFunctions.toTypesMap(types); TypeMapper typeMapper = new TypeMapper(typesMap, options); SafetyEvaluator safetyEvaluator = new SafetyEvaluator(typesMap); - return types.stream().map(typeDef -> generateInner(typeMapper, safetyEvaluator, typesMap, typeDef)); + Stream generatedObjectFiles = types.stream() + .map(typeDef -> generateInner(typeMapper, safetyEvaluator, typesMap, typeDef)) + .map(GeneratedJavaFile::of); + if (options.generateReachabilityMetadata()) { + return Stream.concat( + generatedObjectFiles, + new ReachabilityMetadataGenerator(options.packagePrefix(), Set.of(GenerationMode.OBJECTS)) + .generate(definition)); + } + return generatedObjectFiles; } private JavaFile generateInner( diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadata.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadata.java new file mode 100644 index 000000000..7a677926d --- /dev/null +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadata.java @@ -0,0 +1,42 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. 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. + * 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 com.palantir.conjure.java.types; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.List; + +public record ReachabilityMetadata(@JsonProperty("reflection") ReflectionMetadata reflectionReachabilityMetadata) { + + public record ReflectionMetadata(List reflectionMetadataForTypes) { + @JsonValue + public List toJson() { + return reflectionMetadataForTypes; + } + } + + public record ReflectionMetadataForType( + @JsonProperty("type") String typeName, + @JsonProperty("allDeclaredMethods") boolean allDeclaredMethods, + @JsonProperty("allPublicMethods") boolean allPublicMethods, + @JsonProperty("allDeclaredConstructors") boolean allDeclaredConstructors, + @JsonProperty("allPublicConstructors") boolean allPublicConstructors) { + public static ReflectionMetadataForType allEnabled(String typeName) { + return new ReflectionMetadataForType(typeName, true, true, true, true); + } + } +} diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadataGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadataGenerator.java new file mode 100644 index 000000000..91e6b5d4f --- /dev/null +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/ReachabilityMetadataGenerator.java @@ -0,0 +1,125 @@ +/* + * (c) Copyright 2025 Palantir Technologies Inc. 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. + * 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 com.palantir.conjure.java.types; + +import com.palantir.conjure.java.GeneratedFile; +import com.palantir.conjure.java.GeneratedFile.GeneratedReachabilityMetadataFile; +import com.palantir.conjure.java.Generator; +import com.palantir.conjure.java.types.ReachabilityMetadata.ReflectionMetadata; +import com.palantir.conjure.java.types.ReachabilityMetadata.ReflectionMetadataForType; +import com.palantir.conjure.java.util.Packages; +import com.palantir.conjure.spec.ConjureDefinition; +import com.palantir.conjure.spec.ServiceDefinition; +import com.palantir.conjure.spec.TypeDefinition; +import com.palantir.conjure.spec.TypeName; +import com.palantir.conjure.visitor.TypeDefinitionVisitor; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class ReachabilityMetadataGenerator implements Generator { + + public enum GenerationMode { + OBJECTS, + DIALOGUE_INTERFACES + } + + private final Optional packagePrefix; + private final Set generationModes; + + public ReachabilityMetadataGenerator(Optional packagePrefix, Set generationModes) { + this.packagePrefix = packagePrefix; + this.generationModes = generationModes; + } + + @Override + public Stream generate(ConjureDefinition definition) { + Stream generatedFileStream = Stream.empty(); + if (generationModes.contains(GenerationMode.OBJECTS)) { + generatedFileStream = Stream.concat( + generatedFileStream, + ReflectionMetadataFqcnByPackage.createForObjects(packagePrefix, definition.getTypes()).stream() + .map(packageAndFqcns -> new GeneratedReachabilityMetadataFile( + Packages.getPrefixedPackage(packageAndFqcns.packageName(), packagePrefix), + new ReachabilityMetadata( + new ReflectionMetadata(packageAndFqcns.fullyQualifiedClassNames().stream() + .map(ReflectionMetadataForType::allEnabled) + .toList()))))); + } + if (generationModes.contains(GenerationMode.DIALOGUE_INTERFACES)) { + generatedFileStream = Stream.concat( + generatedFileStream, + ReflectionMetadataFqcnByPackage.createForDialogueInterfaces(packagePrefix, definition.getServices()) + .stream() + .map(packageAndFqcns -> new GeneratedReachabilityMetadataFile( + Packages.getPrefixedPackage(packageAndFqcns.packageName(), packagePrefix), + // Needed for Dialogue interfaces (the types created by DialogueInterfaceGenerator) + // - isAnnotationPresent, isInterface + // - `of` static factory + new ReachabilityMetadata( + new ReflectionMetadata(packageAndFqcns.fullyQualifiedClassNames().stream() + .map(ReflectionMetadataForType::allEnabled) + .toList()))))); + } + return generatedFileStream; + } + + private record ReflectionMetadataFqcnByPackage(String packageName, List fullyQualifiedClassNames) { + static List createForObjects( + Optional packagePrefix, List typeDefinitions) { + return createFqcnsFromPackageToTypeNameMap( + typeDefinitions.stream() + .collect(Collectors.groupingBy( + type -> type.accept(TypeDefinitionVisitor.TYPE_NAME) + .getPackage(), + Collectors.mapping( + type -> type.accept(TypeDefinitionVisitor.TYPE_NAME), + Collectors.toList()))), + packagePrefix); + } + + static List createForDialogueInterfaces( + Optional packagePrefix, List serviceDefinitions) { + return createFqcnsFromPackageToTypeNameMap( + serviceDefinitions.stream() + .collect(Collectors.groupingBy( + serviceDefinition -> + serviceDefinition.getServiceName().getPackage(), + Collectors.mapping(ServiceDefinition::getServiceName, Collectors.toList()))), + packagePrefix); + } + + private static List createFqcnsFromPackageToTypeNameMap( + Map> list, Optional packagePrefix) { + return list.entrySet().stream() + .map(entry -> { + String packageName = entry.getKey(); + List fullyQualifiedTypeNames = entry.getValue().stream() + .map(typeName -> { + TypeName prefixedTypeName = Packages.getPrefixedName(typeName, packagePrefix); + return prefixedTypeName.getPackage() + "." + prefixedTypeName.getName(); + }) + .toList(); + return new ReflectionMetadataFqcnByPackage(packageName, fullyQualifiedTypeNames); + }) + .toList(); + } + } +} diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/ParameterizedConjureGenerationTest.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/ParameterizedConjureGenerationTest.java index 562838345..80e832313 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/ParameterizedConjureGenerationTest.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/ParameterizedConjureGenerationTest.java @@ -51,7 +51,7 @@ void testGeneratedCode(ParameterizedTestCase testCase) throws IOException { Conjure.parse(testCase.files().stream().map(Path::toFile).toList()); List files = new GenerationCoordinator( MoreExecutors.directExecutor(), testCase.generators(), testCase.options()) - .emit(def, tempDir); + .emitForTests(def, tempDir); assertThatFilesAreTheSame(files, testCase); } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java index 4df63b376..d40c578ea 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/TestCases.java @@ -319,6 +319,16 @@ public final class TestCases { GeneratorType.DIALOGUE, GeneratorType.UNDERTOW)) .build()) + .add(ParameterizedTestCase.builder() + .name("reachability-metadata") + .docs("Graal native-image reachability-metadata generation") + .files(Path.of("example-types.yml")) + .files(Path.of("example-service.yml")) + .options( + Options.builder().generateReachabilityMetadata(true).build()) + .generatorTypes(GeneratorType.REACHABILITY_METADATA_OBJECTS) + .generatorTypes(GeneratorType.REACHABILITY_METADATA_DIALOGUE_INTERFACES) + .build()) .build(); public static List get() { diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/GeneratorType.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/GeneratorType.java index 24ffed2c2..755bf5f56 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/GeneratorType.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/GeneratorType.java @@ -21,5 +21,7 @@ public enum GeneratorType { UNDERTOW, JERSEY, ERROR, - CHECKED_ERROR; + CHECKED_ERROR, + REACHABILITY_METADATA_OBJECTS, + REACHABILITY_METADATA_DIALOGUE_INTERFACES; } diff --git a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/ParameterizedTestCase.java b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/ParameterizedTestCase.java index 4a1155bc0..4fe8d8964 100644 --- a/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/ParameterizedTestCase.java +++ b/conjure-java-core/src/test/java/com/palantir/conjure/java/parameterized/objects/ParameterizedTestCase.java @@ -26,6 +26,8 @@ import com.palantir.conjure.java.types.CheckedErrorGenerator; import com.palantir.conjure.java.types.ErrorGenerator; import com.palantir.conjure.java.types.ObjectGenerator; +import com.palantir.conjure.java.types.ReachabilityMetadataGenerator; +import com.palantir.conjure.java.types.ReachabilityMetadataGenerator.GenerationMode; import com.palantir.logsafe.Preconditions; import java.nio.file.Path; import java.util.List; @@ -65,6 +67,10 @@ public Set generators() { case JERSEY -> new JerseyServiceGenerator(options()); case ERROR -> new ErrorGenerator(options()); case CHECKED_ERROR -> new CheckedErrorGenerator(options()); + case REACHABILITY_METADATA_OBJECTS -> new ReachabilityMetadataGenerator( + options().packagePrefix(), Set.of(ReachabilityMetadataGenerator.GenerationMode.OBJECTS)); + case REACHABILITY_METADATA_DIALOGUE_INTERFACES -> new ReachabilityMetadataGenerator( + options().packagePrefix(), Set.of(GenerationMode.DIALOGUE_INTERFACES)); }) .collect(Collectors.toSet()); } diff --git a/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java b/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java index 1e45637e5..ae0eec489 100644 --- a/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java +++ b/conjure-java/src/main/java/com/palantir/conjure/java/cli/ConjureJavaCli.java @@ -239,6 +239,13 @@ public static final class GenerateCommand implements Runnable { "Generate result types in Dialogue clients for endpoints with errors associated with them.") private boolean generateDialogueEndpointErrorResultTypes; + @CommandLine.Option( + names = "--generateReachabilityMetadata", + defaultValue = "false", + description = "Generate reachability-metadata.json files for Conjure generated types which are used by " + + "Graal's native-image") + private boolean generateReachabilityMetadata; + @SuppressWarnings("unused") @CommandLine.Unmatched private List unmatchedOptions; @@ -271,6 +278,12 @@ public void run() { if (config.generateDialogue()) { generatorBuilder.add(new DialogueServiceGenerator(config.options())); } + if (config.options().generateReachabilityMetadata() + && !config.generateObjects() + && !config.generateDialogue()) { + System.err.println( + "Reachability metadata generation is only supported for objects and dialogue services."); + } new GenerationCoordinator(executor, generatorBuilder.build(), config.options()) .emit(conjureDefinition, config.outputDirectory()); } catch (IOException e) { @@ -311,6 +324,7 @@ CliConfiguration getConfiguration() { .externalFallbackTypes(externalFallbackTypes) .preferObjectBuilders(preferObjectBuilders) .generateDialogueEndpointErrorResultTypes(generateDialogueEndpointErrorResultTypes) + .generateReachabilityMetadata(generateReachabilityMetadata) .build()) .build(); }