Spring-Integration: int-http:outbound-channel-adapter using json - json

I want to use the int-http:outbound-channel-adapter calling my rest service, but unfortunately it doesnt want to convert my pojo to json.
My Configuration:
<int-http:outbound-channel-adapter
channel="ticketOutgoingChannel"
http-method="PUT"
url="http://localhost:8080/process/ticket"
expected-response-type="*.model.Ticket"
message-converters="converters"
/>
<util:list id="converters">
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</util:list>
Stacktrace:
Caused by: org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [*.model.Ticket] and content type [application/x-java-serialized-object]
at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:810)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:594)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:572)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:493)
at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.handleRequestMessage(HttpRequestExecutingMessageHandler.java:382)
... 34 more

[application/x-java-serialized-object]
This is the default content type for a Java object if no contentType header is provided.
Use
<int:header-enricher input-channel="ticketOutgoingPreProcessChannel"
output-channel="ticketOutgoingChannel">
<int:header name="contentType" value="application/json" overwrite="true/>
</int:header-enricher>
to instruct the adapter to convert to JSON.

Related

How to set connection timeout with OAuth2RestTemplate while fetching access token

We are able to fetch access token using attached code snapshot but didn't find any way to set connection timeout as we do with spring restTemplate.Is there any way to set a connection timeout with OAuth2RestTemplate.
<bean id="bean" class="com.test.Provider">
<constructor-arg name="context" ref="context" />
<constructor-arg name="detail" ref="resourceDetails" />
</bean>
<bean id="context" class="org.springframework.security.oauth2.client.DefaultOAuth2ClientContext">
<constructor-arg name="accessTokenRequest" ref="request" />
</bean>
<bean id="request" class="org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest"/>
<bean id="resourceDetails" class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails"/>
A little late to the party, but in case you're wondering how to do this with springboot, this is a way:
#Bean
protected OAuth2RestTemplate oauth2RestTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(oAuthDetails());
oAuth2RestTemplate.setRequestFactory(clientHttpRequestFactory);
return oAuth2RestTemplate;
}
#Bean
protected ClientHttpRequestFactory requestFactory() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(10000); //timeout in milliseconds
requestFactory.setReadTimeout(10000); //timeout in milliseconds
return requestFactory;
}
Where oAuthDetails() is a method that reads the oauth configuration properties, similar to this:
#Bean
#ConfigurationProperties("path.to.your.oauth.properties.on.yml")
protected ClientCredentialsResourceDetails oAuthDetails() {
return new ClientCredentialsResourceDetails();
}
If I'm right, the way you give the connection timeout to the Spring RestTemplate as a constructor argument is through giving a ClientHttpRequestFactory as an argument to the constructor
RestTemplate(ClientHttpRequestFactory requestFactory)
Using for example the HttpComponentsClientHttpRequestFactory, one can set the connection timeout to the RestClient in XML as follows
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg name="requestFactory">
<bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="10000" />
</bean>
</constructor-arg>
</bean>
RestTemplate also offers a way to set the requestFactory property through a setter, which it inherits from InterceptingHttpAccessor, and in fact the constructor itself seems to use that setter to set the requestFactory given as constructor argument.
Thus, you can set the requestFactory for the OAuth2RestTemplate through the setter. In XML:
<bean id="oauth2RestTemplate" class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg name="resource" ref="resourceDetails" />
<constructor-arg name="context" ref="context" />
<property name="requestFactory">
<bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="10000" />
</bean>
</property>
</bean>
Or, in your case, you can for example give your class com.test.Provider a constructor argument requestFactory and then use that to set the request factory in the OAuth2RestTemplate as follows:
XML:
<bean id="bean" class="com.test.Provider">
<constructor-arg name="context" ref="context" />
<constructor-arg name="resourceDetails" ref="resourceDetails" />
<constructor-arg name="requestFactory">
<bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="10000" />
</bean>
</constructor-arg>
</bean>
And in your code set
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(this.resourceDetails, this.context);
restTemplate.setRequestFactory(this.requestFactory);
String tokenString = restTemplate.getAccessToken().getValue();
after setting the value of this.requestFactory in the constructor.
PS. I would prefer to create a single OAuth2RestTemplate for the entire class as a private field and reuse it in that class, unless you have a reason to create a new one for each request. You could create it in the constructor, as you are giving the context and details as constructor arguments, or then in a post-construct/init-method. Or even give it as a constructor-argument or as a property to your class in the XML, in case the context and resourceDetails are not used outside the restTemplate.
EDIT
After doing some more research, it seems that the problem might be harder than I thought. OAuth2RestTemplate uses an AccessTokenProvider to get the access tokens, by default it uses a chain of AccessTokenProviders through a instance of AccessTokenProviderChain in order to support the different types of grant types. It seems that each of these uses their own RestTemplate to send the requests to obtain the access token. Unfortunately, it seems that OAuth2RestTemplate doesn't offer a simple way to set the requestFactory of the default AccessTokenProviders' restTemplates.
So, if the solution that I proposed above is not working (which I suspect), I would use the following approach which I believe to work.
All the default AccessTokenProviders in Spring Security Oauth2 extend the class OAuth2AccessTokenSupport, which also is the class that creates the internal RestTemplate. Fortunately, this class offers a setter to set the requestFactory of the internal RestTemplate. So, one can create for example an instance of a ClientCredentialsAccessTokenProvider and give set the requestFactory through the setRequestFactory(...) method. In XML:
<bean id="clientCredentialsAccessTokenProvider class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider">
<property name="requestFactory">
<bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="10000" />
</bean>
</property>
</bean>
For some reason the OAuth2RestTemplate doesn't offer a way to access the default AccessTokenProviders, but one can set them through the setAccessTokenProvider(AccessTokenProvider accessTokenProvider) setter. To replicate the original default behaviour of the OAuth2RestTemplate, one would have to give an instance of a AccessTokenProviderChain, together with four default AccessTokenProviders and set their requestFactories. However, as you know that the accessed resource is of type ClientCredentialsResourceDetails, it suffices to set a single ClientCredentialsAccessTokenProvider in the setAccessTokenProvider(...) setter and set the requestFactory of the ClientCredentialsAccessTokenProvider.
So we would get the following code:
XML:
<bean id="bean" class="com.test.Provider">
<constructor-arg name="context" ref="context" />
<constructor-arg name="resourceDetails" ref="resourceDetails" />
<constructor-arg name="accessTokenProvider" ref="clientCredentialsAccessTokenProvider" />
</bean>
<bean id="clientCredentialsAccessTokenProvider" class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider">
<property name="requestFactory">
<bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="10000" />
</bean>
</property>
</bean>
And in your code, set the AccessTokenProvider:
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(this.resourceDetails, this.context);
restTemplate.setAccessTokenProvider(this.accessTokenProvider);
String tokenString = restTemplate.getAccessToken().getValue();
after setting the value of this.accessTokenProvider in the constructor. If you decide to create the OAuth2RestTemplate in the XML, you can write
<bean id="oauth2RestTemplate" class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg name="resource" ref="resourceDetails" />
<constructor-arg name="context" ref="context" />
<property name="accessTokenProvider" ref="clientCredentialsAccessTokenProvider" />
</bean>
where the bean "clientCredentialsAccessTokenProvider" is defined as above.
Hope that this works.
One issue with above solution is that ClientCredentialsAccessTokenProvider uses a bit enhanced requestFactory:
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() {
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
connection.setInstanceFollowRedirects(false);
connection.setUseCaches(false);
...
}
...
};
So in order to preserve the original functionality you should set up the same implementation of the factory.

