Skip to content

Commit 49ecd01

Browse files
[java][restclient][webclient] add an exception provider interface to allow for easy exception redefinition
1 parent 91fa74a commit 49ecd01

70 files changed

Lines changed: 2731 additions & 253 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ public void processOpts() {
720720
// one by one for each library.
721721
supportsAdditionalPropertiesWithComposedSchema = true;
722722
} else if (libWebClient) {
723+
supportingFiles.add(new SupportingFile("ExceptionProvider.mustache", invokerFolder, "ExceptionProvider.java"));
723724
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);
724725

725726
// Composed schemas can have the 'additionalProperties' keyword, as specified in JSON schema.
@@ -729,6 +730,7 @@ public void processOpts() {
729730
// one by one for each library.
730731
supportsAdditionalPropertiesWithComposedSchema = true;
731732
} else if (libRestClient) {
733+
supportingFiles.add(new SupportingFile("ExceptionProvider.mustache", invokerFolder, "ExceptionProvider.java"));
732734
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);
733735
applyJakartaPackage();
734736

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{{>licenseInfo}}
2+
3+
package {{invokerPackage}};
4+
5+
import org.springframework.web.client.RestClientException;
6+
import java.text.ParseException;
7+
8+
/**
9+
* Extension point for customizing exceptions thrown by the generated API client.
10+
* <p>
11+
* The default implementation throws {@link RuntimeException}. To use custom exception
12+
* types, implement this interface and pass the instance to
13+
* {@link ApiClient#setExceptionProvider(ExceptionProvider)}.
14+
* </p>
15+
* <pre>{@code
16+
* apiClient.setExceptionProvider(new ExceptionProvider() {
17+
* public RuntimeException bearerAuthException() {
18+
* return new MyAuthException("No Bearer authentication configured!");
19+
* }
20+
* });
21+
* }</pre>
22+
*/
23+
{{>generatedAnnotation}}
24+
25+
public interface ExceptionProvider {
26+
27+
/** Returns an exception indicating that no Bearer authentication is configured. */
28+
default RuntimeException bearerAuthException() {
29+
return new RuntimeException("No Bearer authentication configured!");
30+
}
31+
32+
/** Returns an exception indicating that no HTTP basic authentication is configured. */
33+
default RuntimeException httpBasicAuthException() {
34+
return new RuntimeException("No HTTP basic authentication configured!");
35+
}
36+
37+
/** Returns an exception indicating that no API key authentication is configured. */
38+
default RuntimeException apiKeyAuthException() {
39+
return new RuntimeException("No API key authentication configured!");
40+
}
41+
42+
/** Returns an exception indicating that no OAuth2 authentication is configured. */
43+
default RuntimeException oAuth2Exception() {
44+
return new RuntimeException("No OAuth2 authentication configured!");
45+
}
46+
47+
/** Wraps a date/time parse exception. */
48+
default RuntimeException dateTimeException(ParseException e) {
49+
return new RuntimeException(e);
50+
}
51+
52+
/** Wraps a Jackson serialization/deserialization exception. */
53+
default RuntimeException jacksonException(Exception e) {
54+
return new RuntimeException(e);
55+
}
56+
57+
/**
58+
* Returns an exception indicating that the requested authentication scheme is not configured.
59+
*
60+
* @param authName the name of the authentication scheme that could not be found
61+
* @return a {@link RestClientException} describing the missing authentication
62+
*/
63+
default RestClientException undefinedAuthenticationException(String authName) {
64+
return new RestClientException("Authentication undefined: " + authName);
65+
}
66+
67+
/** The default {@link ExceptionProvider} instance. */
68+
ExceptionProvider DEFAULT = new ExceptionProvider() {};
69+
70+
}

modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
154154
{{/useJackson3}}
155155
protected Map<String, Authentication> authentications;
156156

157+
/**
158+
* The {@link ExceptionProvider} used to create exceptions thrown by this client.
159+
* Defaults to {@link ExceptionProvider#DEFAULT}. Can be replaced to customize the exceptions
160+
* thrown by this client by calling {@link #setExceptionProvider(ExceptionProvider)}.
161+
*/
162+
protected ExceptionProvider exceptionProvider = ExceptionProvider.DEFAULT;
163+
157164

