JPA queue using Spring Integration - spring

I am trying to create a persistent event queue using Spring Integration. In a first version I want to use JPA (with a MySQL database), and in the future it is very possible to migrate to a JMS version (I want the transition to be so easy as possible).
I did an example version (without persisting with JPA), that do something similar to what I need:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans">
<int:channel id="customerEventChannel">
<int:queue/>
</int:channel>
<bean id="customerEventService" class="springintegration.CustomerEventService"/>
<int:outbound-channel-adapter ref="customerEventService" method="handleCustomerEvent" channel="customerEventChannel">
<int:poller fixed-delay="3000" max-messages-per-poll="1" />
</int:outbound-channel-adapter>
</beans>
But when I try to modify the example to use JPA inbound and outbound channel adapters, I cannot make it work.
My idea is to read the database every 5 seconds (configurable), and then process the elements in the "queue" recovered from database. On the other hand, I want to insert new elements in the database calling a method in a service class. For doing this, I tried:
<int:channel id="customerEventInputChannel" />
<int:channel id="customerEventOutputChannel" />
<int-jpa:inbound-channel-adapter channel="customerEventInputChannel"
entity-class="springintegration.CustomerEvent"
entity-manager-factory="entityManagerFactory"
auto-startup="true"
expect-single-result="true"
delete-after-poll="true">
<int:poller fixed-rate="5000">
<int:transactional propagation="REQUIRED" transaction-manager="transactionManager"/>
</int:poller>
</int-jpa:inbound-channel-adapter>
<int-jpa:outbound-channel-adapter channel="customerEventOutputChannel"
entity-class="springintegration.CustomerEvent"
entity-manager-factory="entityManagerFactory">
<int-jpa:transactional transaction-manager="transactionManager" />
</int-jpa:outbound-channel-adapter>
But I am missing something because I cannot read or write to database. I tried also with service-activator, bridge, gateway, etc., with same results.
Any help would be appreciated.

I recently found out that queueChannels can become persistent with a Jdbc.
See http://docs.spring.io/spring-integration/reference/html/jdbc.html chapter Backing Message Channels

Related

Does spring integration's tcp-inbound-channel-adapter and tcp-connection-factory have failover support?

I have a simple tcp client using spring integration that takes in a message, and writes it to file.
I want the client to connect by default to thing.input.primary.host, but in the event of a connection failure, I want it to reconnect to thing.input.secondary.host, and then send the message on like normal; is there support in spring integration for this behavior?
Edit: for clarity. What I'd like to do, is to have it switch from thingClient to thingClientSecondary, if thingClient fails.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip.xsd
http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<int-ip:tcp-inbound-channel-adapter
id="thingInGateway"
connection-factory="thingClientPrimary"
channel="fileOutputChain"
client-mode="true"
auto-startup="true"
retry-interval="1000"/>
<int-ip:tcp-connection-factory
id="thingClientPrimary"
type="client"
host="${thing.input.primary.host}"
port="${thing.input.primary.port}"
deserializer="thingSerializer"
serializer="thingSerializer"
socket-support="thingSocketSupport"
so-keep-alive="true"
using-nio="true"/>
<int-ip:tcp-connection-factory
id="thingClientSecondary"
type="client"
host="${thing.input.secondary.host}"
port="${thing.input.secondary.port}"
deserializer="thingSerializer"
serializer="thingSerializer"
socket-support="thingSocketSupport"
so-keep-alive="true"
using-nio="true"/>
<int:chain input-channel="fileOutputChain" >
<int-file:outbound-channel-adapter directory="${thing.output.directory}"/>
</int:chain>
</beans>
No; simply define a second channel adapter that uses the other factory and the same channel; then any data coming in on either port will go to the same place.
"Failover" is an unusual term on the server side. Normally, clients fail over when a server connection is lost.
On the server side, we are passive and the term "failover" makes no sense here.

How to guarantee message processing with Spring Integration MQTT Inbound Adapter

