From fe5f911cb44d1b02e386ca2776a9f1270009a725 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 18 Jan 2025 21:46:06 -0500 Subject: [PATCH 1/6] fully support package private will now generate separate HttpComponents for package-private client interfaces --- .../generator/client/ClientProcessor.java | 82 ++++++++++++++----- .../http/generator/client/ClientWriter.java | 12 +-- .../generator/client/ComponentMetaData.java | 44 ++++++++-- .../generator/client/ComponentReader.java | 60 ++++++++------ .../client/SimpleComponentWriter.java | 10 --- .../client/clients/PrivateClient.java | 13 +++ .../client/clients/PrivateClient2.java | 13 +++ .../client/clients/other/PrivateClient2.java | 13 +++ .../generator/core/ProcessingContext.java | 8 +- 9 files changed, 184 insertions(+), 71 deletions(-) create mode 100644 http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient.java create mode 100644 http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient2.java create mode 100644 http-generator-client/src/test/java/io/avaje/http/generator/client/clients/other/PrivateClient2.java diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java index 2ba487244..af97a5f5f 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java @@ -1,11 +1,14 @@ package io.avaje.http.generator.client; +import static io.avaje.http.generator.core.ProcessingContext.createMetaInfWriter; import static io.avaje.http.generator.core.ProcessingContext.logError; import static io.avaje.http.generator.core.ProcessingContext.platform; import static io.avaje.http.generator.core.ProcessingContext.setPlatform; import static io.avaje.http.generator.core.ProcessingContext.typeElement; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -15,10 +18,13 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; import io.avaje.http.generator.core.APContext; import io.avaje.http.generator.core.ClientPrism; +import io.avaje.http.generator.core.Constants; import io.avaje.http.generator.core.ControllerReader; import io.avaje.http.generator.core.ImportPrism; import io.avaje.http.generator.core.ProcessingContext; @@ -30,6 +36,8 @@ public class ClientProcessor extends AbstractProcessor { private final ComponentMetaData metaData = new ComponentMetaData(); + private final Map privateMetaData = new HashMap<>(); + private boolean useJsonB; private SimpleComponentWriter componentWriter; @@ -79,7 +87,7 @@ private void readModule() { return; } readModuleInfo = true; - new ComponentReader(metaData).read(); + new ComponentReader(metaData, privateMetaData).read(); } private void writeForImported(Element importedElement) { @@ -94,39 +102,75 @@ private void writeClient(Element controller) { final ControllerReader reader = new ControllerReader((TypeElement) controller); reader.read(false); try { - metaData.add(writeClientAdapter(reader)); + + var packagePrivate = + !controller.getModifiers().contains(Modifier.PUBLIC) + && ClientPrism.isPresent(controller); + if (packagePrivate) { + var packageName = + APContext.elements().getPackageOf(controller).getQualifiedName().toString(); + var meta = privateMetaData.computeIfAbsent(packageName, k -> new ComponentMetaData()); + meta.add(writeClientAdapter(reader, true)); + } else { + metaData.add(writeClientAdapter(reader, false)); + } + } catch (final Exception e) { logError(reader.beanType(), "Failed to write client class " + e); } } } - protected String writeClientAdapter(ControllerReader reader) throws IOException { + protected String writeClientAdapter(ControllerReader reader, boolean packagePrivate) throws IOException { var suffix = ClientSuffix.fromInterface(reader.beanType().getQualifiedName().toString()); - return new ClientWriter(reader, suffix, useJsonB).write(); - } - - private void initialiseComponent() { - metaData.initialiseFullName(); - if (!metaData.all().isEmpty()) { - ProcessingContext.addClientComponent(metaData.fullName()); - ProcessingContext.validateModule(); - } - try { - componentWriter.init(); - } catch (final IOException e) { - logError("Error creating writer for JsonbComponent", e); - } + return new ClientWriter(reader, suffix, useJsonB, packagePrivate).write(); } private void writeComponent(boolean processingOver) { - initialiseComponent(); if (processingOver) { + try { - componentWriter.write(); + + + if (!metaData.all().isEmpty()) { + ProcessingContext.addClientComponent(metaData.fullName()); + + componentWriter.init(); + componentWriter.write(); + } + + for (var meta : privateMetaData.values()) { + ProcessingContext.addClientComponent(meta.fullName()); + + var writer = new SimpleComponentWriter(meta); + writer.init(); + writer.write(); + } + writeMetaInf(); + ProcessingContext.validateModule(); } catch (final IOException e) { logError("Error writing component", e); } } } + + void writeMetaInf() throws IOException { + final FileObject fileObject = createMetaInfWriter(Constants.META_INF_COMPONENT); + if (fileObject != null) { + try (var fileWriter = fileObject.openWriter()) { + + if (!metaData.all().isEmpty()) { + + fileWriter.write(metaData.fullName()); + fileWriter.write("\n"); + } + + for (var meta : privateMetaData.values()) { + + fileWriter.write(meta.fullName()); + fileWriter.write("\n"); + } + } + } + } } diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java index 643525135..11aef35f0 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java @@ -1,6 +1,5 @@ package io.avaje.http.generator.client; -import io.avaje.http.generator.core.APContext; import io.avaje.http.generator.core.BaseControllerWriter; import io.avaje.http.generator.core.ClientPrism; import io.avaje.http.generator.core.ControllerReader; @@ -28,9 +27,12 @@ final class ClientWriter extends BaseControllerWriter { private final Set propertyConstants = new HashSet<>(); private final String suffix; - ClientWriter(ControllerReader reader, String suffix, boolean useJsonB) throws IOException { + private final boolean packagePrivate; + + ClientWriter(ControllerReader reader, String suffix, boolean useJsonB, boolean packagePrivate) throws IOException { super(reader, suffix); this.suffix = suffix; + this.packagePrivate = packagePrivate; reader.addImportType(HTTP_CLIENT); this.useJsonb = useJsonB; readMethods(); @@ -79,12 +81,12 @@ private void writeMethods() { private void writeClassStart() { writer.append(AT_GENERATED).eol(); AnnotationUtil.writeAnnotations(writer, reader.beanType()); - - writer.append("public final class %s%s implements %s, AutoCloseable {", shortName, suffix, shortName).eol().eol(); + var access = packagePrivate ? "" : "public "; + writer.append("%sfinal class %s%s implements %s, AutoCloseable {", access, shortName, suffix, shortName).eol().eol(); writer.append(" private final HttpClient client;").eol().eol(); - writer.append(" public %s%s(HttpClient client) {", shortName, suffix).eol(); + writer.append(" %s%s%s(HttpClient client) {", access, shortName, suffix).eol(); writer.append(" this.client = client;").eol(); writer.append(" }").eol().eol(); } diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentMetaData.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentMetaData.java index ac0866394..28b1ec99e 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentMetaData.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentMetaData.java @@ -4,7 +4,7 @@ final class ComponentMetaData { - private final List generatedClients = new ArrayList<>(); + private final Set generatedClients = new HashSet<>(); private String fullName; @Override @@ -12,11 +12,6 @@ public String toString() { return generatedClients.toString(); } - /** Ensure the component name has been initialised. */ - void initialiseFullName() { - fullName(); - } - void add(String type) { generatedClients.add(type); } @@ -28,13 +23,13 @@ void setFullName(String fullName) { String fullName() { if (fullName == null) { String topPackage = TopPackage.of(generatedClients); - fullName = topPackage + ".GeneratedHttpComponent"; + fullName = topPackage + "." + name(topPackage) + "HttpComponent"; } return fullName; } List all() { - return generatedClients; + return new ArrayList<>(generatedClients); } /** Return the package imports for the JsonAdapters and related types. */ @@ -46,4 +41,37 @@ Collection allImports() { return packageImports; } + + + static String name(String name) { + if (name == null) { + return null; + } + final int pos = name.lastIndexOf('.'); + if (pos > -1) { + name = name.substring(pos + 1); + } + return camelCase(name).replaceFirst("Httpclient", "Generated"); + } + + private static String camelCase(String name) { + StringBuilder sb = new StringBuilder(name.length()); + boolean upper = true; + for (char aChar : name.toCharArray()) { + if (Character.isLetterOrDigit(aChar)) { + if (upper) { + aChar = Character.toUpperCase(aChar); + upper = false; + } + sb.append(aChar); + } else if (toUpperOn(aChar)) { + upper = true; + } + } + return sb.toString(); + } + + private static boolean toUpperOn(char aChar) { + return aChar == ' ' || aChar == '-' || aChar == '_'; + } } diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java index b6d335af1..37278d7f9 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java @@ -1,8 +1,10 @@ package io.avaje.http.generator.client; + import static io.avaje.http.generator.core.ProcessingContext.filer; import static io.avaje.http.generator.core.ProcessingContext.logDebug; import static io.avaje.http.generator.core.ProcessingContext.logWarn; import static io.avaje.http.generator.core.ProcessingContext.typeElement; +import static java.util.stream.Collectors.toList; import java.io.FileNotFoundException; import java.io.LineNumberReader; @@ -11,14 +13,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import javax.annotation.processing.FilerException; -import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; import javax.tools.FileObject; import javax.tools.StandardLocation; +import io.avaje.http.generator.core.APContext; import io.avaje.http.generator.core.Constants; import io.avaje.prism.GeneratePrism; @@ -26,41 +29,48 @@ final class ComponentReader { private final ComponentMetaData componentMetaData; + private final Map privateMetaData; - ComponentReader(ComponentMetaData metaData) { + ComponentReader(ComponentMetaData metaData, Map privateMetaData) { this.componentMetaData = metaData; + this.privateMetaData = privateMetaData; } void read() { - final String componentFullName = loadMetaInfServices(); - if (componentFullName != null) { - final TypeElement moduleType = typeElement(componentFullName); + + for (String fqn : loadMetaInf()) { + + final TypeElement moduleType = typeElement(fqn); if (moduleType != null) { - componentMetaData.setFullName(componentFullName); - readMetaData(moduleType); - } - } - } + var adapters = + MetaDataPrism.getInstanceOn(moduleType).value().stream() + .map(APContext::asTypeElement) + .collect(toList()); - /** Read the existing JsonAdapters from the MetaData annotation of the generated component. */ - private void readMetaData(TypeElement moduleType) { - for (final AnnotationMirror annotationMirror : moduleType.getAnnotationMirrors()) { - MetaDataPrism.getOptional(annotationMirror).map(MetaDataPrism::value).stream() - .flatMap(List::stream) - .map(TypeMirror::toString) - .forEach(componentMetaData::add); - } - } + if (adapters.get(0).getModifiers().contains(Modifier.PUBLIC)) { + componentMetaData.setFullName(fqn); + adapters.stream() + .map(TypeElement::getQualifiedName) + .map(Object::toString) + .forEach(componentMetaData::add); - private String loadMetaInfServices() { - final List lines = loadMetaInf(); - return lines.isEmpty() ? null : lines.get(0); + } else { + var packageName = + APContext.elements().getPackageOf(moduleType).getQualifiedName().toString(); + var meta = privateMetaData.computeIfAbsent(packageName, k -> new ComponentMetaData()); + adapters.stream() + .map(TypeElement::getQualifiedName) + .map(Object::toString) + .forEach(meta::add); + } + } + } } private List loadMetaInf() { try { - final FileObject fileObject = filer() - .getResource(StandardLocation.CLASS_OUTPUT, "", Constants.META_INF_COMPONENT); + final FileObject fileObject = + filer().getResource(StandardLocation.CLASS_OUTPUT, "", Constants.META_INF_COMPONENT); if (fileObject != null) { final List lines = new ArrayList<>(); diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/SimpleComponentWriter.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/SimpleComponentWriter.java index 9a5f05809..8ffd70de3 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/SimpleComponentWriter.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/SimpleComponentWriter.java @@ -46,16 +46,6 @@ void write() throws IOException { writeRegister(); writeClassEnd(); writer.close(); - writeMetaInf(); - } - - void writeMetaInf() throws IOException { - final FileObject fileObject = createMetaInfWriter(Constants.META_INF_COMPONENT); - if (fileObject != null) { - try (var fileWriter = fileObject.openWriter()) { - fileWriter.write(fullName); - } - } } private void writeRegister() { diff --git a/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient.java b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient.java new file mode 100644 index 000000000..06fc0b735 --- /dev/null +++ b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient.java @@ -0,0 +1,13 @@ +package io.avaje.http.generator.client.clients; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Header; + +@Client +interface PrivateClient { + + @Get("/private") + String apiCall(@Header("Accept") String accept); + +} diff --git a/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient2.java b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient2.java new file mode 100644 index 000000000..407fa636e --- /dev/null +++ b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/PrivateClient2.java @@ -0,0 +1,13 @@ +package io.avaje.http.generator.client.clients; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Header; + +@Client +public interface PrivateClient2 { + + @Get("/private") + String apiCall(@Header("Accept") String accept); + +} diff --git a/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/other/PrivateClient2.java b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/other/PrivateClient2.java new file mode 100644 index 000000000..f3bfe589d --- /dev/null +++ b/http-generator-client/src/test/java/io/avaje/http/generator/client/clients/other/PrivateClient2.java @@ -0,0 +1,13 @@ +package io.avaje.http.generator.client.clients.other; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Header; + +@Client +interface PrivateClient2 { + + @Get("/private") + String apiCall(@Header("Accept") String accept); + +} diff --git a/http-generator-core/src/main/java/io/avaje/http/generator/core/ProcessingContext.java b/http-generator-core/src/main/java/io/avaje/http/generator/core/ProcessingContext.java index f15a9ebc0..c865caa14 100644 --- a/http-generator-core/src/main/java/io/avaje/http/generator/core/ProcessingContext.java +++ b/http-generator-core/src/main/java/io/avaje/http/generator/core/ProcessingContext.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.net.URI; import java.nio.file.Paths; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -49,7 +50,7 @@ private static final class Ctx { private final boolean instrumentAllMethods; private final boolean disableDirectWrites; private final boolean javalin6; - private String clientFQN; + private final Set clientFQN = new HashSet<>(); Ctx(ProcessingEnvironment env, PlatformAdapter adapter, boolean generateOpenAPI) { readAdapter = adapter; @@ -243,9 +244,8 @@ public static void validateModule() { logWarn(module, "io.avaje.http.api.javalin only contains SOURCE retention annotations. It should added as `requires static`"); } }); - var fqn = CTX.get().clientFQN; - reader.validateServices("io.avaje.http.client.HttpClient.GeneratedComponent", Set.of(fqn)); + reader.validateServices("io.avaje.http.client.HttpClient.GeneratedComponent", CTX.get().clientFQN); } catch (Exception e) { // can't read module @@ -285,6 +285,6 @@ private static boolean resourceExists(String relativeName) { } public static void addClientComponent(String clientFQN) { - CTX.get().clientFQN = clientFQN; + CTX.get().clientFQN.add(clientFQN); } } From d50b4b73871918835c4cc07a65eaa5b6d4e37a2f Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 18 Jan 2025 21:53:14 -0500 Subject: [PATCH 2/6] Create PrivateClient.java --- .../java/org/example/pkgprivate/PrivateClient.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/test-client-generation/src/main/java/org/example/pkgprivate/PrivateClient.java diff --git a/tests/test-client-generation/src/main/java/org/example/pkgprivate/PrivateClient.java b/tests/test-client-generation/src/main/java/org/example/pkgprivate/PrivateClient.java new file mode 100644 index 000000000..cb9511b44 --- /dev/null +++ b/tests/test-client-generation/src/main/java/org/example/pkgprivate/PrivateClient.java @@ -0,0 +1,13 @@ +package org.example.pkgprivate; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.api.Header; + +@Client +interface PrivateClient { + + @Get("/private") + String apiCall(@Header("Accept") String accept); + +} From 5fdc12027ff61db216bbcbb8b03ba9089673b8a4 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sun, 26 Jan 2025 00:45:05 -0500 Subject: [PATCH 3/6] Update ClientProcessor.java --- .../http/generator/client/ClientProcessor.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java index e11132d2a..00ae30c98 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java @@ -125,25 +125,11 @@ protected String writeClientAdapter(ControllerReader reader, boolean packagePriv return new ClientWriter(reader, suffix, packagePrivate).write(); } - private void initialiseComponent() { - metaData.initialiseFullName(); - if (!metaData.all().isEmpty()) { - ProcessingContext.addClientComponent(metaData.fullName()); - ProcessingContext.validateModule(); - } - try { - componentWriter.init(); - } catch (final IOException e) { - logError("Error creating writer for JsonbComponent", e); - } - } - private void writeComponent(boolean processingOver) { if (processingOver) { try { - if (!metaData.all().isEmpty()) { ProcessingContext.addClientComponent(metaData.fullName()); From 281d5fa580232f4393b2ccab032669acc05fd377 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sun, 26 Jan 2025 00:55:35 -0500 Subject: [PATCH 4/6] test --- .../github/pkgprivate/SimplePkgPrivate.java | 12 +++++ .../src/main/java/module-info.java | 3 +- ....http.client.HttpClient$GeneratedComponent | 1 - .../github/pkgprivate/PkgPrivateTest.java | 44 +++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/test-client/src/main/java/example/github/pkgprivate/SimplePkgPrivate.java delete mode 100644 tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent create mode 100644 tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java diff --git a/tests/test-client/src/main/java/example/github/pkgprivate/SimplePkgPrivate.java b/tests/test-client/src/main/java/example/github/pkgprivate/SimplePkgPrivate.java new file mode 100644 index 000000000..674f1ccb8 --- /dev/null +++ b/tests/test-client/src/main/java/example/github/pkgprivate/SimplePkgPrivate.java @@ -0,0 +1,12 @@ +package example.github.pkgprivate; + +import io.avaje.http.api.Client; +import io.avaje.http.api.Get; +import io.avaje.http.client.HttpException; + +@Client +interface SimplePkgPrivate { + + @Get("private") + String get() throws HttpException; +} diff --git a/tests/test-client/src/main/java/module-info.java b/tests/test-client/src/main/java/module-info.java index a925d770c..a83254a65 100644 --- a/tests/test-client/src/main/java/module-info.java +++ b/tests/test-client/src/main/java/module-info.java @@ -7,5 +7,6 @@ exports example.github; - provides io.avaje.http.client.HttpClient.GeneratedComponent with example.github.httpclient.GeneratedHttpComponent; + provides io.avaje.http.client.HttpClient.GeneratedComponent + with example.github.httpclient.GeneratedHttpComponent, example.github.pkgprivate.PkgprivateHttpComponent; } diff --git a/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent b/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent deleted file mode 100644 index d419efe24..000000000 --- a/tests/test-client/src/main/resources/META-INF/services/io.avaje.http.client.HttpClient$GeneratedComponent +++ /dev/null @@ -1 +0,0 @@ -example.github.httpclient.GeneratedHttpComponent \ No newline at end of file diff --git a/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java b/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java new file mode 100644 index 000000000..449e82350 --- /dev/null +++ b/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java @@ -0,0 +1,44 @@ +package example.github.pkgprivate; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.avaje.http.client.HttpClient; +import io.avaje.jex.Jex; + +class PkgPrivateTest { + + static Jex.Server server = null; + String url = "http://localhost:" + server.port(); + + @BeforeAll + static void startServer() { + server = Jex.create().get("/private", ctx -> ctx.text("private")).port(0).start(); + } + + @AfterAll + static void stop() { + server.shutdown(); + } + + @Test + void test_create() { + final HttpClient client = HttpClient.builder().baseUrl("https://api.github.com").build(); + + final var simple = client.create(SimplePkgPrivate.class); + assertThat(simple).isNotNull(); + } + + @Test + void test_pkg_private() { + final HttpClient client = HttpClient.builder().baseUrl(url).build(); + + final var simple = client.create(SimplePkgPrivate.class); + + final var result = simple.get(); + assertThat(result).isNotEmpty(); + } +} From bcfa30c39802173459ecb049d5f9749a0ba588f3 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 26 Jan 2025 22:34:40 +1300 Subject: [PATCH 5/6] Format only changes --- .../generator/client/ClientProcessor.java | 19 +++---------- .../generator/client/ComponentReader.java | 27 ++++++++----------- .../github/pkgprivate/PkgPrivateTest.java | 5 ++-- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java index 00ae30c98..9b28b0954 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java @@ -35,13 +35,9 @@ public class ClientProcessor extends AbstractProcessor { private final ComponentMetaData metaData = new ComponentMetaData(); - private final Map privateMetaData = new HashMap<>(); - private boolean useJsonB; - private SimpleComponentWriter componentWriter; - private boolean readModuleInfo; @Override @@ -101,13 +97,11 @@ private void writeClient(Element controller) { final ControllerReader reader = new ControllerReader((TypeElement) controller); reader.read(false); try { - var packagePrivate = - !controller.getModifiers().contains(Modifier.PUBLIC) - && ClientPrism.isPresent(controller); + !controller.getModifiers().contains(Modifier.PUBLIC) + && ClientPrism.isPresent(controller); if (packagePrivate) { - var packageName = - APContext.elements().getPackageOf(controller).getQualifiedName().toString(); + var packageName = APContext.elements().getPackageOf(controller).getQualifiedName().toString(); var meta = privateMetaData.computeIfAbsent(packageName, k -> new ComponentMetaData()); meta.add(writeClientAdapter(reader, true)); } else { @@ -127,19 +121,15 @@ protected String writeClientAdapter(ControllerReader reader, boolean packagePriv private void writeComponent(boolean processingOver) { if (processingOver) { - try { - if (!metaData.all().isEmpty()) { ProcessingContext.addClientComponent(metaData.fullName()); - componentWriter.init(); componentWriter.write(); } for (var meta : privateMetaData.values()) { ProcessingContext.addClientComponent(meta.fullName()); - var writer = new SimpleComponentWriter(meta); writer.init(); writer.write(); @@ -156,15 +146,12 @@ void writeMetaInf() throws IOException { final FileObject fileObject = createMetaInfWriter(Constants.META_INF_COMPONENT); if (fileObject != null) { try (var fileWriter = fileObject.openWriter()) { - if (!metaData.all().isEmpty()) { - fileWriter.write(metaData.fullName()); fileWriter.write("\n"); } for (var meta : privateMetaData.values()) { - fileWriter.write(meta.fullName()); fileWriter.write("\n"); } diff --git a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java index 37278d7f9..086103fff 100644 --- a/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java +++ b/http-generator-client/src/main/java/io/avaje/http/generator/client/ComponentReader.java @@ -37,31 +37,28 @@ final class ComponentReader { } void read() { - for (String fqn : loadMetaInf()) { - final TypeElement moduleType = typeElement(fqn); if (moduleType != null) { var adapters = - MetaDataPrism.getInstanceOn(moduleType).value().stream() - .map(APContext::asTypeElement) - .collect(toList()); + MetaDataPrism.getInstanceOn(moduleType).value().stream() + .map(APContext::asTypeElement) + .collect(toList()); if (adapters.get(0).getModifiers().contains(Modifier.PUBLIC)) { componentMetaData.setFullName(fqn); adapters.stream() - .map(TypeElement::getQualifiedName) - .map(Object::toString) - .forEach(componentMetaData::add); + .map(TypeElement::getQualifiedName) + .map(Object::toString) + .forEach(componentMetaData::add); } else { - var packageName = - APContext.elements().getPackageOf(moduleType).getQualifiedName().toString(); + var packageName = APContext.elements().getPackageOf(moduleType).getQualifiedName().toString(); var meta = privateMetaData.computeIfAbsent(packageName, k -> new ComponentMetaData()); adapters.stream() - .map(TypeElement::getQualifiedName) - .map(Object::toString) - .forEach(meta::add); + .map(TypeElement::getQualifiedName) + .map(Object::toString) + .forEach(meta::add); } } } @@ -69,9 +66,7 @@ void read() { private List loadMetaInf() { try { - final FileObject fileObject = - filer().getResource(StandardLocation.CLASS_OUTPUT, "", Constants.META_INF_COMPONENT); - + final FileObject fileObject = filer().getResource(StandardLocation.CLASS_OUTPUT, "", Constants.META_INF_COMPONENT); if (fileObject != null) { final List lines = new ArrayList<>(); final Reader reader = fileObject.openReader(true); diff --git a/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java b/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java index 449e82350..54ce01373 100644 --- a/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java +++ b/tests/test-client/src/test/java/example/github/pkgprivate/PkgPrivateTest.java @@ -16,7 +16,7 @@ class PkgPrivateTest { @BeforeAll static void startServer() { - server = Jex.create().get("/private", ctx -> ctx.text("private")).port(0).start(); + server = Jex.create().get("/private", ctx -> ctx.text("myPrivateResult")).port(0).start(); } @AfterAll @@ -37,8 +37,7 @@ void test_pkg_private() { final HttpClient client = HttpClient.builder().baseUrl(url).build(); final var simple = client.create(SimplePkgPrivate.class); - final var result = simple.get(); - assertThat(result).isNotEmpty(); + assertThat(result).isEqualTo("myPrivateResult"); } } From 1b6bd23ddf4e1a643cc44603a3242b2e8c503ab0 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 26 Jan 2025 22:40:32 +1300 Subject: [PATCH 6/6] Add some unit tests for ComponentMetaData.name() --- .../client/ComponentMetaDataTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 http-generator-client/src/test/java/io/avaje/http/generator/client/ComponentMetaDataTest.java diff --git a/http-generator-client/src/test/java/io/avaje/http/generator/client/ComponentMetaDataTest.java b/http-generator-client/src/test/java/io/avaje/http/generator/client/ComponentMetaDataTest.java new file mode 100644 index 000000000..116cbdf60 --- /dev/null +++ b/http-generator-client/src/test/java/io/avaje/http/generator/client/ComponentMetaDataTest.java @@ -0,0 +1,18 @@ +package io.avaje.http.generator.client; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ComponentMetaDataTest { + + @Test + void name() { + assertThat(ComponentMetaData.name(null)).isNull(); + assertThat(ComponentMetaData.name("org.foo")).isEqualTo("Foo"); + assertThat(ComponentMetaData.name("org.fooBar")).isEqualTo("FooBar"); + assertThat(ComponentMetaData.name("org.FooBar")).isEqualTo("FooBar"); + assertThat(ComponentMetaData.name("org.FooBarHttpclient")).isEqualTo("FooBarGenerated"); + assertThat(ComponentMetaData.name("org.FooBarHttpclientAgainHttpclient")).isEqualTo("FooBarGeneratedAgainHttpclient"); + } +}