添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account Unable to invoke request: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/x-www-form-urlencoded type: javax.ws.rs.core.Form$1 #13667 Unable to invoke request: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/x-www-form-urlencoded type: javax.ws.rs.core.Form$1 #13667 TheGeekPharaoh opened this issue Dec 3, 2020 · 27 comments · Fixed by #13704

Describe the bug
Exception after updating project to Quarkus 1.10.2 when leveraging the Keycloak client class. Attempting to programmatically get an OIDC access token using the client fails.

Expected behavior
Calls using the Keycloak client would return the OIDC access token.

Actual behavior
An exception:

javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/x-www-form-urlencoded type: javax.ws.rs.core.Form$1
	at org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.invoke(ManualClosingApacheHttpClient43Engine.java:287)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:488)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76)
	at com.sun.proxy.$Proxy74.grantToken(Unknown Source)
	at org.keycloak.admin.client.token.TokenManager.grantToken(TokenManager.java:90)
	at org.keycloak.admin.client.token.TokenManager.getAccessToken(TokenManager.java:70)
	at org.keycloak.admin.client.token.TokenManager.getAccessTokenString(TokenManager.java:65)
	at com.myapp.jaxrs.test.AbstractAuthenticatedRestIntegrationTest.getAccessToken(AbstractAuthenticatedRestIntegrationTest.java:122)
	at com.myapp.jaxrs.collab.test.AbstractCommunityTest.createGlobalTestCommunity(AbstractCommunityTest.java:60)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:807)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptBeforeAllMethod(QuarkusTestExtension.java:621)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:126)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeAllMethod(TimeoutExtension.java:68)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllMethods$9(ClassBasedTestDescriptor.java:384)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllMethods(ClassBasedTestDescriptor.java:382)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:196)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:78)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:136)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:84)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/x-www-form-urlencoded type: javax.ws.rs.core.Form$1
	at org.jboss.resteasy.core.interception.jaxrs.ClientWriterInterceptorContext.throwWriterNotFoundException(ClientWriterInterceptorContext.java:50)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.getWriter(AbstractWriterInterceptorContext.java:302)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.syncProceed(AbstractWriterInterceptorContext.java:240)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:224)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.writeRequestBody(ClientInvocation.java:440)
	at org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.writeRequestBodyToOutputStream(ManualClosingApacheHttpClient43Engine.java:589)
	at org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.buildEntity(ManualClosingApacheHttpClient43Engine.java:548)
	at org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.loadHttpMethod(ManualClosingApacheHttpClient43Engine.java:455)
	at org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.invoke(ManualClosingApacheHttpClient43Engine.java:265)
	... 69 more

To Reproduce

		Keycloak kc = KeycloakBuilder.builder().serverUrl(oidcClient.getAuthServerURL().toExternalForm())
				.realm(oidcClient.getRealm()).clientId(oidcClient.getClientID())
				.clientSecret(oidcClient.getCredentials().getClientSecret()).username(username).password(password)
				.build();
		assertNotNull(kc);
		String accessToken = kc.tokenManager().getAccessTokenString();

Configuration

Quarkus 1.10.2, Keycloak 11.0.3

Environment (please complete the following information):
Quarkus 1.10.2, Keycloak 11.0.3

/cc @asoldano we had several reports of similar issues. We suspect a regression in RESTEasy. Could you have a look?

It's pretty serious.

/cc @geoand

I am running into the same (or very similar) issue as well. I can actually reproduce with the following simple setup:

Resource class:

