Skip to content

Commit 8f01653

Browse files
[Java] [Spring Client] Add an exception provider interface to allow for easy exception redefinition
1 parent 74f7b07 commit 8f01653

70 files changed

Lines changed: 2707 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: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
public interface ExceptionProvider {
24+
25+
/** Returns an exception indicating that no Bearer authentication is configured. */
26+
default RuntimeException bearerAuthException() {
27+
return new RuntimeException("No Bearer authentication configured!");
28+
}
29+
30+
/** Returns an exception indicating that no HTTP basic authentication is configured. */
31+
default RuntimeException httpBasicAuthException() {
32+
return new RuntimeException("No HTTP basic authentication configured!");
33+
}
34+
35+
/** Returns an exception indicating that no API key authentication is configured. */
36+
default RuntimeException apiKeyAuthException() {
37+
return new RuntimeException("No API key authentication configured!");
38+
}
39+
40+
/** Returns an exception indicating that no OAuth2 authentication is configured. */
41+
default RuntimeException oAuth2Exception() {
42+
return new RuntimeException("No OAuth2 authentication configured!");
43+
}
44+
45+
/** Wraps a date/time parse exception. */
46+
default RuntimeException dateTimeException(ParseException e) {
47+
return new RuntimeException(e);
48+
}
49+
50+
/** Wraps a Jackson serialization/deserialization exception. */
51+
default RuntimeException jacksonException(Exception e) {
52+
return new RuntimeException(e);
53+
}
54+
55+
/**
56+
* Returns an exception indicating that the requested authentication scheme is not configured.
57+
*
58+
* @param authName the name of the authentication scheme that could not be found
59+
* @return a {@link RestClientException} describing the missing authentication
60+
*/
61+
default RestClientException undefinedAuthenticationException(String authName) {
62+
return new RestClientException("Authentication undefined: " + authName);
63+
}
64+
65+
/** The default {@link ExceptionProvider} instance. */
66+
ExceptionProvider DEFAULT = new ExceptionProvider() {};
67+
68+
}

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
@@ -149,6 +149,13 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
149149
{{/useJackson3}}
150150
protected Map<String, Authentication> authentications;
151151

152+
/**
153+
* The {@link ExceptionProvider} used to create exceptions thrown by this client.
154+
* Defaults to {@link ExceptionProvider#DEFAULT}. Can be replaced to customize the exceptions
155+
* thrown by this client by calling {@link #setExceptionProvider(ExceptionProvider)}.
156+
*/
157+
protected ExceptionProvider exceptionProvider = ExceptionProvider.DEFAULT;
158+
152159

153160
public ApiClient() {
154161
this(null);
@@ -351,6 +358,25 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
351358
return authentications;
352359
}
353360

361+
/**
362+
* Get the current {@link ExceptionProvider}.
363+
* @return the exception provider
364+
*/
365+
public ExceptionProvider getExceptionProvider() {
366+
return exceptionProvider;
367+
}
368+
369+
/**
370+
* Set a custom {@link ExceptionProvider} to control which exception types are thrown
371+
* by this client.
372+
* @param exceptionProvider the exception provider
373+
* @return this client instance
374+
*/
375+
public ApiClient setExceptionProvider(ExceptionProvider exceptionProvider) {
376+
this.exceptionProvider = exceptionProvider;
377+
return this;
378+
}
379+
354380
/**
355381
* Get authentication for the given name.
356382
*
@@ -372,7 +398,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
372398
return;
373399
}
374400
}
375-
throw new RuntimeException("No Bearer authentication configured!");
401+
throw exceptionProvider.bearerAuthException();
376402
}
377403

378404
/**
@@ -387,7 +413,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
387413
return;
388414
}
389415
}
390-
throw new RuntimeException("No Bearer authentication configured!");
416+
throw exceptionProvider.bearerAuthException();
391417
}
392418

393419
/**
@@ -401,7 +427,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
401427
return;
402428
}
403429
}
404-
throw new RuntimeException("No HTTP basic authentication configured!");
430+
throw exceptionProvider.httpBasicAuthException();
405431
}
406432

407433
/**
@@ -415,7 +441,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
415441
return;
416442
}
417443
}
418-
throw new RuntimeException("No HTTP basic authentication configured!");
444+
throw exceptionProvider.httpBasicAuthException();
419445
}
420446

421447
/**
@@ -429,7 +455,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
429455
return;
430456
}
431457
}
432-
throw new RuntimeException("No API key authentication configured!");
458+
throw exceptionProvider.apiKeyAuthException();
433459
}
434460

435461
/**
@@ -443,7 +469,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
443469
return;
444470
}
445471
}
446-
throw new RuntimeException("No API key authentication configured!");
472+
throw exceptionProvider.apiKeyAuthException();
447473
}
448474

449475
{{#hasOAuthMethods}}
@@ -468,7 +494,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
468494
return this;
469495
}
470496
}
471-
throw new RuntimeException("No OAuth2 authentication configured!");
497+
throw exceptionProvider.oAuth2Exception();
472498
}
473499

474500
{{/hasOAuthMethods}}
@@ -524,7 +550,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
524550
try {
525551
return dateFormat.parse(str);
526552
} catch (ParseException e) {
527-
throw new RuntimeException(e);
553+
throw exceptionProvider.dateTimeException(e);
528554
}
529555
}
530556

@@ -592,7 +618,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
592618
try {
593619
return parameterToMultiValueMap(collectionFormat, name, mapper.writeValueAsString(value));
594620
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
595-
throw new RuntimeException(e);
621+
throw exceptionProvider.jacksonException(e);
596622
}
597623
}
598624

@@ -601,7 +627,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
601627
try {
602628
values.add(mapper.writeValueAsString(o));
603629
} catch ({{#useJackson3}}JacksonException{{/useJackson3}}{{^useJackson3}}JsonProcessingException{{/useJackson3}} e) {
604-
throw new RuntimeException(e);
630+
throw exceptionProvider.jacksonException(e);
605631
}
606632
}
607633
return parameterToMultiValueMap(collectionFormat, name, "[" + StringUtils.collectionToDelimitedString(values, collectionFormat.separator) + "]");
@@ -935,7 +961,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
935961
for (String authName : authNames) {
936962
Authentication auth = authentications.get(authName);
937963
if (auth == null) {
938-
throw new RestClientException("Authentication undefined: " + authName);
964+
throw exceptionProvider.undefinedAuthenticationException(authName);
939965
}
940966
auth.applyToParams(queryParams, headerParams, cookieParams);
941967
}

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)