We are consuming messages from a HiveMQ MQTT Broker and process the data with spring integration. As the last processing step it is common for us to execute an update/insert statement on a relational database using an int-jpa:outbound-channel-adapter.
Let's consider a situation where the database connection is lost but mqtt messages are still consumed by the inbound mqtt adataper. Currently we lose these messages as we are not handling the failed database operations.
How should we handle the messages if the database connection is currently not available?
Should we implement a Message Store backed by another highly available database which is persisting messages from spring integration?
Should we implement a retry advice and periodically retry database operations?
As our starting point we tried to implement the retry advice with the following spring-context.xml. As a result the message was retried once and then an exception occured as it is not able to perform the database operation. No further retry was executed after the exception occurred.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-jpa="http://www.springframework.org/schema/integration/jpa"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jpa http://www.springframework.org/schema/integration/jpa/spring-integration-jpa.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<int:channel id="mqttInputChannel" />
<!-- MQTT INBOUND ADAPTER IS CONFIGURED IN JAVA -->
<int-jpa:outbound-channel-adapter
channel="mqttInputChannel"
flush-size=""
entity-class="com.iot.db.DefaultBean"
persist-mode="PERSIST"
entity-manager-factory="entityManagerFactory">
<int-jpa:transactional transaction-manager="transactionManager" />
<int-jpa:request-handler-advice-chain>
<ref bean="retryAdvice" />
</int-jpa:request-handler-advice-chain>
</int-jpa:outbound-channel-adapter>
<int:handler-retry-advice id="retryAdvice" />
</beans>
Another approach was to back up the messages via a message store but we weren't able to bind the message store to the jpa adapters transaction. Here is the corresponding spring-context.xml file with the message store:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-jpa="http://www.springframework.org/schema/integration/jpa"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jpa
http://www.springframework.org/schema/integration/jpa/spring-integration-jpa.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<int:channel id="mqttInputChannel">
<int:queue message-store="messageStore" />
</int:channel>
<bean
id="messageStore"
class="org.springframework.integration.jdbc.store.JdbcChannelMessageStore">
<property
name="dataSource"
ref="dataSource" />
<property
name="channelMessageStoreQueryProvider"
ref="queryProvider" />
</bean>
<bean
id="queryProvider"
class="org.springframework.integration.jdbc.store.channel.H2ChannelMessageStoreQueryProvider" />
<!-- MQTT INBOUND ADAPTER IS CONFIGURED IN JAVA -->
<int-jpa:outbound-channel-adapter
channel="mqttInputChannel"
flush-size=""
entity-class="com.iot.db.DefaultBean"
persist-mode="PERSIST"
entity-manager-factory="entityManagerFactory">
<int-jpa:transactional transaction-manager="transactionManager" />
<int-jpa:request-handler-advice-chain>
<ref bean="retryAdvice" />
</int-jpa:request-handler-advice-chain>
</int-jpa:outbound-channel-adapter>
<int:handler-retry-advice id="retryAdvice" />
</beans>
Are there any best practices or well known pattern how to handle this situation?
thanks in advance

Using Gateway from another application