Camel DataFormat Jackson using blueprint XML DSL throws context exception

No matter where I place the dataformats in XML DSL blueprint, I get this error just starting at different places. if I remove it, it works but of course I can't convert JSON to POJO. ??? any help or tell me what I'm doing wrong, what i'm missing. thanks!
Error
Unable to start blueprint container for bundle passthrumt1.core/1.0.1.SNAPSHOT
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'endpoint'. One of '{"http://camel.apache.org/schema/blueprint":redeliveryPolicyProfile, "http://camel.apache.org/schema/blueprint":onException, "http://camel.apache.org/schema/blueprint":onCompletion, "http://camel.apache.org/schema/blueprint":intercept, "http://camel.apache.org/schema/blueprint":interceptFrom, "http://camel.apache.org/schema/blueprint":interceptSendToEndpoint, "http://camel.apache.org/schema/blueprint":restConfiguration, "http://camel.apache.org/schema/blueprint":rest, "http://camel.apache.org/schema/blueprint":route}' is expected.
XML DSL
<camelContext
id="com.passthru.coreCamelContext"
trace="true"
xmlns="http://camel.apache.org/schema/blueprint"
allowUseOriginalMessage="false"
streamCache="true"
errorHandlerRef="deadLetterErrorHandler" >
<properties>
<property key="http.proxyHost" value="PITC-Zscaler-Americas.proxy.corporate.com"/>
<property key="http.proxyPort" value="80"/>
</properties>
<streamCaching id="CacheConfig"
spoolUsedHeapMemoryThreshold="70"
anySpoolRules="true"/>
<!-- -->
<dataFormats>
<json id="Json2Pojo" library="Jackson" unmarshalTypeName="com.passthru.core.entities.TokenEntities">
</json>
</dataFormats>
<endpoint id="predixConsumer" uri="direct:preConsumer" />
<endpoint id="predixProducer" uri="direct:preProducer" />
<endpoint id="getToken" uri="direct:getToken" />
<onException>
<exception>com.passthru.dataservice.PDXDataServiceInvalidDataException</exception>
<redeliveryPolicy maximumRedeliveries="3" />
<handled>
<constant>true</constant>
</handled>
<log
message="Invalid Data From Data Service"
loggingLevel="ERROR" />
<setBody>
<simple>${body.toString}</simple>
</setBody>
<to uri="file:{{errorArchive}}" />
</onException>
If I place the dataformats above properties, it complains, I have to remove properties and streamcache statements in order for it to work. but I need the proxy properties. any suggestions??? thanks again
If the
<camelContext
id="com.ge.digital.passthru.coreCamelContext"
trace="true"
xmlns="http://camel.apache.org/schema/blueprint"
allowUseOriginalMessage="false"
streamCache="true"
errorHandlerRef="deadLetterErrorHandler" >
<dataFormats>
<json id="Json2Pojo" library="Jackson" unmarshalTypeName="com.passthru.core.entities.TokenEntities"/>
</dataFormats>
<properties>
<property key="http.proxyHost" value="PITC-Zscaler-Americas-Cincinnati3PR.proxy.corporate.com"/>
<property key="http.proxyPort" value="80"/>
</properties>
i get this
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'properties'. One of '{"http://camel.apache.org/schema/blueprint":redeliveryPolicyProfile, "http://camel.apache.org/schema/blueprint":onException, "http://camel.apache.org/schema/blueprint":onCompletion, "http://camel.apache.org/schema/blueprint":intercept, "http://camel.apache.org/schema/blueprint":interceptFrom, "http://camel.apache.org/schema/blueprint":interceptSendToEndpoint, "http://camel.apache.org/schema/blueprint":restConfiguration, "http://camel.apache.org/schema/blueprint":rest, "http://camel.apache.org/schema/blueprint":route}' is expected.
what am I missing?
Camel blueprint XML is validated against camel-blueprint.xsd.
You are interested in complex type with name camelContextFactoryBean which contains sequence of available elements with fixed order.
Correct order of camelContext elements defined in this sequence is:
properties
globalOptions
propertyPlaceholder
package
packageScan
contextScan
jmxAgent
streamCaching
export
defaultServiceCallConfiguration
serviceCallConfiguration
defaultHystrixConfiguration
hystrixConfiguration
routeBuilder
routeContextRef
restContextRef
threadPoolProfile
threadPool
endpoint
dataFormats
transformers
validators
redeliveryPolicyProfile
onException
onCompletion
intercept
interceptFrom
interceptSendToEndpoint
restConfiguration
rest
route
To solve your problem move all endpoint declarations right above dataFormats.

