From 0702da9e1baeb9eca9775e5dbfcf5d09e88daab1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:16:21 +0000 Subject: [PATCH 1/5] Document AutoRest Java customizations Co-authored-by: XiaofeiCao <92354331+XiaofeiCao@users.noreply.github.com> --- docs/contributor/README.md | 1 + .../autorest-java-customization.md | 172 ++++++++++++++++++ docs/contributor/typespec-quickstart.md | 2 + 3 files changed, 175 insertions(+) create mode 100644 docs/contributor/autorest-java-customization.md diff --git a/docs/contributor/README.md b/docs/contributor/README.md index 9f9a26aa7405..f449d9b4f7c9 100644 --- a/docs/contributor/README.md +++ b/docs/contributor/README.md @@ -28,6 +28,7 @@ If you are a **consumer** of the SDK looking for usage guidance, start at the [U | [Versioning](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/versioning.md) | `version_client.txt`, dependency tags, incrementing versions | | [Adding a Module](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/adding-a-module.md) | Create a new SDK module: dir structure, POM, versioning, CODEOWNERS | | [TypeSpec Quickstart](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/typespec-quickstart.md) | End-to-end workflow: generate → build → test → release | +| [AutoRest Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/autorest-java-customization.md) | Customize generated Java code when TypeSpec cannot express the required behavior | | [Writing Performance Tests](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/performance-tests.md) | Set up and run `perf-test-core` benchmarks | | [JavaDoc & Code Snippets](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/javadocs.md) | Javadoc standards and codesnippet-maven-plugin workflow | | [Access Helpers](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/access-helpers.md) | Cross-package internal access without public APIs | diff --git a/docs/contributor/autorest-java-customization.md b/docs/contributor/autorest-java-customization.md new file mode 100644 index 000000000000..cd3cb578d5a0 --- /dev/null +++ b/docs/contributor/autorest-java-customization.md @@ -0,0 +1,172 @@ +# AutoRest Java Customization + +The `azure-autorest-customization` package provides APIs to safely customize generated Java code for cases that +AutoRest cannot represent directly. It uses the Eclipse language server to validate the Java code it changes. + +## Before you customize + +First consider whether the change belongs in TypeSpec (`client.tsp`). TypeSpec customizations are cleaner and persist +through regeneration. See the [TypeSpec Client Customizations Reference](https://github.com/Azure/azure-sdk-for-java/blob/main/eng/common/knowledge/customizing-client-tsp.md) for decorators such as `@@clientName` and `@@access`. + +Use Java code customizations only when TypeSpec cannot express the required behavior. + +## Set up a customization project + +Create a Maven project that depends on `azure-autorest-customization`: + +```xml + + com.azure.tools + azure-autorest-customization + 1.0.0-beta.11 + +``` + +Create a class that extends `com.azure.autorest.customization.Customization` and overrides +`void customize(LibraryCustomization, Logger)`. The `LibraryCustomization` parameter is the entry point for changing +generated Java code before it is written to disk. + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo") + .customizeAst(ast -> ast.getClassByName("Foo") + .ifPresent(clazz -> clazz.addMarkerAnnotation("Deprecated"))); +} +``` + +Configure the generator to use the class: + +```yaml +customization-class: src/main/java/MyCustomization.java +``` + +## Navigate generated code + +Start with `LibraryCustomization`, then navigate to a package or class with `getClass(packageName, className)`. +`LibraryCustomization`, `PackageCustomization`, and `ClassCustomization` provide the primary navigation APIs. +Use `customizeAst` to change the JavaParser AST for the selected source file. Add imports with +`ast.addImport(...)` whenever the new code references a type not already imported. + +## Supported customizations + +The following common changes can be made through the JavaParser AST: + +| Customization | Typical AST operation | +|---|---| +| Change a class modifier | `ClassOrInterfaceDeclaration.setModifiers(...)` | +| Change a method modifier | `MethodDeclaration.setModifiers(...)` | +| Change a method return type or body | `MethodDeclaration.setType(...)` and `setBody(...)` | +| Change a class supertype | Clear and add `ClassOrInterfaceType` extended types | +| Add or remove a class or method annotation | `addMarkerAnnotation(...)` or remove the matching annotation node | +| Add a field default value | `VariableDeclarator.setInitializer(...)` | +| Add getter and setter methods | `ClassOrInterfaceDeclaration.addMethod(...)` | +| Rename an enum member | `EnumConstantDeclaration.setName(...)` | +| Update class or method Javadoc | `setJavadocComment(...)` | +| Add or remove Javadoc tags | Update the `Javadoc` returned by `getJavadoc()` | + +### Change a method modifier + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo").customizeAst(ast -> ast.getClassByName("Foo") + .ifPresent(clazz -> clazz.getMethodsByName("getBar") + .forEach(method -> method.setModifiers(Modifier.Keyword.PRIVATE)))); +} +``` + +### Change a method return type + +When changing a return type, update both the declared type and method body. Add an import for the replacement type. + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo").customizeAst(ast -> { + ast.addImport(UUID.class); + ast.getClassByName("Foo").ifPresent(clazz -> clazz.getMethodsByName("getId").forEach(method -> { + method.setType("UUID"); + method.setBody(StaticJavaParser.parseBlock("{ return UUID.fromString(this.id); }")); + })); + }); +} +``` + +For a method originally returning `void`, provide the complete return expression. To change a method to return +`void`, set its body appropriately and do not add a return value. + +### Change a supertype + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo").customizeAst(ast -> ast.getClassByName("Foo") + .ifPresent(clazz -> { + ast.addImport("com.azure.myservice.models.Bar1"); + clazz.getExtendedTypes().clear(); + clazz.addExtendedType(new ClassOrInterfaceType(null, "Bar1")); + })); +} +``` + +### Add a field default value + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo").customizeAst(ast -> ast.getClassByName("Foo") + .flatMap(clazz -> clazz.getFieldByName("bar")) + .ifPresent(field -> field.getVariables().forEach(variable -> { + if ("bar".equals(variable.getNameAsString())) { + variable.setInitializer("\"bar\""); + } + }))); +} +``` + +### Generate accessor methods + +```java +@Override +public void customize(LibraryCustomization customization, Logger logger) { + customization.getClass("com.azure.myservice.models", "Foo").customizeAst(ast -> ast.getClassByName("Foo") + .ifPresent(clazz -> { + clazz.addMethod("isActive", Modifier.Keyword.PUBLIC).setType("boolean") + .setBody(StaticJavaParser.parseBlock("{ return this.active; }")); + clazz.addMethod("setActive", Modifier.Keyword.PUBLIC).setType("Foo") + .addParameter("boolean", "active") + .setBody(StaticJavaParser.parseBlock("{ this.active = active; return this; }")); + })); +} +``` + +### Update Javadoc + +Set a complete description with `setJavadocComment`. For parameter, return, and exception tags, get the existing +Javadoc and add a block tag: + +```java +method.getJavadoc().ifPresent(javadoc -> method.setJavadocComment( + javadoc.addBlockTag("param", "active", "whether the Foo is active"))); +``` + +Use `return` for a return-value tag and `throws` with the exception type and description for an exception tag. + +## Troubleshooting + +### AutoRest reports “Unable to format output file” + +Customized Java code likely contains a syntax error. + +1. Add the `skip-formatting` flag to bypass formatting while diagnosing the generated source. +2. Compile or inspect the generated code to find the error. +3. Fix the customization code, or report a defect in `customization-base` if it caused the invalid code. +4. Remove `skip-formatting`. + +## Developer note + +`azure-autorest-customization` sends generated source files to an Eclipse language server for IDE-like analysis. Its +dummy `pom.xml` in `src/main/resources` supplies the language server dependencies. When generator changes cause +customizations to fail, verify that this POM contains all required dependencies; otherwise processing can fall back to +less reliable text-based behavior. diff --git a/docs/contributor/typespec-quickstart.md b/docs/contributor/typespec-quickstart.md index c482c82b5fde..b5d787bbe5a5 100644 --- a/docs/contributor/typespec-quickstart.md +++ b/docs/contributor/typespec-quickstart.md @@ -101,6 +101,8 @@ Set `partial-update: true` in `tspconfig.yaml` emitter options. TypeSpec-Java wi customization-class: customization/src/main/java/MyCustomization.java ``` +See [AutoRest Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/autorest-java-customization.md) for guidance on writing AST-based Java customizations. + --- ## 5. Improve Documentation From c45fce6df5e10c4f678baf6b870f6302b5dceee3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:26:27 +0000 Subject: [PATCH 2/5] Rename customization guide for TypeSpec Java Co-authored-by: XiaofeiCao <92354331+XiaofeiCao@users.noreply.github.com> --- docs/contributor/README.md | 2 +- .../sdk-generation-pipeline-troubleshooting.md | 2 +- ...ization.md => typespec-java-customization.md} | 16 ++++++++-------- docs/contributor/typespec-quickstart.md | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) rename docs/contributor/{autorest-java-customization.md => typespec-java-customization.md} (90%) diff --git a/docs/contributor/README.md b/docs/contributor/README.md index f449d9b4f7c9..1de726c5377a 100644 --- a/docs/contributor/README.md +++ b/docs/contributor/README.md @@ -28,7 +28,7 @@ If you are a **consumer** of the SDK looking for usage guidance, start at the [U | [Versioning](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/versioning.md) | `version_client.txt`, dependency tags, incrementing versions | | [Adding a Module](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/adding-a-module.md) | Create a new SDK module: dir structure, POM, versioning, CODEOWNERS | | [TypeSpec Quickstart](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/typespec-quickstart.md) | End-to-end workflow: generate → build → test → release | -| [AutoRest Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/autorest-java-customization.md) | Customize generated Java code when TypeSpec cannot express the required behavior | +| [TypeSpec Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/typespec-java-customization.md) | Customize generated Java code when TypeSpec cannot express the required behavior | | [Writing Performance Tests](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/performance-tests.md) | Set up and run `perf-test-core` benchmarks | | [JavaDoc & Code Snippets](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/javadocs.md) | Javadoc standards and codesnippet-maven-plugin workflow | | [Access Helpers](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/access-helpers.md) | Cross-package internal access without public APIs | diff --git a/docs/contributor/sdk-generation-pipeline-troubleshooting.md b/docs/contributor/sdk-generation-pipeline-troubleshooting.md index 6b54bcf646da..30e2d3ba8a06 100644 --- a/docs/contributor/sdk-generation-pipeline-troubleshooting.md +++ b/docs/contributor/sdk-generation-pipeline-troubleshooting.md @@ -131,7 +131,7 @@ please check whether it causes failure, and fix them before apiview. > - *generated code*: produced by the generator; API surface may change when the spec or generator changes, which can break compilation. > - *customization code*: maintained by SDK developers; commonly wired via `customization-class` and preserved during regeneration with `partial-update: true`. > -> Reference: autorest.java customization-base: https://github.com/Azure/autorest.java/tree/main/customization-base +> Reference: [TypeSpec Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/typespec-java-customization.md) **Prerequisites for customization:** When adding customization code, two options must be set in `tspconfig.yaml`: ```yaml diff --git a/docs/contributor/autorest-java-customization.md b/docs/contributor/typespec-java-customization.md similarity index 90% rename from docs/contributor/autorest-java-customization.md rename to docs/contributor/typespec-java-customization.md index cd3cb578d5a0..bb6dbc19d4ab 100644 --- a/docs/contributor/autorest-java-customization.md +++ b/docs/contributor/typespec-java-customization.md @@ -1,7 +1,7 @@ -# AutoRest Java Customization +# TypeSpec Java Customization The `azure-autorest-customization` package provides APIs to safely customize generated Java code for cases that -AutoRest cannot represent directly. It uses the Eclipse language server to validate the Java code it changes. +TypeSpec Java cannot represent directly. It uses the Eclipse language server to validate the Java code it changes. ## Before you customize @@ -155,18 +155,18 @@ Use `return` for a return-value tag and `throws` with the exception type and des ## Troubleshooting -### AutoRest reports “Unable to format output file” +### TypeSpec Java reports “Unable to format output file” Customized Java code likely contains a syntax error. 1. Add the `skip-formatting` flag to bypass formatting while diagnosing the generated source. 2. Compile or inspect the generated code to find the error. -3. Fix the customization code, or report a defect in `customization-base` if it caused the invalid code. +3. Fix the customization code, or report a defect in the TypeSpec Java generator if it produced invalid code. 4. Remove `skip-formatting`. ## Developer note -`azure-autorest-customization` sends generated source files to an Eclipse language server for IDE-like analysis. Its -dummy `pom.xml` in `src/main/resources` supplies the language server dependencies. When generator changes cause -customizations to fail, verify that this POM contains all required dependencies; otherwise processing can fall back to -less reliable text-based behavior. +TypeSpec Java sends generated source files to an Eclipse language server for IDE-like analysis. Its dummy `pom.xml` in +`src/main/resources` supplies the language server dependencies. When generator changes cause customizations to fail, +verify that this POM contains all required dependencies; otherwise processing can fall back to less reliable text-based +behavior. diff --git a/docs/contributor/typespec-quickstart.md b/docs/contributor/typespec-quickstart.md index b5d787bbe5a5..c2d0878c0c90 100644 --- a/docs/contributor/typespec-quickstart.md +++ b/docs/contributor/typespec-quickstart.md @@ -101,7 +101,7 @@ Set `partial-update: true` in `tspconfig.yaml` emitter options. TypeSpec-Java wi customization-class: customization/src/main/java/MyCustomization.java ``` -See [AutoRest Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/autorest-java-customization.md) for guidance on writing AST-based Java customizations. +See [TypeSpec Java Customization](https://github.com/Azure/azure-sdk-for-java/blob/main/docs/contributor/typespec-java-customization.md) for guidance on writing AST-based Java customizations. --- From 245db22883a3969dd04ea757f750a72778f72fea Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Tue, 4 Aug 2026 11:41:59 +0800 Subject: [PATCH 3/5] address comments --- docs/contributor/typespec-java-customization.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/docs/contributor/typespec-java-customization.md b/docs/contributor/typespec-java-customization.md index bb6dbc19d4ab..39855d26c9cb 100644 --- a/docs/contributor/typespec-java-customization.md +++ b/docs/contributor/typespec-java-customization.md @@ -1,7 +1,7 @@ # TypeSpec Java Customization The `azure-autorest-customization` package provides APIs to safely customize generated Java code for cases that -TypeSpec Java cannot represent directly. It uses the Eclipse language server to validate the Java code it changes. +TypeSpec Java cannot represent directly. Customizations use JavaParser ASTs through `ClassCustomization.customizeAst`. ## Before you customize @@ -155,18 +155,8 @@ Use `return` for a return-value tag and `throws` with the exception type and des ## Troubleshooting -### TypeSpec Java reports “Unable to format output file” +### TypeSpec Java reports “Failed to format file: ``. File content: ``.” Customized Java code likely contains a syntax error. -1. Add the `skip-formatting` flag to bypass formatting while diagnosing the generated source. -2. Compile or inspect the generated code to find the error. -3. Fix the customization code, or report a defect in the TypeSpec Java generator if it produced invalid code. -4. Remove `skip-formatting`. - -## Developer note - -TypeSpec Java sends generated source files to an Eclipse language server for IDE-like analysis. Its dummy `pom.xml` in -`src/main/resources` supplies the language server dependencies. When generator changes cause customizations to fail, -verify that this POM contains all required dependencies; otherwise processing can fall back to less reliable text-based -behavior. +Inspect the source shown after `File content:` to locate the malformed code, then fix the customization and regenerate SDK. From 83cf6efafefc346dcccbdc4f5344b8a921087951 Mon Sep 17 00:00:00 2001 From: Xiaofei Cao <92354331+XiaofeiCao@users.noreply.github.com> Date: Tue, 4 Aug 2026 11:57:30 +0800 Subject: [PATCH 4/5] apply copilot review for customization example Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/contributor/typespec-java-customization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributor/typespec-java-customization.md b/docs/contributor/typespec-java-customization.md index 39855d26c9cb..160b54ac2d95 100644 --- a/docs/contributor/typespec-java-customization.md +++ b/docs/contributor/typespec-java-customization.md @@ -38,7 +38,7 @@ public void customize(LibraryCustomization customization, Logger logger) { Configure the generator to use the class: ```yaml -customization-class: src/main/java/MyCustomization.java +customization-class: customization/src/main/java/MyCustomization.java ``` ## Navigate generated code From 12252bfd9fdb809b50b145302f769d7f53128d92 Mon Sep 17 00:00:00 2001 From: "Xiaofei Cao (from Dev Box)" Date: Tue, 4 Aug 2026 14:00:15 +0800 Subject: [PATCH 5/5] use diff for customization-class configuration --- docs/contributor/typespec-java-customization.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/contributor/typespec-java-customization.md b/docs/contributor/typespec-java-customization.md index 160b54ac2d95..05c3f102c0c9 100644 --- a/docs/contributor/typespec-java-customization.md +++ b/docs/contributor/typespec-java-customization.md @@ -37,8 +37,11 @@ public void customize(LibraryCustomization customization, Logger logger) { Configure the generator to use the class: -```yaml -customization-class: customization/src/main/java/MyCustomization.java +```diff + "@azure-tools/typespec-java": + emitter-output-dir: "{output-dir}/{service-dir}/azure-contoso-widgetmanager" + namespace: com.azure.contoso.widgetmanager ++ customization-class: customization/src/main/java/MyCustomization.java ``` ## Navigate generated code