158165
public ApiClient() {
159166
this(null);
@@ -361,6 +368,25 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
361368
return authentications;
362369
}
363370

371+
/**
372+
* Get the current {@link ExceptionProvider}.
373+
* @return the exception provider
374+
*/
375+
public ExceptionProvider getExceptionProvider() {
376+
return exceptionProvider;
377+
}
378+
379+
/**
380+
* Set a custom {@link ExceptionProvider} to control which exception types are thrown
381+
* by this client.
382+
* @param exceptionProvider the exception provider
383+
* @return this client instance
384+
*/
385+
public ApiClient setExceptionProvider(ExceptionProvider exceptionProvider) {
386+
this.exceptionProvider = exceptionProvider;
387+
return this;
388+
}
389+
364390
/**
365391
* Get authentication for the given name.
366392
*
@@ -382,7 +408,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
382408
return;
383409
}
384410
}
385-
throw new RuntimeException("No Bearer authentication configured!");
411+
throw exceptionProvider.bearerAuthException();
386412
}
387413

388414
/**
@@ -397,7 +423,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
397423
return;
398424
}
399425
}
400-
throw new RuntimeException("No Bearer authentication configured!");
426+
throw exceptionProvider.bearerAuthException();
401427
}
402428

403429
/**
@@ -411,7 +437,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
411437
return;
412438
}
413439
}
414-
throw new RuntimeException("No HTTP basic authentication configured!");
440+
throw exceptionProvider.httpBasicAuthException();
415441
}
416442

417443
/**
@@ -425,7 +451,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
425451
return;
426452
}
427453
}
428-
throw new RuntimeException("No HTTP basic authentication configured!");
454+
throw exceptionProvider.httpBasicAuthException();
429455
}
430456

431457
/**
@@ -439,7 +465,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
439465
return;
440466
}
441467
}
442-
throw new RuntimeException("No API key authentication configured!");
468+
throw exceptionProvider.apiKeyAuthException();
443469
}
444470

445471
/**
@@ -453,7 +479,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
453479
return;
454480
}
455481
}
456-
throw new RuntimeException("No API key authentication configured!");
482+
throw exceptionProvider.apiKeyAuthException();
457483
}
458484

459485
{{#hasOAuthMethods}}
@@ -478,7 +504,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
478504
return this;
479505
}
480506
}
481-
throw new RuntimeException("No OAuth2 authentication configured!");
507+
throw exceptionProvider.oAuth2Exception();
482508
}
483509

484510
{{/hasOAuthMethods}}
@@ -534,7 +560,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
534560
try {
535561
return dateFormat.parse(str);
536562
} catch (ParseException e) {
537-
throw new RuntimeException(e);
563+
throw exceptionProvider.dateTimeException(e);
538564
}
539565
}
540566

@@ -602,7 +628,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
602628
try {
603629
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
604630
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
605-
throw new RuntimeException(e);
631+
throw exceptionProvider.jacksonException(e);
606632
}
607633
}
608634

@@ -611,7 +637,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
611637
try {
612638
values.add(mapper.writeValueAsString(o));
613639
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
614-
throw new RuntimeException(e);
640+
throw exceptionProvider.jacksonException(e);
615641
}
616642
}
617643
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
@@ -945,7 +971,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
945971
for (String authName : authNames) {
946972
Authentication auth = authentications.get(authName);
947973
if (auth == null) {
948-
throw new RestClientException("Authentication undefined: " + authName);
974+
throw exceptionProvider.undefinedAuthenticationException(authName);
949975
}
950976
auth.applyToParams(queryParams, headerParams, cookieParams);
951977
}

modules/openapi-generator/src/main/resources/Java/libraries/webclient/ApiClient.mustache

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
133133

134134
protected Map<String, Authentication> authentications;
135135

136+
/**
137+
* The {@link ExceptionProvider} used to create exceptions thrown by this client.
138+
* Defaults to {@link ExceptionProvider#DEFAULT}. Can be replaced to customize the exceptions
139+
* thrown by this client by calling {@link #setExceptionProvider(ExceptionProvider)}.
140+
*/
141+
protected ExceptionProvider exceptionProvider = ExceptionProvider.DEFAULT;
142+
136143