Camel-jackson vs 2.9.0 ignore unknown properties during unmarshal

<dataFormats>
 <json id="json" library="Jackson"
unmarshalTypeName="com.foo.MyPojo" disableFeatures="FAIL_ON_UNKNOWN_PROPERTIES"/>
</dataFormats>
I want to disbleFeature Fail on unknown property of jackson but I think it is available only in camel vs2.15.0 and greater.
How can i implement following using spring dsl:
dataFormat.getObjectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false);
I'm not sure, or may be did not understand you right. But try this thing:
<bean id="format" class="org.apache.camel.component.jackson.JacksonDataFormat"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="format"/>
<property name="targetMethod" value="disableFeature"/>
<property name="arguments">
<list>
<value type="com.fasterxml.jackson.databind.DeserializationFeature">FAIL_ON_UNKNOWN_PROPERTIES</value>
</list>
</property>
</bean>
With JSON:
Use JsonIgnoreProperties with attribute "value". For instance:
#JsonIgnoreProperties(ignoreUnknown = true, value={"dataIgnored"})
When BeanDeserializerBuild is instanced, use those attributes.

Spring json not using the desired root name

I have the following configuration :
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<!-- XML View -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="autodetectAnnotations" value="true" />
</bean>
</constructor-arg>
</bean>
</list>
</property>
It creates a json, but the root name is not what i want
#XStreamAlias("house")
#JsonAutoDetect
#JsonRootName(value = "house")
public class TableHouse {
private Long value;
.....
}
For the xml it works fine, however for the json it does not pick up the #JsonRootName.. and outputs json with class name as root...
Any ideas?
You have to enable root-level wrapping. See How do I rename the root key of a JSON with Java Jackson? to have an idea on how to use JsonRootName properly.