I am using spring-boot, spring-integration and hornetq.
I have a central messaging bridge project "app-bridge" and a number of other projects that request information from the bridge. All projects are deployed as "war" files to a tomcat server.
I need to create a synchronous request from "app-1" to the "app-bridge" application ("app-bridge" makes an MQ request to a remote application for the response and I don't want to expose the way it gets the data to each individual application. ie only "app-bridge" should know how to get the response).
In "app-bridge" I have the following gateway defined.
<int:gateway service-interface="org.company.SendAndReceive"
default-request-channel="synchronousOutChannel"
default-reply-channel="synchronousInChannel"
default-reply-timeout="30000"
default-request-timeout="30000">
</int:gateway>
This works fine when run from the "app-bridge" project.
#Autowired
private final SendAndReceive sendAndReceive;
...
#Scheduled(fixedDelay = 30000L)
public void testing() {
sendAndReceive.send("HELLO");
String resposne = sendAndReceive.receive();
System.out.println(resposne); //prints the response or null if a timeout occurred
}
The problem is that I need to run this from the "app-1" project.
How can I achieve this?
UPDATE for #Gary
integration xml file in the app-bridge project.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.1.xsd
http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<!--************************* SENDING ********************************-->
<!-- handle errors -->
<int:channel id="as400SynchronousOutFailedChannel" />
<int-jms:outbound-channel-adapter
id="as400SynchronousOutFailed"
destination-name="as400.synchronous.out.failed"
channel="as400SynchronousOutFailedChannel"
connection-factory="jmsConnectionFactory"/>
<!-- Read local messages from hornet -->
<int:channel id="as400SynchronousOutChannel" />
<int-jms:message-driven-channel-adapter
id="jmsSynchronousAS400Out"
acknowledge="transacted"
destination-name="as400.synchronous.out"
channel="as400SynchronousOutChannel"
connection-factory="jmsConnectionFactory"
error-channel="as400SynchronousOutFailedChannel"
concurrent-consumers="1"
pub-sub-domain="false" />
<!-- Send messages to AS/400 -->
<int-jms:outbound-channel-adapter
id="jmsSynchronousOut"
destination="as400SynchronousOutQueue"
channel="as400SynchronousOutChannel"
jms-template="as400JmsTemplate">
<int-jms:request-handler-advice-chain>
<int:retry-advice max-attempts="3">
<int:exponential-back-off initial="2000" multiplier="2" />
</int:retry-advice>
</int-jms:request-handler-advice-chain>
</int-jms:outbound-channel-adapter>
<!-- Connection to remote AS/400 Queue -->
<bean id="as400SynchronousOutQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="AS400.SYNCHRONOUS.IN" />
<property name="targetClient">
<bean id="com.ibm.mq.jms.JMSC.MQJMS_CLIENT_NONJMS_MQ" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
</property>
</bean>
<!-- Place to put messages that have failed -->
<int-jms:outbound-channel-adapter
id="jmsAS400SynchronousOutFailed"
destination-name="as400.synchronous.out.failed"
channel="as400SynchronousOutFailedChannel"
connection-factory="jmsConnectionFactory"/>
<!--************************* RECEIVING ********************************-->
<!-- handle errors -->
<int:channel id="as400SynchronousInFailedChannel" />
<int-jms:outbound-channel-adapter
id="as400SynchronousInFailed"
destination-name="as400.synchronous.in.failed"
channel="as400SynchronousInFailedChannel"
connection-factory="jmsConnectionFactory"/>
<!-- Receive messages from AS/400 -->
<int:channel id="as400SynchronousInChannel">
<int:rendezvous-queue/>
</int:channel>
<int-jms:message-driven-channel-adapter
id="jmsAS400SynchronousIn"
acknowledge="transacted"
destination="as400SynchronousInQueue"
channel="as400SynchronousInChannel"
connection-factory="as400ConnectionFactory"
error-channel="as400SynchronousInFailedChannel"/>
<!-- Connection to remote AS/400 Queue -->
<bean id="as400SynchronousInQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="AS400.SYNCHRONOUS.OUT" />
</bean>
<int:gateway service-interface="com.example.bridge.as400.As400SendAndReceive"
default-request-channel="as400SynchronousOutChannel"
default-reply-channel="as400SynchronousInChannel"
default-reply-timeout="30000"
default-request-timeout="30000">
</int:gateway>
</beans>
com.example.bridge.as400.As400SendAndReceive java class.
public interface As400SendAndReceive {
public void send(final String message);
public String receive();
}
So I want all my other war applications (app-1, app-2, app-3) to be able to call the "com.example.bridge.as400.As400SendAndReceive" gateway somehow that is defined in the "app-bridge" war. It is also important that if say both "app-1" and "app-2" request a message, it is sent back to the correct requestor. The As400 message does not support HEADERS so it is being sent as a plain MQSTR.
The <int:gateway/> generates a local java API; you can't use it alone for requests to a remote system.
Your app-bridge should have a <int-jms:inbound-gateway/> to service requests over JMS.
The other apps would use an <int:gateway/> wired to send requests to an <int-jms:outbound-gateway/> configured to send messages to the same destination the app-1 inbound gateway is listening on.
EDIT:
The remote apps can't "call" the gateway in app-bridge; it's a simple java object that's only visible within app-bridge.
You need some kind of external communication between app-n and app-bridge. You can choose the technology of your choice, JMS, RMI, RabbitMQ, HTTP, etc, etc.
You need an <int-x:outbound-gateway/> in app-n and an <int-x:inbound-gateway/> in app-bridge.
Where x is whatever you choose to use for the communication. Explore the documentation to make your choice. Given you are already using JMS to talk to the AS/400, maybe JMS would be the best choice (but you need different queues).

Spring configuration issues not easy to find solution for

I have found several really awkward problems which took me 3hr+ to resolve. I was wondering if anyone could explain me why this is the case. I believe both of them belong to the very much same context so I have two questions. I hope reader will have patience as for me this is both intimidating and interesting behavior of sf.
I only know the error and resolution, but not satisfied until I understand:
Guidance I follow: Have one configuration file - use package scan inside of your root customConfig only(declared via mapping in web.xml), make sure servlet-context.xml only scans controllers' package. All other context files import via import directive at the very beginning of your customConfig.
1.1 Error if you do it other way: Dependency injection(of various components) will dramatically fail with multiple overlapping config package scanning.
1.2 Error if you do it other way: Transaction during service request of transactionManager in context with entityManagerFactory will fail if servlet-context.xml scans the same package. (i.e. same service package as your customConfig scans)
2: LocaleChangeInterceptor can only be declared in servlet-context - won't work in custom root configuration, reason unknown(doesn't work even if adding package scan for controllers package inside customConfig however now funny bit - SessionLocaleResolver on the other hand will work ok if defined in custom config! )
Q1: So it is me to blame who as human was mistakenly adding overlapping context-component package scan or it would be logical for Spring to resolve these collisions? Or they should be resolved but it doesn't work for me for some reason?
I observed fellow developer and smiled when he told it is best not to touch spring configuration nor try to improve it nor try to update it. I smiled, now I clearly don't(I now find myself intimated by this sf config violence), after all of this do you think it is ok to place everything inside just a single configuration file like servlet-context.xml ?
Q2: What is the magic behind LocaleChangeInterceptor, I've spent around 5 hours fixing it until just moved in "try-and-fail" mood it into servlet-context and it worked.
Second is a pure mystery to solve. Nothing fancy inside customConfig
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!--
<import resource="securityContext.xml"/> -->
<import resource="jpaContext.xml"/>
<context:annotation-config />
<context:component-scan base-package="com.org.app" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages" />
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en_GB" />
</bean>
<mvc:interceptors>
<bean
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang" />
</mvc:interceptors> ...
After firing ?lang=locale_LOCALE request nothing will happen - no error, no indication , app will load successfully and page will just be reloaded under the same locale.
However placing this interceptor code inside servlet-context.xml below will resolve on locale request successfully.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<context:component-scan base-package="com.org.app.controller" />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<interceptors>
<beans:bean
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang" />
</interceptors>
</beans:beans>
Your LocalChangeInterceptor and LocaleResolver must be defined in the servlet-context.xml. <context:annotation-driven /> is already implied by the use of <context:component-scan />
In your root context you are also scanning for #Controller you need to exclude them.
<context:component-scan base-package="com.org.app">
<context:exclude-filter type="annotation" value="org.springframework.stereotype.Controller" />
</context:compoment-scan>
Basically all the web related things (and the things which are used by the DispatcherServlet) must be loaded by the DispatcherServlet. Due to its nature it will only look into its own local application context for the beans it needs instead of in its parents.
The reason for this is that you can have multiple DispatcherServlets each with its own configuration, this would break if it would load the configuration from the root application context.

