[ 
https://issues.apache.org/jira/browse/CXF-4309?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13273244#comment-13273244
 ] 

Jordi Torrente commented on CXF-4309:
-------------------------------------

Great!!! With "org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider" 
the OAuth2 server serializes ClientAccessToken with no problems (and without 
any special configuration):
---------------------------
ID: 2
Response-Code: 200
Content-Type: application/json
Headers: {Cache-Control=[no-store], Pragma=[no-cache], Date=[Fri, 11 May 2012 
13:10:54 GMT]}
Payload: {"access_token":"9e2e647541e87dc62778bb9f484db8","token_type":"bearer"}
--------------------------------------
Now, no empty attributes are sent and properties names change: 
tokenKey->access_token and tokenType->token_type.

But at client side, the following code is still unable to de-serialize the 
response:

OAuthJSONProvider provider = new OAuthJSONProvider();
WebClient client2 = 
WebClient.create("http://localhost:8080/fwmobisecurity2/services/oauth2";, 
        Collections.singletonList(provider));
[...]
ClientAccessToken obj = client2.post(formData, ClientAccessToken.class);

Received error:

May 11, 2012 3:10:54 PM org.apache.cxf.jaxrs.client.AbstractClient 
reportMessageHandlerProblem
SEVERE: .No message body reader has been found for class : class 
org.apache.cxf.rs.security.oauth2.common.ClientAccessToken, ContentType : 
application/json
                
> OAuth2 Access Token Service: returned ClientAccessToken is not JAXB compliant
> -----------------------------------------------------------------------------
>
>                 Key: CXF-4309
>                 URL: https://issues.apache.org/jira/browse/CXF-4309
>             Project: CXF
>          Issue Type: Bug
>          Components: JAX-RS Security
>    Affects Versions: 2.6
>            Reporter: Jordi Torrente
>              Labels: jax-rs, jaxb, oauth2
>
> The OAuth2 Access Token Service current implementation (class 
> org.apache.cxf.rs.security.oauth2.services.AccessTokenService) processes a 
> request inside "handleTokenRequest()" and this method returns the 
> successfully generated token using an instance of ClientAccessToken.
> But that class has two problems or limitations:
> 1) It is not a JAXB-annotated bean so the error "No message body writer has 
> been found for response class ClientAccessToken" is raised. This can be 
> solved adding the "jaxbElementClassMap" property to the default JSON provider 
> (jettison):
> <bean id="jsonProvider" 
> class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
>   <property name="jaxbElementClassMap" ref="myElementClassMap"/>
> </bean>
> <util:map id="myElementClassMap">
>   <entry key="org.apache.cxf.rs.security.oauth2.common.ClientAccessToken" 
> value="ClientAccessToken"/>
> </util:map>
> <jaxrs:server id="oauth2Server" address="/oauth2">
>   <jaxrs:serviceBeans>
>     <ref bean="accessTokenService2"/>
>   </jaxrs:serviceBeans>
>   <jaxrs:providers>
>       <ref bean="jsonProvider"/>
>   </jaxrs:providers>    
> </jaxrs:server>
> Unluckily, after doing that change we find out the second problem:
>  
> 2) ClientAccessToken does not have a no-arg default constructor, so it's not 
> JAXB compliant, and the default JSON provider is unable to serialize the data:
> org.apache.cxf.rs.security.oauth2.common.AccessToken does not have a no-arg 
> default constructor.
>       this problem is related to the following location:
>               at org.apache.cxf.rs.security.oauth2.common.AccessToken
>               at org.apache.cxf.rs.security.oauth2.common.ClientAccessToken
>               
> The only way I've found to overcome both limitations is changing the JSON 
> provider to Codehaus jackson:
> <bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
> <bean id="jsonProvider" 
> class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider">
>   <property name="mapper" ref="jacksonObjectMapper" />
> </bean>
> <jaxrs:server id="oauth2Server" address="/oauth2">
>   <jaxrs:serviceBeans>
>     <ref bean="accessTokenService2"/>
>   </jaxrs:serviceBeans>
>   <jaxrs:providers>
>     <ref bean="jsonProvider"/>
>   </jaxrs:providers>    
> </jaxrs:server>
> Here you can see a response generated using this new provider:
> Response-Code: 200
> Content-Type: application/json
> Headers: {Cache-Control=[no-store], Pragma=[no-cache], Date=[Fri, 11 May 2012 
> 11:11:29 GMT]}
> Payload: 
> {"tokenKey":"e91ffcefb133de5eb7ebd02c25e7886e","tokenType":"bearer","parameters":{},"approvedScope":null,"refreshToken":null}
> So far all the work is done at the server side, but what about the client 
> side?
> If we try to de-serialize an access token response using CXF client 
> capabilities (org.apache.cxf.jaxrs.client.WebClient), we will find the same 
> already known problems: 
> a) With jettison: 
> No message body reader has been found for class : class 
> org.apache.cxf.rs.security.oauth2.common.ClientAccessToken, ContentType : 
> application/json
> Adding the "jaxbElementClassMap" property to the provider, will stop us at 
> the no-arg default constructor problem:
> JSONProvider<?> provider = new JSONProvider<Object>();
> provider.setJaxbElementClassMap(Collections.singletonMap("org.apache.cxf.rs.security.oauth2.common.ClientAccessToken",
>  "ClientAccessToken"));
> WebClient client = 
> WebClient.create("http://localhost:8080/fwmobisecurity2/services/oauth2";, 
> Collections.singletonList(provider));
> [...]
> ClientAccessToken obj = client.post(formData, ClientAccessToken.class);
>       
> b) With jackson:
> No suitable constructor found for type [simple type, class 
> org.apache.cxf.rs.security.oauth2.common.ClientAccessToken]: can not 
> instantiate from JSON object (need to add/enable type information?)
> Luckily, jackson offers "Mix-in Annotations" that allow us to define which 
> constructor to use, and its parameter binding:
> First we must create a class with the following content:
> import org.codehaus.jackson.annotate.JsonCreator;
> import org.codehaus.jackson.annotate.JsonProperty;
> public abstract class ClientAccessTokenDeserializeInfo {
>       @JsonCreator
>       ClientAccessTokenDeserializeInfo(
>                       @JsonProperty(value="tokenType") String tokenType, 
>                       @JsonProperty(value="tokenKey") String tokenKey) { }
> }
> And then we map it to the ClientAccessToken class:
> JacksonJsonProvider provider = new JacksonJsonProvider();
> ObjectMapper mapper = new ObjectMapper();
> mapper.getDeserializationConfig().addMixInAnnotations(ClientAccessToken.class,
>  ClientAccessTokenDeserializeInfo.class);
> provider.setMapper(mapper);
> WebClient client = 
> WebClient.create("http://localhost:8080/fwmobisecurity2/services/oauth2";, 
> Collections.singletonList(provider));
> [...]
> ClientAccessToken obj = client.post(formData, ClientAccessToken.class);
> Conclusion: Without changing ClientAccessToken source code, jackson JSON 
> provider MUST be used at server and client sides

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: 
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to