No unique bean of type [org.springframework.ws.client.core.WebServiceTemplate] is defined

I am trying to make two testcases using class org.springframework.ws.client.core.WebServiceTemplate. Both testcases are in different classes so I made two different beans for them.
On running the junit tests I got a error like that
Error creating bean with name 'testcases.TestAdminMethodsWebService':
Unsatisfied dependency expressed through bean property 'admin': : No
unique bean of type
[org.springframework.ws.client.core.WebServiceTemplate] is defined:
expected single matching bean but found 7: [admin, rules]; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
unique bean of type
[org.springframework.ws.client.core.WebServiceTemplate] is defined:
expected single matching bean but found 2: [admin, rules]
My bean is like this:
<oxm:jaxb2-marshaller id="marshaller_admin" contextPath="a.com.b" />
<bean id="admin" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller_admin" />
<property name="unmarshaller" ref="marshaller_admin" />
<property name="defaultUri"
value="http://dev05:8080/.." />
</bean>
<oxm:jaxb2-marshaller id="marshaller_rules" contextPath="r.com.b" />
<bean id="rules" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller_rules" />
<property name="unmarshaller" ref="marshaller_rules" />
<property name="defaultUri"
value="http://dev05:8080/.." />
</bean>
please tell me how to overcome with this problem or why this error occurs any help will be appreciated thank you.
Use the #Qualifier annotation to help Spring determine which bean should be injected.
public class TestClass {
#Autowired
#Qualifier("admin")
WebServiceTemplate admin;
#Autowired
#Qualifier("rules")
WebServiceTemplate rules;
// ... Rest of your class
}
Read up on the documentation here under the section Fine-tuning annotation-based autowiring with qualifiers.
Update:
You also need to change your xml bean definitions like this:
<oxm:jaxb2-marshaller id="marshaller_admin" contextPath="a.com.b" />
<bean class="org.springframework.ws.client.core.WebServiceTemplate">
<qualifier value="admin"/>
<property name="marshaller" ref="marshaller_admin" />
<property name="unmarshaller" ref="marshaller_admin" />
<property name="defaultUri"
value="http://dev05:8080/.." />
</bean>
<oxm:jaxb2-marshaller id="marshaller_rules" contextPath="r.com.b" />
<bean class="org.springframework.ws.client.core.WebServiceTemplate">
<qualifier value="rules"/>
<property name="marshaller" ref="marshaller_rules" />
<property name="unmarshaller" ref="marshaller_rules" />
<property name="defaultUri"
value="http://dev05:8080/.." />
</bean>
Note the inclusion of <qualifier> tag under each bean definition.
Hello this is the right answer
public class TestClass {
protected WebServiceOperations admin;
admin = (WebServiceOperations) getApplicationContext().getBean("admin");
protected WebServiceOperations rules;
rules = (WebServiceOperations) getApplicationContext().getBean("rules");
// ... Rest of the class
}