How do I make the JMS transport for CXF repost failed QPid messages? Alternately, is there a better solution that I'm missing?

I'm writing an emailer webservice for my company. One of the main requirements is guaranteed delivery, so we have a thin HTTP layer over the JMS transport, using a persistent QPid queue.
One of the issues I'm running into is the handling of errors during processing. If I simply roll back the transaction when there's an error, the message goes to the head of the queue. If the error is pervasive enough, this can lock up the entire queue until someone intervenes manually, and we would like to avoid this by posting back to the head so that messages can be processed in the meantime.
However, therein lie my problems. First, while AMQP has a mechanism to "reject and requeue" a message atomically instead of acknowledging it, JMS doesn't seem to have any analog for this feature, so the only way to access it is by casting, which ties me to a specific JMS implementation. Further, the JMS transport for CXF doesn't seem to have any means of overriding or injecting behavior at the transport level, which means I'm stuck either writing a bytecode agent or changing the code and recompiling just to get the behavior I want.
To work around the issue, I've toyed with the idea of implementing a fault handler in CXF that simply reconstructs the JMS message from the CXF message, and re-queues it. But then I can't use the transacted session, because the fault causes a rollback which I can't override, and then I'll wind up with a copy of the message on the head (from the rollback) and on the tail (from the re-queue). And I can't use CLIENT_ACKNOWLEDGE, because the JMS transport acknowledges the message before submitting it for processing, which means if the server goes down at the wrong time, I could lose a message.
So basically, as long as I'm stuck accepting the default behavior of the JMS transport, it seems impossible to get the behavior I want (requeueing of failed messages) without compromising data integrity.
A coworker has suggested eschewing the JMS transport entirely, and invoking the queue directly. The service implementation would then be a skeleton class that exists solely to put messages on the queue, and another process would implement a message listener. To me, this solution is sub-optimal because I lose the elegance of an agnostic web service, and I lose some scalability by coupling my implementation to the underlying technology.
I've also considered just writing a CXF transport for AMQP using the RabbitMQ client library. It would take longer but it would be something our company could continue using going forward, and perhaps something that could be contributed back to the CXF project. That said, I'm not wild about this idea either because of the amount of time involved writing, running and testing the code.
Here's my beans.xml for CXF:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:jms="http://cxf.apache.org/transports/jms"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/transports/jms http://cxf.apache.org/schemas/configuration/jms.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<context:component-scan base-package="com.edo" />
<bean id="jmsConnectionFactory" class="org.apache.qpid.client.AMQConnectionFactory">
<constructor-arg name="broker" value="tcp://localhost:5672"/>
<constructor-arg name="username" value="guest"/>
<constructor-arg name="password" value="guest"/>
<constructor-arg name="clientName" value=""/>
<constructor-arg name="virtualHost" value=""/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:explicitQosEnabled="true" p:deliveryMode="1" p:timeToLive="5000" p:connectionFactory-ref="jmsConnectionFactory" p:sessionTransacted="false" p:sessionAcknowledgeModeName="CLIENT_ACKNOWLEDGE" />
<bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration" p:connectionFactory-ref="jmsConnectionFactory" p:wrapInSingleConnectionFactory="false" p:jmsTemplate-ref="jmsTemplate" p:timeToLive="500000" p:sessionTransacted="false" p:concurrentConsumers="1" p:maxSuspendedContinuations="0" p:maxConcurrentConsumers="1" />
<jms:destination id="jms-destination-bean" name="{http://test.jms.jaxrs.edo.com/}HelloWorldImpl.jms-destination">
<jms:address jndiConnectionFactoryName="ConnectionFactory" jmsDestinationName="TestQueue">
<jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory"/>
<jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:5672"/>
</jms:address>
<jms:jmsConfig-ref>jmsConfig</jms:jmsConfig-ref>
</jms:destination>
<jaxrs:server id="helloWorld" address="/HelloWorld" transportId="http://cxf.apache.org/transports/jms">
<jaxrs:serviceBeans>
<ref bean="helloWorldBean"/>
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<bean class="com.edo.jaxrs.jms.test.FlowControlInInterceptor" p:periodMs="1000" p:permitsPerPeriod="18" />
</jaxrs:inInterceptors>
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.JSONProvider">
<property name="produceMediaTypes" ref="jsonTypes"/>
<property name="consumeMediaTypes" ref="jsonTypes"/>
</bean>
</jaxrs:providers>
</jaxrs:server>
<bean id="http-jms-config" class="com.edo.jaxrs.jms.test.HttpOverJmsConfig"
p:jmsFactory-ref="jmsConnectionFactory"
p:jmsDestinationName="TestQueue" />
<util:list id="jsonTypes">
<value>application/json</value>
<value>application/jettison</value>
</util:list>
</beans>
Is there something simple I'm missing? Or is there a better way to go about this that would sidestep the problem?
So - I'm taking my coworker's advice and not using the JMS transport for the web service. Instead, we're going to create a thin web service layer over Spring Integration. This should allow us the granularity of control we need without unnecessarily exposing the messaging layer.

Resources