Set Http Conduit timeout dynamically - jax-rs

I am facing an issue where I need to set timeout for one particular webservice to a value other than the default value for all other services. Now I need to find a way where I can somehow programmatically override the http Conduit timeout for my service. Can someone please guide me how to achieve this?
This is my current configuration and service:
<http:conduit name="*.http-conduit">
<http:client ConnectionTimeout="${httpConduit.connectionTimeout:30000}" ReceiveTimeout="${httpConduit.receiveTimeout:30000}" />
<http:tlsClientParameters disableCNCheck="${httpConduit.ssl.disableCNCheck:false}">
<sec:keyManagers keyPassword="${httpConduit.ssl.keyPassword}">
<sec:keyStore type="${httpConduit.ssl.keyStoreType}" password="${httpConduit.ssl.keyStorePassword}" file="${httpConduit.ssl.keyStoreFile}" />
</sec:keyManagers>
<sec:trustManagers>
<sec:keyStore type="${httpConduit.ssl.trustStoreType}" password="${httpConduit.ssl.trustStorePassword}" file="${httpConduit.ssl.trustStoreFile}" />
</sec:trustManagers>
<sec:cipherSuitesFilter>
<sec:include>.*_EXPORT_.*</sec:include>
<sec:include>.*_EXPORT1024_.*</sec:include>
<sec:include>.*_WITH_DES_.*</sec:include>
<sec:include>.*_WITH_AES_.*</sec:include>
<sec:include>.*_WITH_NULL_.*</sec:include>
<sec:exclude>.*_DH_anon_.*</sec:exclude>
</sec:cipherSuitesFilter>
</http:tlsClientParameters>
</http:conduit>
<jaxrs:client id="testProxy" address="${test.endpoint}" threadSafe="true" serviceClass="foo.TestProxy">
<jaxrs:headers>
<entry key="Accept-Encoding" value="gzip,deflate" />
<entry key="Content-Type" value="application/json;charset=UTF-8" />
<entry key="Content-Length" value="92" />
<entry key="Connection" value="Keep-Alive" />
</jaxrs:headers>
<jaxrs:providers>
<ref bean="jsonProvider" />
</jaxrs:providers>
<jaxrs:features>
<!-- Enables logging of the 'on-the-wire' request/response -->
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxrs:features>
</jaxrs:client>

