[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>();
}