137144
public ApiClient() {
138145
this.dateFormat = createDefaultDateFormat();
@@ -287,6 +294,25 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
287294
return authentications;
288295
}
289296

297+
/**
298+
* Get the current {@link ExceptionProvider}.
299+
* @return the exception provider
300+
*/
301+
public ExceptionProvider getExceptionProvider() {
302+
return exceptionProvider;
303+
}
304+
305+
/**
306+
* Set a custom {@link ExceptionProvider} to control which exception types are thrown
307+
* by this client.
308+
* @param exceptionProvider the exception provider
309+
* @return this client instance
310+
*/
311+
public ApiClient setExceptionProvider(ExceptionProvider exceptionProvider) {
312+
this.exceptionProvider = exceptionProvider;
313+
return this;
314+
}
315+
290316
/**
291317
* Get authentication for the given name.
292318
*
@@ -308,7 +334,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
308334
return;
309335
}
310336
}
311-
throw new RuntimeException("No Bearer authentication configured!");
337+
throw exceptionProvider.bearerAuthException();
312338
}
313339

314340
/**
@@ -322,7 +348,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
322348
return;
323349
}
324350
}
325-
throw new RuntimeException("No HTTP basic authentication configured!");
351+
throw exceptionProvider.httpBasicAuthException();
326352
}
327353

328354
/**
@@ -336,7 +362,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
336362
return;
337363
}
338364
}
339-
throw new RuntimeException("No HTTP basic authentication configured!");
365+
throw exceptionProvider.httpBasicAuthException();
340366
}
341367

342368
/**
@@ -350,7 +376,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
350376
return;
351377
}
352378
}
353-
throw new RuntimeException("No API key authentication configured!");
379+
throw exceptionProvider.apiKeyAuthException();
354380
}
355381

356382
/**
@@ -364,7 +390,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
364390
return;
365391
}
366392
}
367-
throw new RuntimeException("No API key authentication configured!");
393+
throw exceptionProvider.apiKeyAuthException();
368394
}
369395

370396
{{#hasOAuthMethods}}
@@ -379,7 +405,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
379405
return;
380406
}
381407
}
382-
throw new RuntimeException("No OAuth2 authentication configured!");
408+
throw exceptionProvider.oAuth2Exception();
383409
}
384410

385411
{{/hasOAuthMethods}}
@@ -435,7 +461,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
435461
try {
436462
return dateFormat.parse(str);
437463
} catch (ParseException e) {
438-
throw new RuntimeException(e);
464+
throw exceptionProvider.dateTimeException(e);
439465
}
440466
}
441467

@@ -502,8 +528,8 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
502528
} else {
503529
try {
504530
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
505-
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
506-
throw new RuntimeException(e);
531+
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
532+
throw exceptionProvider.jacksonException(e);
507533
}
508534
}
509535

@@ -512,7 +538,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
512538
try {
513539
values.add(mapper.writeValueAsString(o));
514540
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
515-
throw new RuntimeException(e);
541+
throw exceptionProvider.jacksonException(e);
516542
}
517543
}
518544
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
@@ -821,7 +847,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
821847
for (String authName : authNames) {
822848
Authentication auth = authentications.get(authName);
823849
if (auth == null) {
824-
throw new RestClientException("Authentication undefined: " + authName);
850+
throw exceptionProvider.undefinedAuthenticationException(authName);
825851
}
826852
auth.applyToParams(queryParams, headerParams, cookieParams);
827853
}

samples/client/echo_api/java/restclient/.openapi-generator/FILES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pom.xml
3333
settings.gradle
3434
src/main/AndroidManifest.xml
3535
src/main/java/org/openapitools/client/ApiClient.java
36+
src/main/java/org/openapitools/client/ExceptionProvider.java
3637
src/main/java/org/openapitools/client/JavaTimeFormatter.java
3738
src/main/java/org/openapitools/client/RFC3339DateFormat.java
3839
src/main/java/org/openapitools/client/RFC3339InstantDeserializer.java

0 commit comments

Comments
 (0)