Issue resolved!
I created an out interceptor bean in the client and a CustomINterceptor class to update the values:
<jaxrs:headers>
<entry key="Accept-Encoding" value="gzip,deflate" />
<entry key="Content-Type" value="application/json;charset=UTF-8" />
<entry key="Content-Length" value="92" />
<entry key="Connection" value="Keep-Alive" />
</jaxrs:headers>
<jaxrs:providers>
<ref bean="jsonProvider" />
</jaxrs:providers>
<jaxrs:features>
<!-- Enables logging of the 'on-the-wire' request/response -->
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxrs:features>
<jaxrs:outInterceptors>
<ref bean="customInterceptor" />
</jaxrs:outInterceptors>
</jaxrs:client>
<bean id="customInterceptor" class="com.bmo.channel.alert.interceptor.CustomInterceptor" ></bean>
public class CustomInterceptor extends AbstractPhaseInterceptor<Message>{
public CustomInterceptor () {
super(Phase.SETUP);
}
#Override
public void handleMessage(Message message) {
System.out.println("Inside handle message");
try {
final Conduit conduit = message.getExchange().getConduit(message);
if (conduit instanceof HTTPConduit) {
final HTTPConduit httpConduit = (HTTPConduit) conduit;
HTTPClientPolicy policy = httpConduit.getClient();
policy.setReceiveTimeout(timeout);
policy.setConnectionTimeout(timeout);
httpConduit.setClient(policy);
System.out.println("ConnectionTimeout() -- " + policy.getConnectionTimeout());
System.out.println("ReceiveTimeout -- " + policy.getReceiveTimeout());
System.out.println("HTTPClientPolicy -- " + policy.getHost());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
'''

Related

RabbitMQ listener stops listening messages when MessageListener throws exception first time

I am facing a unusual problem, after an exception is thrown during processing of first message, Spring Amqp MessageListener stops picking up any further messages.In my code I am not explicitly doing any resource locking. Can anyone please suggest what is the problem.Any help will be appreciated as I am falling short of ideas.
MQ configuraion:
<rabbit:admin id="rabbitAdmin" connection-factory="rabbitConnectionFactory" />
<rabbit:connection-factory
id="rabbitConnectionFactory"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"/>
<!--
Configure the rabbitTemplate helper class to simplify rabbitMQ
access (sending and receiving message).
The default Exchange is used here.
-->
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" message-converter="rabbitSimpleMessageConverter" retry-template="rabbitRetryTemplate" />
<bean id="rabbitRetryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="500" />
<property name="multiplier" value="5" />
<property name="maxInterval" value="90000" />
</bean>
</property>
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="5"/>
</bean>
</property>
</bean>
<bean id="rabbitRetryInterceptor" class="org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean">
<property name="messageRecoverer" ref="rejectAndDontRequeueRecoverer"/>
<property name="retryOperations" ref="rabbitRetryTemplate" />
</bean>
<!-- Consumers -->
<bean id="genericMessageConsumer" class="org.bla.GenericMessageConsumer" />
<rabbit:listener-container
connection-factory="rabbitConnectionFactory"
advice-chain="rabbitRetryInterceptor"
concurrency="${rabbitmq.concurrency}"
max-concurrency="${rabbitmq.maxconcurrency}"
acknowledge="auto" >
<rabbit:listener ref="genericMessageConsumer" queue-names="${rabbitmq.orion.genericNotificationdata.queueName}" />
</rabbit:listener-container>
MessageListener class
public class GenericMessageConsumer implements MessageListener{
....
#Override
public void onMessage(Message message) {
try{
...Some logic...
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
After exception is thrown while processing first message, The container remains up but the remaining messages are not processed until i restart the container. Also the first messages stays in unacknowledged state.
Tried checking other posts same as my issue:
1) RabbitMQ listener stops listening messages when MessageListener throws exception
but am unable to figure out the solution.

Query regarding Spring message-driven-channel-adapter

I am using Spring's message-driven-channel-adapter.
My component is consuming message from Tibco Topic and Publishing to RabbitMQ topic
So The message flow is as follows:
Tibco-> (subscribed by )Component (Published to)-> RabbitMQ
The service activator is shown below: as we see there is a input-channel and an output-channel. The bean storeAndForwardActivator will have the business logic (within the method createIssueOfInterestOratorRecord)
<int:service-activator input-channel="inboundOratorIssueOfInterestJmsInputChannel"
ref="storeAndForwardActivator" method="createIssueOfInterestOratorRecord"
output-channel="outboundIssueOfInterestRabbitmqOratorJmsOutputChannel" />
I also have a message=driven-channel-adapter. This adapter will be invoked before the service adapter is invoked.
<int-jms:message-driven-channel-adapter
id="oratorIssueOfInterestInboundChannel" channel="inboundOratorIssueOfInterestJmsInputChannel"
container="oratorIssueOfInterestmessageListenerContainer" />
i.e. specifically the container (shown below) will hold the Topic name to be used - this is the DefaultMessageListenerContainer
<bean id="oratorIssueOfInterestmessageListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="oratorIssueOfInterestTibcoConnectionFactory" />
<property name="destination" ref="oratorTibcojmsDestination" />
<property name="sessionTransacted" value="true" />
<property name="maxConcurrentConsumers" value="1" />
<property name="concurrentConsumers" value="1" />
<property name="receiveTimeout" value="5000" />
<property name="recoveryInterval" value="60000" />
<property name="autoStartup" value="true" />
<property name="exposeListenerSession" value="false" />
<property name="subscriptionDurable" value="true" />
<property name="durableSubscriptionName" value="${topic.orator.durable-subscription-name}" />
<property name="messageSelector" value="${topic.orator.selector}" />
</bean>
This set up works perfectly fine. However in some cases my consumer/component receives a 'rogue' message. i.e. an empty payload or a message type of HashMap (instead of plain TextMessage) - when we get this - what I observe is - an exception is caught at the DefaultMessageListener level (i.e. I don't go as far as my business bean i.e. storeAndForwardActivator), because of this my component is not sending ACK back - and since this is a durable Topic - there is a build of messages at the Topic - which is undesirable.
Is there a way for me to ACK the message straight away irrespective of weather an exception is caught at the DefaultMessageListener level?
Or should I introduce an error handler at the DefaultMessageListener?
What's the best way to handle this, any suggestions?
regards
D
Update:
I tried adding a errorHandler to the org.springframework.jms.listener.DefaultMessageListenerContainer
as shown below
<bean id="oratorIssueOfInterestmessageListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="oratorIssueOfInterestTibcoConnectionFactory" />
<property name="destination" ref="oratorTibcojmsDestination" />
<property name="sessionTransacted" value="true" />
<property name="maxConcurrentConsumers" value="1" />
<property name="concurrentConsumers" value="1" />
<property name="receiveTimeout" value="5000" />
<property name="recoveryInterval" value="60000" />
<property name="autoStartup" value="true" />
<property name="exposeListenerSession" value="false" />
<property name="subscriptionDurable" value="true" />
<property name="durableSubscriptionName" value="${topic.orator.durable-subscription-name}" />
<property name="messageSelector" value="${topic.orator.selector}" />
<property name="errorHandler" ref="myErrorHandler"/>
</bean>
myErrorHandler is a bean as shpwn below
<bean id="myErrorHandler"
class="com.igate.firds.icmf.activators.concentrator.MyErrorHandler" />
MyErroHandler implements ErrorHandler
#Service
public class MyErrorHandler implements ErrorHandler{
private static Log log = LogFactory.getLog(MyErrorHandler.class);
#Override
public void handleError(Throwable t) {
if (t instanceof MessageHandlingException) {
MessageHandlingException exception = (MessageHandlingException) t;
if (exception != null) {
org.springframework.messaging.Message<?> message = exception.getFailedMessage();
Object payloadObject = message.getPayload();
if (null != payloadObject) {
log.info("Payload is not null, type is: " + payloadObject.getClass());
}
}
} else {
log.info("Exception is not of type: MessageHandlingException ");
}
}
}
What I notice is that the exception is caught (when the subscriber consumes a rogue message). I keep on seeing this log in a loop
Exception is not of type: MessageHandlingException
Exception is not of type: MessageHandlingException
Exception is not of type: MessageHandlingException
i.e. since the transaction is not committed - the same message from durable topic is consumed again and again. My aim is to send an ACK back to the broker after consuming the message (irrespective of weather an exception is caught or not).
I will try the error-channel tomorrow.
regards
D
Add an error-channel to the message-driven adapter; the ErrorMessage will contain a MessagingException payload that has two fields; the cause (exception) and failedMessage.
If you use the default error-channel="errorChannel", the exception is logged.
If you want to do more than that you can configure your own error channel and add some flow to it.
EDIT:
Further to your comments below...
payload must not be null is not a stack trace; it's a message.
That said, payload must not be null looks like a Spring Integration message; it is probably thrown in the message listener adapter during message conversion, which is before we get to a point where the failure can go to the error-channel; such an exception will be thrown back to the container.
Turn on DEBUG logging and look for this log entry:
logger.debug("converted JMS Message [" + jmsMessage + "] to integration Message payload [" + result + "]");
Also, provide a FULL stack trace.
EDIT#2
So, I reproduced your issue by forcing the converted payload to null in a custom MessageConverter.
The DMLC error handler is called by the container after the transaction is rolled back so there's no way to stop the rollback.
We can add an option to the adapter to handle such errors differently but that will take some work.
In the meantime, a work-around would be to write a custom MessageConverter; something like the one in this Gist.
Then, your service will have to deal with handling the "Bad Message Received" payload.
You then provide the custom converter like this...
<jms:message-driven-channel-adapter id="jmsIn"
destination="requestQueue" acknowledge="transacted"
message-converter="converter"
channel="jmsInChannel" />
<beans:bean id="converter" class="foo.MyMessageConverter" />

Spring-Data-Solr How to provide authentication data

how do i proivde authentication data for spring data solr server?
Here is what i have in configuration
<solr:solr-server id="solrServer" url="http://xxxxxxxx:8983/solr" />
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate" scope="singleton">
<constructor-arg ref="solrServer" />
</bean>
<bean id="searchRepository" class="com.bankofamerica.atmtech.repository.SolrJournalRepository">
<property name="solrOperations" ref="solrTemplate" />
</bean>
<bean id="App" class="App">
<property name="repo" ref="searchRepository" />
</bean>
I don't see any property where i can set it.
You cannot set Credentials directly but have to go through the factory.
#Bean
SolrTemplate solrTemplate() {
return new SolrTemplate(solrServerFactory());
}
#Bean
SolrServerFactory solrServerFactory() {
Credentials credentials = new UsernamePasswordCredentials("foo", "bar");
return new HttpSolrServerFactory(solrServer(), "collection1", credentials , "BASIC");
}
#Bean
SolrServer solrServer() {
return new HttpSolrServer("http://localhost:8983/solr");
}
I guess some kind of SolrAuthenticationProvider picked up and applied if present in application context would make sense in this case.

Spring rabbit does not allow overriding defaults in caching connection factory configuration : release 1.1.4

I trying a spring amqp application to send JSON data as messages to a consumer (written in Ruby).
I get connection reset errors with any setting that I try
NOTE: I deleted the default guest user and added admin with the same privileges over default virtual host.
Here's the configuration:
<beans:bean id="cachingConnectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<beans:property name="channelCacheSize" value="5" />
<beans:property name="username" value="admin" />
<beans:property name="password" value="admin" />
</beans:bean>
<rabbit:queue id='analytics.persistence.queue' name='analytics.persistence.queue' />
<rabbit:direct-exchange name="amq.direct">
<rabbit:bindings>
<rabbit:binding queue="analytics.persistence.queue">
</rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<rabbit:connection-factory id="cachingConnectionFactory" />
<rabbit:admin connection-factory="cachingConnectionFactory" />
<beans:bean id="rabbitTemplate"
class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<beans:property name="connectionFactory" ref="cachingConnectionFactory" />
<beans:property name="exchange" value="amq.direct" />
<beans:property name="routingKey" value="analytics.persistence.queue"/>
</beans:bean>
<beans:bean id="analyticsMessageProducer"
class="hoodibaba.analytics.publish.AnalyticsPersistenceMessageProducer">
<beans:property name="rabbitTemplate" ref="rabbitTemplate" />
</beans:bean>
And in my producer class
public class AnalyticsPersistenceMessageProducer extends RabbitGatewaySupport implements
AnalyticsPersistenceMessageGateway {
private static final Logger logger = LoggerFactory.getLogger(AnalyticsPersistenceMessageProducer.class);
#Override
public void sendAnalyticsMessage(String jsonData) {
try {
getRabbitTemplate().convertAndSend("analytics.persistence.queue",jsonData);
} catch (AmqpException e) {
logger.error(e.getMessage());
logger.error("Exception Stacktrace",e);
logger.error("flushing JSON data to logs: " + jsonData);
}
}
}
I get a
com.rabbitmq.client.PossibleAuthenticationFailureException: Possibly
caused by authentication failure exception
the stack trace shows connection reset
Caused by: com.rabbitmq.client.PossibleAuthenticationFailureException: Possibly caused by authentication failure
at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:348)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:516)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:545)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:160)
... 43 more
Caused by: com.rabbitmq.client.ShutdownSignalException: connection error; reason: java.net.SocketException: Connection reset
at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:67)
at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:33)
at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:343)
at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:216)
at com.rabbitmq.client.impl.AMQChannel.rpc(AMQChannel.java:202)
at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:340)
... 46 more
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:189)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
at java.io.BufferedInputStream.read(BufferedInputStream.java:254)
at java.io.DataInputStream.readUnsignedByte(DataInputStream.java:288)
at com.rabbitmq.client.impl.Frame.readFrom(Frame.java:95)
at com.rabbitmq.client.impl.SocketFrameHandler.readFrame(SocketFrameHandler.java:131)
at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:508)
The message consumer is as follows
#!/usr/bin/env ruby
# encoding: UTF-8
require 'bunny'
conn = Bunny.new("amqp://admin:admin#localhost:5672")
conn.start
ch = conn.create_channel
puts 'channel created'
q = ch.queue("analytics.persistence.queue",:exclusive => false, :auto_delete => false)
x = ch.direct("amq.direct")
q.bind(x)
q.subscribe(:block => true, :ack => true) do |delivery_info, properties, payload|
puts "Received #{payload}, message properties are #{properties.inspect}"
end
ch.close
con.close
Update: it seems the updated username and password settings are not propagated, rabbit logs show my application still uses guest
=ERROR REPORT==== 19-Mar-2013::00:16:12 ===
closing AMQP connection <0.13815.0> (127.0.0.1:44736 -> 127.0.0.1:5672):
{handshake_error,starting,0,
{amqp_error,access_refused,
"PLAIN login refused: user 'guest' - invalid credentials",
'connection.start_ok'}}
(The OP edited the answer in the post. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
The problem is in my configuration; caching connection factory is defined using both the beans namespace and rabbit namespcace, latter using the defaults. Changed to configuration to following and I do not get connection reset errors anymore
<rabbit:queue id='analytics.persistence.queue' name='analytics.persistence.queue' />
<rabbit:direct-exchange name="amq.direct">
<rabbit:bindings>
<rabbit:binding queue="analytics.persistence.queue">
</rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<rabbit:connection-factory id="cachingConnectionFactory"
username="analytics" password="analytics" channel-cache-size="5"
virtual-host="analytics" />
<rabbit:admin connection-factory="cachingConnectionFactory" />
<beans:bean id="rabbitTemplate"
class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<beans:property name="connectionFactory" ref="cachingConnectionFactory" />
<beans:property name="exchange" value="amq.direct" />
<beans:property name="queue" value="analytics.persistence.queue" />
<beans:property name="routingKey" value="save.analytics"></beans:property>
</beans:bean>
<beans:bean id="analyticsMessageProducer"
class="hoodibaba.analytics.publish.AnalyticsPersistenceMessageProducer"
autowire="byName" />

jaxrs configuring multiple jaxrs:server tags with different beans

This problem is related to JAX-RS configuration.
I configured JAX-RS for a single class. The configuration worked fine.
#Path(/bean1/)
#Produces("application/xml")
public class class1 {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path(/m1)
public String method1(JAXBElement<String> request) {
}
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path(/m2)
public String method2(JAXBElement<String> request) {
}
}
Below is jaxrs:server tag
<jaxrs:server id="bean1" address="/">
<jaxrs:serviceBeans>
<ref bean="class1" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
</jaxrs:server>
I could call through Apache Jersey client with URL "/bean1/m1"
Now, I wanted to configure another class with JAX-RS. Hence, I added configuration as below
#Path(/bean2/)
#Produces("application/xml")
public class class2 {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path(/m3)
public String method3(JAXBElement<String> request) {
}
}
I added another jaxrs:server tag and specified address. The effective configuration is
<jaxrs:server id="bean1" address="/bean1">
<jaxrs:serviceBeans>
<ref bean="class1" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
</jaxrs:server>
<jaxrs:server id="bean2" address="/bean2">
<jaxrs:serviceBeans>
<ref bean="class2" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
</jaxrs:server>
I again tried to call web service with URL "/bean1/m1".
However, I received an error No root resource matching request path /m1 has been found.
Requesting help.
Looking at your configuration you now have a mapping that maps to:
/bean1/bean1/m1
/bean2/bean2/m3
You probably want to do something like this:
<jaxrs:server id="server" address="/">
<jaxrs:serviceBeans>
<ref bean="class1" />
<ref bean="class2" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
</jaxrs:server>
You can just define 2 servicebeans for the same server if you want. That should give you what you want.