Skip to content

Commit 2ce27b4

Browse files
committed
working
1 parent 71d2701 commit 2ce27b4

File tree

15 files changed

+363
-78
lines changed

15 files changed

+363
-78
lines changed

http-api/src/main/java/io/avaje/http/api/InstrumentServerContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* @Get
1919
* @InstrumentServerContext
2020
* void helloWorld(long id) {
21-
* Server resolver.currentRequest()
21+
* Context = resolver.currentRequest()
2222
* ...
2323
* }
2424
*

http-client/src/main/java/io/avaje/http/client/DHttpApi.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.avaje.http.client;
22

33
import io.avaje.applog.AppLog;
4+
import io.avaje.http.client.HttpClient.GeneratedComponent;
45

56
import java.util.HashMap;
67
import java.util.Map;
@@ -19,20 +20,21 @@ final class DHttpApi {
1920

2021
private final Map<Class<?>, HttpApiProvider<?>> providerMap = new HashMap<>();
2122

22-
DHttpApi() {
23+
private DHttpApi() {
2324
init();
2425
}
2526

2627
@SuppressWarnings("rawtypes")
2728
void init() {
28-
for (HttpApiProvider apiProvider : ServiceLoader.load(HttpApiProvider.class)) {
29-
addProvider(apiProvider);
29+
for (final HttpApiProvider apiProvider : ServiceLoader.load(HttpApiProvider.class)) {
30+
providerMap.put(apiProvider.type(), apiProvider);
3031
}
31-
log.log(DEBUG, "providers for {0}", providerMap.keySet());
32-
}
3332

34-
void addProvider(HttpApiProvider<?> apiProvider) {
35-
providerMap.put(apiProvider.type(), apiProvider);
33+
for (final GeneratedComponent apiProvider : ServiceLoader.load(GeneratedComponent.class)) {
34+
apiProvider.register(providerMap);
35+
}
36+
37+
log.log(DEBUG, "providers for {0}", providerMap.keySet());
3638
}
3739

3840
@SuppressWarnings("unchecked")

http-client/src/main/java/io/avaje/http/client/DHttpClientContext.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,43 @@ public <T> T create(Class<T> clientInterface) {
5858
if (!clientInterface.isInterface()) {
5959
throw new IllegalArgumentException("API declarations must be interfaces.");
6060
}
61-
HttpApiProvider<T> apiProvider = DHttpApi.get(clientInterface);
61+
final HttpApiProvider<T> apiProvider = DHttpApi.get(clientInterface);
6262
if (apiProvider != null) {
6363
return apiProvider.provide(this);
6464
}
6565
try {
66-
Class<?> implementationClass = implementationClass(clientInterface);
67-
Constructor<?> constructor = implementationClass.getConstructor(HttpClientContext.class);
66+
final Class<?> implementationClass = implementationClass(clientInterface);
67+
final Constructor<?> constructor = implementationClass.getConstructor(HttpClient.class);
6868
return (T) constructor.newInstance(this);
69-
} catch (Exception e) {
70-
String cn = implementationClassName(clientInterface, "HttpClient");
69+
} catch (final Exception e) {
70+
return constructReflectively(clientInterface);
71+
}
72+
}
73+
74+
@SuppressWarnings("unchecked")
75+
private <T> T constructReflectively(Class<T> clientInterface) {
76+
try {
77+
final Class<?> implementationClass = implementationClass(clientInterface);
78+
final Constructor<?> constructor = implementationClass.getConstructor(HttpClientContext.class);
79+
return (T) constructor.newInstance(this);
80+
} catch (final Exception e) {
81+
final String cn = implementationClassName(clientInterface, "HttpClient");
7182
throw new IllegalStateException("Failed to create http client service " + cn, e);
7283
}
7384
}
7485

7586
private Class<?> implementationClass(Class<?> clientInterface) throws ClassNotFoundException {
7687
try {
7788
return Class.forName(implementationClassName(clientInterface, "HttpClient"));
78-
} catch (ClassNotFoundException e) {
89+
} catch (final ClassNotFoundException e) {
7990
// try the older generated client suffix
8091
return Class.forName(implementationClassName(clientInterface, "$HttpClient"));
8192
}
8293
}
8394

8495
private <T> String implementationClassName(Class<T> clientInterface, String suffix) {
85-
String packageName = clientInterface.getPackageName();
86-
String simpleName = clientInterface.getSimpleName();
96+
final String packageName = clientInterface.getPackageName();
97+
final String simpleName = clientInterface.getSimpleName();
8798
return packageName + ".httpclient." + simpleName + suffix;
8899
}
89100

@@ -202,7 +213,7 @@ public BodyContent readErrorContent(boolean responseAsBytes, HttpResponse<?> htt
202213
if (body instanceof String) {
203214
return new BodyContent(contentType, ((String) body).getBytes(StandardCharsets.UTF_8));
204215
}
205-
String type = (body == null) ? "null" : body.getClass().toString();
216+
final String type = (body == null) ? "null" : body.getClass().toString();
206217
throw new IllegalStateException("Unable to translate response body to bytes? Maybe use HttpResponse directly instead? Response body type: " + type);
207218
}
208219

@@ -212,7 +223,7 @@ public BodyContent readContent(HttpResponse<byte[]> httpResponse) {
212223
if (body != null && body.length > 0) {
213224
metricResBytes.add(body.length);
214225
}
215-
byte[] bodyBytes = decodeContent(httpResponse);
226+
final byte[] bodyBytes = decodeContent(httpResponse);
216227
final String contentType = getContentType(httpResponse);
217228
return new BodyContent(contentType, bodyBytes);
218229
}
@@ -227,21 +238,22 @@ String getContentEncoding(HttpResponse<?> httpResponse) {
227238

228239
@Override
229240
public byte[] decodeContent(String encoding, byte[] body) {
230-
if (encoding.equals("gzip")) {
241+
if ("gzip".equals(encoding)) {
231242
return GzipUtil.gzipDecode(body);
232243
}
233244
// todo: register decoders with context and use them
234245
return body;
235246
}
236247

237-
public byte[] decodeContent(HttpResponse<byte[]> httpResponse) {
238-
String encoding = getContentEncoding(httpResponse);
248+
@Override
249+
public byte[] decodeContent(HttpResponse<byte[]> httpResponse) {
250+
final String encoding = getContentEncoding(httpResponse);
239251
return encoding == null ? httpResponse.body() : decodeContent(encoding, httpResponse.body());
240252
}
241253

242254
String firstHeader(HttpHeaders headers, String... names) {
243255
final Map<String, List<String>> map = headers.map();
244-
for (String key : names) {
256+
for (final String key : names) {
245257
final List<String> values = map.get(key);
246258
if (values != null && !values.isEmpty()) {
247259
return values.get(0);
@@ -253,9 +265,9 @@ String firstHeader(HttpHeaders headers, String... names) {
253265
<T> HttpResponse<T> send(HttpRequest.Builder requestBuilder, HttpResponse.BodyHandler<T> bodyHandler) {
254266
try {
255267
return httpClient.send(requestBuilder.build(), bodyHandler);
256-
} catch (IOException e) {
268+
} catch (final IOException e) {
257269
throw new HttpException(499, e);
258-
} catch (InterruptedException e) {
270+
} catch (final InterruptedException e) {
259271
Thread.currentThread().interrupt();
260272
throw new HttpException(499, e);
261273
}
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package io.avaje.http.client;
22

3+
import java.util.Map;
4+
35
/**
46
* Provides http client implementations for an interface.
57
*
68
* @param <T> The interface type
79
*/
10+
@FunctionalInterface
811
public interface HttpApiProvider<T> {
912

10-
/**
11-
* Return the interface type this API implements.
12-
*/
13-
Class<T> type();
13+
/** Return the interface type this API implements. */
14+
default Class<T> type() {
15+
throw new UnsupportedOperationException();
16+
}
1417

15-
/**
16-
* Return the provided implementation of the API.
17-
*/
18+
/** Return the provided implementation of the API. */
1819
T provide(HttpClient client);
19-
2020
}

http-client/src/main/java/io/avaje/http/client/HttpClient.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package io.avaje.http.client;
22

3-
import io.avaje.inject.BeanScope;
4-
5-
import javax.net.ssl.SSLContext;
6-
import javax.net.ssl.SSLParameters;
73
import java.net.Authenticator;
84
import java.net.CookieHandler;
95
import java.net.ProxySelector;
106
import java.time.Duration;
7+
import java.util.Map;
118
import java.util.concurrent.Executor;
129

10+
import javax.net.ssl.SSLContext;
11+
import javax.net.ssl.SSLParameters;
12+
13+
import io.avaje.inject.BeanScope;
14+
1315
/**
1416
* The HTTP client context that we use to build and process requests.
1517
*
@@ -356,4 +358,11 @@ interface State {
356358
interface Metrics extends HttpClientContext.Metrics {
357359

358360
}
361+
362+
/** Components register Generated Client interface Providers */
363+
@FunctionalInterface
364+
interface GeneratedComponent {
365+
366+
void register(Map<Class<?>, HttpApiProvider<?>> providerMap);
367+
}
359368
}

http-client/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module io.avaje.http.client {
22

33
uses io.avaje.http.client.HttpApiProvider;
4+
uses io.avaje.http.client.HttpClient.GeneratedComponent;
45

56
requires transitive java.net.http;
67
requires transitive io.avaje.applog;

http-generator-client/pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121
<version>${project.version}</version>
2222
</dependency>
2323

24+
<dependency>
25+
<groupId>io.avaje</groupId>
26+
<artifactId>avaje-http-client</artifactId>
27+
<scope>test</scope>
28+
<version>${project.version}</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>io.avaje</groupId>
32+
<artifactId>avaje-http-api</artifactId>
33+
<scope>test</scope>
34+
<version>${project.version}</version>
35+
</dependency>
36+
2437
</dependencies>
2538

2639
<build>
@@ -36,6 +49,14 @@
3649
<compilerArgument>-proc:none</compilerArgument>
3750
</configuration>
3851
</plugin>
52+
<plugin>
53+
<groupId>org.apache.maven.plugins</groupId>
54+
<artifactId>maven-surefire-plugin</artifactId>
55+
<version>${maven-surefire-plugin.version}</version>
56+
<configuration>
57+
<useModulePath>false</useModulePath>
58+
</configuration>
59+
</plugin>
3960
</plugins>
4061
</build>
4162

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.avaje.http.generator.client;
22

33
import static io.avaje.http.generator.core.ProcessingContext.*;
4+
import static io.avaje.http.generator.core.ProcessingContext.platform;
5+
import static io.avaje.http.generator.core.ProcessingContext.setPlatform;
6+
import static io.avaje.http.generator.core.ProcessingContext.typeElement;
7+
48
import java.io.IOException;
5-
import java.io.Writer;
69
import java.util.LinkedHashSet;
710
import java.util.Objects;
811
import java.util.Set;
@@ -14,7 +17,6 @@
1417
import javax.lang.model.SourceVersion;
1518
import javax.lang.model.element.Element;
1619
import javax.lang.model.element.TypeElement;
17-
import javax.tools.FileObject;
1820

1921
import io.avaje.http.generator.core.ClientPrism;
2022
import io.avaje.http.generator.core.ControllerReader;
@@ -25,8 +27,6 @@
2527
@SupportedAnnotationTypes({ClientPrism.PRISM_TYPE, ImportPrism.PRISM_TYPE})
2628
public class ClientProcessor extends AbstractProcessor {
2729

28-
private static final String METAINF_SERVICES_PROVIDER = "META-INF/services/io.avaje.http.client.HttpApiProvider";
29-
3030
private final Set<String> generatedClients = new LinkedHashSet<>();
3131

3232
private final boolean useJsonB;
@@ -61,29 +61,16 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
6161
for (final Element controller : round.getElementsAnnotatedWith(typeElement(ClientPrism.PRISM_TYPE))) {
6262
writeClient(controller);
6363
}
64-
for (final Element importedElement : round.getElementsAnnotatedWith(typeElement(ImportPrism.PRISM_TYPE))) {
64+
for (final var importedElement : round.getElementsAnnotatedWith(typeElement(ImportPrism.PRISM_TYPE))) {
6565
writeForImported(importedElement);
6666
}
67-
if (round.processingOver()) {
68-
writeServicesFile();
69-
}
67+
68+
writeComponent(round.processingOver());
69+
7070
setPlatform(platform);
7171
return false;
7272
}
7373

74-
private void writeServicesFile() {
75-
try {
76-
final FileObject metaInfWriter = createMetaInfWriter(METAINF_SERVICES_PROVIDER);
77-
final Writer writer = metaInfWriter.openWriter();
78-
for (String generatedClient : generatedClients) {
79-
writer.append(generatedClient).append("$Provider\n");
80-
}
81-
writer.close();
82-
} catch (IOException e) {
83-
logError(null, "Error writing services file " + e, e);
84-
}
85-
}
86-
8774
private void writeForImported(Element importedElement) {
8875
ImportPrism.getInstanceOn(importedElement).types().stream()
8976
.map(ProcessingContext::asElement)
@@ -93,11 +80,11 @@ private void writeForImported(Element importedElement) {
9380

9481
private void writeClient(Element controller) {
9582
if (controller instanceof TypeElement) {
96-
ControllerReader reader = new ControllerReader((TypeElement) controller);
83+
final ControllerReader reader = new ControllerReader((TypeElement) controller);
9784
reader.read(false);
9885
try {
9986
generatedClients.add(writeClientAdapter(reader));
100-
} catch (Throwable e) {
87+
} catch (final Throwable e) {
10188
e.printStackTrace();
10289
logError(reader.beanType(), "Failed to write client class " + e);
10390
}
@@ -108,4 +95,13 @@ protected String writeClientAdapter(ControllerReader reader) throws IOException
10895
return new ClientWriter(reader, useJsonB).write();
10996
}
11097

98+
private void writeComponent(boolean processingOver) {
99+
if (processingOver) {
100+
try {
101+
new SimpleComponentWriter(generatedClients).write();
102+
} catch (final IOException e) {
103+
logError("Error writing component", e);
104+
}
105+
}
106+
}
111107
}

0 commit comments

Comments
 (0)