@Path("fruits")
public class FruitResource {
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public String hello(MultivaluedMap<String, String> formData) {
        return formData.entrySet().stream()
                .map(e -> e.getKey() + ":" + String.join(",", e.getValue()))
                .collect(toList())
                .toString();

Test class:

@QuarkusTest
public class FruitResourceTest {
    @Test
    public void testHelloEndpoint() {
        given()
            .formParams(Map.of("test", "value"))
            .contentType(ContentType.URLENC)
            .when().post("/fruits")
            .then()
                .statusCode(200)
                .body(is("[test:value]"));

When running on Quarkus 1.9.2.Final, the test passes, but it fails with Quarkus 1.10.2.Final (or any 1.10 version) with the following exception:

javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: interface javax.ws.rs.core.MultivaluedMap<class java.lang.String, class java.lang.String> of content type: application/x-www-form-urlencoded; charset=ISO-8859-1

The error message is slightly different, but I think the underlying cause is similar, if not the same. Quick inspection shows that ServerFormUrlEncodedProvider is no longer detected as a message body reader by RestEasy in ResteasyProviderFactoryImpl#getMessageBodyReader (as was the case in Quarkus 1.9.2.Final)

@gsmet this turns out to be caused by 347015b - the fact that we are not including the built in providers means that ServerFormUrlEncodedProvider is no longer available.

I am not really familiar with the change in the linked commit, so I'll leave it up to you to decide what to do, but a quick fix would be to register this provider manually.

Yeah and I see the example doesn't set the @Produces annotation for a @POST so it was indeed including all the providers before JSON becoming the default.

There's a good chance you can reproduce it with 1.9 if you define the @Produces annotation to JSON.

The big issue is that ServerFormUrlEncodedProvider should be available considering the @Consumes.

I'll have a look.

The issue is that these providers are registered manually in RESTEasy instead of being registered via the service files.

See ResteasyDeploymentImpl:

            // having problems using form parameters from container for a couple of TCK tests.  I couldn't figure out
            // why, specifically:
            // com/sun/ts/tests/jaxrs/spec/provider/standardhaspriority/JAXRSClient.java#readWriteMapProviderTest_from_standalone                                               Failed. Test case throws exception: [JAXRSCommonClient] null failed!  Check output for cause of failure.
            // com/sun/ts/tests/jaxrs/spec/provider/standardwithjaxrsclient/JAXRSClient.java#mapElementProviderTest_from_standalone                                             Failed. Test case throws exception: returned MultivaluedMap is null
            providerFactory.registerProviderInstance(new ServerFormUrlEncodedProvider(useContainerFormParams), null, null, true);
            providerFactory.registerProviderInstance(new JaxrsServerFormUrlEncodedProvider(useContainerFormParams), null, null, true);

Thus they are not part of the providers list we categorize in Quarkus.

It's registered manually in RESTEasy so it totally defeats our
auto-detection mechanism.
Add a version with a no-args constructor and add it manually to Quarkus.
Fix quarkusio#13667
It's registered manually in RESTEasy so it totally defeats our
auto-detection mechanism.
Add a version with a no-args constructor and add it manually to Quarkus.
Fix quarkusio#13667
It's registered manually in RESTEasy so it totally defeats our
auto-detection mechanism.
Add a version with a no-args constructor and add it manually to Quarkus.
Fix quarkusio#13667

I'm still seeing the same issue on Quarkus 1.10.3 when making my Keycloak call. The code in question is:

		Keycloak kc = KeycloakBuilder.builder().serverUrl(oidcClient.getAuthServerURL().toExternalForm())
				.realm(oidcClient.getRealm()).clientId(oidcClient.getClientID())
				.clientSecret(oidcClient.getCredentials().getClientSecret()).username(username).password(password)
				.build();
		assertNotNull(kc);
		String accessToken = kc.tokenManager().getAccessTokenString();

Upon making this call I get the same javax.ws.rs.ProcessingException in question. Is there something more I need to do? Changes I need to make in my code?

Still experiencing this issue with 1.10.5.Final

'ProcessingException' is not a WebApplicationException: 'RESTEASY003215: could not find writer for content-type application/x-www-form-urlencoded type: javax.ws.rs.c
ore.Form$1
      [KEYCLOAK-16668] - Error when creating admin user when starting Keycloak.x for the first time
      keycloak/keycloak#7690
          

This problem also causes me headaches and I can't continue working as I'm not able to submit forms that I made and that are crucial for the application.
A fix as soon as possible would definitely be truly appreciated.

Can someone still experiencing the issue with the latest Quarkus upload a reproducer project please?

Thanks

Well, even better if everyone experiencing the issue could post a small reproducer. There are apparently several things into play here.

And I think there's probably something to check with the Keycloak client itself. I don't know how the calls are made but it might not trigger the registration of the provider.

There is the sample reproducer. A simple auth request to a keycloak server:
rest-client-error.zip

After some research I've figured out that in my case the problem occurs when I mix io.quarkus:quarkus-resteasy-jackson and io.quarkus:quarkus-rest-client

@TheGeekPharaoh are you using the quarkus-keycloak-admin-client extension? Because I couldn't reproduce the issue if I use the extension but I could see how you would have this very problem if you use the underlying library directly.

@vhmolinar yeah, using the REST client like that won't work as we can't detect the media types that are used. Either use the quarkus-keycloak-admin-client or if you still want to access things directly, follow https://quarkus.io/guides/rest-client .

I understand that the keyclock lib was causing this issue for you but it is an issue for me as well and just in case this helps anyone that is what I did to make it go through
In the pom I just include

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-multipart-provider</artifactId>
</dependency>

and then I build the builder using Resteasy explicitly.

WebTarget usercreds = ResteasyClientBuilder.newClient().target("...")

I think this avoids mixing up something with the Apache Client but not sure if that's the problem. Hope this helps locating the issue.

I got success using javax.ws.rs.core.MultivaluedHashMap class instead Form (javax.ws.rs.core.Form).

private static final MultivaluedMap<String, String> values = new MultivaluedHashMap<String, String>();

javax.ws.rs.core.Response response = client.target(REST_URI).request().post(Entity.entity(values, MediaType.APPLICATION_FORM_URLENCODED));

Quarkus: 1.10.3.Final

Looks like that was my issue as well. An errant keycloak-admin-client reference in my pom.xml. Once I replaced that with quarkus-keycloak-admin-client everything worked.

This just solved my problem.