[BUG][JAVA][OKHTTP] UnsupportedOperationException when using okhhtpclient with gzip feature enabled
Created by: hatzlj
Description
When instantiating a java/okhttp based client (generated with 'useGzipFeature' enabled) with parameters for oauth authentication, the constructor throws a java.lang.UnsupportedOperationException
because the generated ApiClient tries to add the GzipRequestInterceptor' to an Unmodifiable Collection of the 'OkHttpClient
.
The error originates in the generated code of the ApiClient during initialization, because httpClient.interceptors().add(...)
is called after httpClient = builder.build();
:
private void init() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(getProgressInterceptor());
builder.addInterceptor(new GzipRequestInterceptor());
httpClient = builder.build();
// Enable gzip request compression
httpClient.interceptors().add(new GzipRequestInterceptor());
verifyingSsl = true;
json = new JSON();
// Set default User-Agent.
setUserAgent("OpenAPI-Generator/0.2.1-SNAPSHOT/java");
authentications = new HashMap<String, Authentication>();
}
openapi-generator version
4.0.0
OpenAPI declaration file content or url
openapi: 3.0.2
info:
version: 0.2.1
title: My Api
servers:
- url: 'https://my.domain.org'
security:
- myAuth:
- 'api:access'
paths:
'/api/v1/test':
post:
summary: Create a new object
description: >-
Creates a new object
operationId: createObject
tags:
- command
- object
requestBody:
$ref: '#/components/requestBodies/Object.JsonApi.v1'
responses:
'202':
description: 'Object is being created'
default:
$ref: '#/components/responses/Error.JsonApi.v1'
components:
# ------------------------------------------------------------------------- SCHEMAS
schemas:
# ------------------------------------------------------------------------- BASE SCHEMAS
Object:
description: 'An object'
type: object
required:
- type
properties:
id:
type: string
description: 'The domain object identifier'
example: 'c24c0bfb-7a15-4f17-94e2-9b112491b882'
type:
type: string
readOnly: true
description: 'The type of the domain object'
default: 'my.api.model.Object'
Object.JsonApi.v1:
description: 'Object details, JSON:API v1.0'
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Object'
ErrorMetadata:
# only used in responses for now, no need to declare required properties
description: 'Basic error object, JSON:API v1.0'
type: object
properties:
code:
type: string
description: 'Application specific error code'
readOnly: true
example: 'ERR_0011'
status:
type: string
description: 'The HTTP status code applicable to this problem'
readOnly: true
example: '400'
detail:
type: string
description: 'A verbose, human readable description of the error, may be localized'
readOnly: true
example: 'The validation has failed due to a missing parameter.'
# ------------------------------------------------------------------------- RESPONSES
responses:
Error.JsonApi.v1:
description: Unexpected, unspecified error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMetadata'
example:
errors:
- code: 'ERR_0500'
status: '500'
details: "Something happened, but we cannot determine what exactly."
# ------------------------------------------------------------------------- REQUEST BODIES
requestBodies:
Object.JsonApi.v1:
description: Object
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Object.JsonApi.v1'
# ------------------------------------------------------------------------- SECURITY SCHEMES
securitySchemes:
myAuth:
type: oauth2
flows:
password:
tokenUrl: /api/v1/oauth2/token
refreshUrl: /api/v1/oauth2/token
scopes:
api:access: "Access My REST API"
Command line used for generation
used via the openapi-generator-maven-plugin
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.0.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<verbose>false</verbose>
<inputSpec>minimal.yaml</inputSpec>
<skipValidateSpec>false</skipValidateSpec>
<generatorName>java</generatorName>
<output>${project.build.directory}/generated-sources/openapi</output>
<addCompileSourceRoot>true</addCompileSourceRoot>
<apiPackage>my.api.client</apiPackage>
<modelPackage>my.api.model</modelPackage>
<modelNamePrefix>DTO</modelNamePrefix>
<generateApis>true</generateApis>
<generateApiTests>false</generateApiTests>
<generateApiDocumentation>true</generateApiDocumentation>
<generateModels>true</generateModels>
<generateModelTests>false</generateModelTests>
<generateModelDocumentation>true</generateModelDocumentation>
<generateSupportingFiles>true</generateSupportingFiles>
<groupId>my.api</groupId>
<artifactId>client</artifactId>
<artifactVersion>${project.version}</artifactVersion>
<withXml>false</withXml>
<configOptions>
<library>okhttp-gson</library>
<title>${project.name}</title>
<fullJavaUtil>false</fullJavaUtil>
<sourceFolder>src/main/java</sourceFolder>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<booleanGetterPrefix>is</booleanGetterPrefix>
<useBeanValidation>true</useBeanValidation>
<performBeanValidation>true</performBeanValidation>
<useGzipFeature>true</useGzipFeature>
<caseInsensitiveResponseHeaders>true</caseInsensitiveResponseHeaders>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Steps to reproduce
- generate the client with the sample file (oauth authentication included)
- instantiate a client by
String clientId = "myClientId";
String clientSecret = "myClientSecret";
String username = "myUsername";
String password = "myPasswd";
Map<String, String> params = new HashMap<>();
params.put("username", username);
params.put("password", password);
ApiClient client = new ApiClient(clientId, clientSecret, params);
acutal output: java.lang.UnsupportedOperationException
expected output: client is instantiated with specified parameters for OAuth
Related issues/PRs
see also https://github.com/square/okhttp/issues/2219
Suggest a fix
fix the ApiClient Mustache Template in line 147 to add the GzipRequestInterceptor before the builder is built
private void init() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(getProgressInterceptor());
// Enable gzip request compression (MOVED HERE)
builder.addInterceptor(new GzipRequestInterceptor());
httpClient = builder.build();
// gzip request compression MOVED FROM HERE
verifyingSsl = true;
json = new JSON();
// Set default User-Agent.
setUserAgent("OpenAPI-Generator/0.2.1-SNAPSHOT/java");
authentications = new HashMap<String, Authentication>();
}