I'm using spring integration to communicate via active mq. I've got the following config.
<integration:channel id="riskApprovalRequestChannel"/>
<integration:channel id="riskApprovalResponseChannel"/>
<jms:outbound-gateway id="riskApprovalServiceGateway"
request-destination-name="${risk.approval.queue.request}"
reply-destination-name="${risk.approval.queue.response}"
request-channel="riskApprovalRequestChannel"
reply-channel="riskApprovalResponseChannel"
connection-factory="jmsConnectionFactory"
receive-timeout="1000"/>
<integration:gateway id="riskApprovalService" service-interface="com.my.ServiceInterface"
default-request-channel="riskApprovalRequestChannel"
default-reply-channel="riskApprovalResponseChannel"/>
This works well except for the 1st request, which is slow. I always hit the 1 second timeout on the 1st request. There's obviously some lazy loading going on somewhere.
My question is, how do I do a full initialisation at start up to avoid the 1st request always timing out?
Cheers,
Peter
It is probably connection establishment that's the issue.
One thing you can do is wrap the vendor ConnectionFactory in a Spring CachingConnectionFactory (good idea to do this anyway, for performance reasons), and call createConnection() during initialization.
There are a number of ways to do that - including injecting the connection factory into some other bean, with an init-method, #PostConstruct method, or an InitializingBean with afterPropertiesSet(); it will be called during context initialization.
This will cause the cache's connection to be eagerly connected.
You might want to do it in a try block, to avoid the application context failing to initialize because the JMS broker is not available (of course messaging will still fail, but the app will be up and ready to connect when the broker is available).
Related
we have a larger multi service java spring app that declares about 100 exchanges and queues in RabbitMQ on startup. Some are declared explicitly via Beans, but most of them are declared implicitly via #RabbitListener Annotations.
#Component
#RabbitListener(
bindings = #QueueBinding(key = {"example.routingkey"},
exchange = #Exchange(value = "example.exchange", type = ExchangeTypes.TOPIC),
value = #Queue(name = "example_queue", autoDelete = "true", exclusive = "true")))
public class ExampleListener{
#RabbitHandler
public void handleRequest(final ExampleRequest request) {
System.out.println("got request!");
}
There are quite a lot of these listeners in the whole application.
The services of the application sometimes talk to each other via RabbitMq, so take a example Publisher that publishes a message to the Example Exchange that the above ExampleListener is bound to.
If that publish happens too early in the application lifecycle (but AFTER all the Spring Lifecycle Events are through, so after ApplicationReadyEvent, ContextStartedEvent), the binding of the Example Queue to the Example Exchange has not yet happend and the very first publish and reply chain will fail. In other words, the above Example Listener would not print "got request".
We "fixed" this problem by simply waiting 3 seconds before we start sending any RabbitMq messages to give it time to declare all queues,exchanges and bindings but this seems like a very suboptimal solution.
Does anyone else have some advice on how to fix this problem? It is quite hard to recreate as I would guess that it only occurs with a large amount of queues/exchanges/bindings that RabbitMq can not create fast enough. Forcing Spring to synchronize this creation process and wait for a confirmation by RabbitMq would probably fix this but as I see it, there is no built in way to do this.
Are you using multiple connection factories?
Or are you setting usePublisherConnection on the RabbitTemplate? (which is recommended, especially for a complex application like yours).
Normally, a single connection is used and all users of it will block until the admin has declared all the elements (it is run as a connection listener).
If the template is using a different connection factory, it will not block because a different connection is used.
If that is the case, and you are using the CachingConnectionFactory, you can call createConnection().close() on the consumer connection factory during initialization, before sending any messages. That call will block until all the declarations are done.
It´s more of a conceptual question: I currently have a working activemq queue which is consumed by a Java Spring application. Now I want the queue not to permanently delete the messages until the Java app tells it the message has been correctly saved in DB. After reading documentation I get I have to do it transactional and usa the commit() / rollback() methods. Correct me if I'm wrong here.
My problem comes with every example I find over the internet telling me to configure the app to work this or that way, but my nose tells me I should instead be setting up the queue itself to work the way I want. And I can't find the way to do it.
Otherwise, is the queue just working in different ways depending on how the consumer application is configured to work? What am I getting wrong?
Thanks in advance
The queue it self is not aware of any transactional system but you can pass the 1st parameter boolean to true to create a transactional session but i propose the INDIVIDUAL_ACKNOWLEDGE when creating a session because you can manage messages one by one. Can be set on spring jms DefaultMessageListenerContainer .
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE
And calling this method to ack a message, unless the method is not called the message is considered as dispatched but not ack.
ActiveMQTextMessage.acknowledge();
UPDATE:
ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE can be used like this :
onMessage(ActiveMQTextMessage message)
try {
do some stuff in the database
jdbc.commit(); (unless auto-commit is enabled on the JDBC)
message.acknowledge();
}
catch (Exception e) {
}
There are 2 kinds of transaction support in ActiveMQ.
JMS transactions - the commit() / rollback() methods on a Session (which is like doing commit() / rollback() on a JDBC connection)
XA Transactions - where the XASession acts as an XAResource by communicating with the Message Broker, rather like a JDBC Connection takes place in an XA transaction by communicating with the database.
http://activemq.apache.org/how-do-transactions-work.html
Should I use XA transactions (two phase commit?)
A common use of JMS is to consume messages from a queue or topic, process them using a database or EJB, then acknowledge / commit the message.
If you are using more than one resource; e.g. reading a JMS message and writing to a database, you really should use XA - its purpose is to provide atomic transactions for multiple transactional resources. For example there is a small window from when you complete updating the database and your changes are committed up to the point at which you commit/acknowledge the message; if there is a network/hardware/process failure inside that window, the message will be redelivered and you may end up processing duplicates.
http://activemq.apache.org/should-i-use-xa.html
I'm currently working on a java application. It's a standalone client with Spring and Hibernate. Also C3P0.
In the previous version we used a standard user(hardcoded in the configuration file) for the database connection but now we changed it so that every user has to provide his own credentials.
The beans with the code for the database are basically created on-demand.
I changed the XML-files and added a postprocessor which sets the credentials as well as some connection settings. It looks similar to this now:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
configurer = new PropertyPlaceholderConfigurer();
// properties are retrieved from a private method
configurer.setProperties(getProperties());
context.addBeanFactoryPostProcessor(configurer);
context.setConfigLocations(new String[] { "first.xml","second.xml" });
context.refresh();
return context.getBean("myClass", MyClass.class);
This all works as expected but now we reach the part where I'm currently stuck.
I want to provide a test functionality for the connection, so that the user can click a button and then is told if his credentials were accepted or not.
My first idea was to do a simple select on the database. Sifting through the logs however, I noticed that Spring tries to connect to the database during the refresh() (or rather the instantiation of the bean) anyway. I can see exceptions in the logs, for example: java.sql.SQLException: Connections could not be acquired from the underlying database!
Unfortunately, Spring doesn't seem to actually care. The exceptions are logged away but refresh() finishes and is not throwing any exceptions because of this. I had hoped that I could simply catch the exception and then I would know that the connection failed.
I could just do the select as planned, but I want to limit the connection attempts as much as possible, because the database server will block the user after several attempts. Even permanently if there are to many attempts(already had some fun with that, before I changed the settings for the connection pool).
My searches regarding this problem came up with practically nothing. Is there a way to get the exception somehow? Or does Spring provide an API of sorts that would tell me about the connection error during the instantiation/refresh?
Failing that, any ideas for an alternative approach? Preferably one that needs only a single attempt to determine if a connection is possible.
Edit: For anyone interested: I went with the suggestion from Santosh and implemented a connection test in JDBC.
Unfortunately there seems to be no easy way to make use of the database errors/exceptions encountered during the bean instantiation.
The kind of functionality you are looking for would be very tricky to accomplish using spring+hibernate.
The connection properties are set at the session-factory level and if credentials are incorrect, the session-factory is not instantiated.
Quoting #Bozo from his answer here.
What you can do is extend LocalSessionFactoryBean and override the
getObject() method, and make it return a proxy (via
java.lang.reflect.Proxy or CGLIB / javassist), in case the
sessionFactory is null. That way a SessionFactory will be injected.
The proxy should hold a reference to a bare SessionFactory, which
would initially be null. Whenever the proxy is asked to connect, if
the sessionFacotry is still null, you call the buildSessionFactory()
(of the LocalSessionFactoryBean) and delegate to it. Otherwise throw
an exception. (Then of course map your new factory bean instead of the
current)
There is also a simple and rudimentary approach wherein before creating ClassPathXmlApplicationContext, simply try to obtain a connection using raw JDBC calls. If that succeed then proceed or else give use appropriate message.
You can limit the connection attempts here as you are in full control.
I have a EJB to send a message to JMS queue and wait the reply from it. I want to test the EJB, it's easy to use OpenEJB to do the JUnit test of the EJB. But the problem is this EJB will wait the JMS response to continue process.
Although I can send message in my junit code, but because the EJB is still on-going, I cannot run it before the EJB is completed.
2nd solution is I can initialize a MDB to listen and reply the JMS message form the EJB, but the problem is the MDB must in src\main\java and cannot in src\test\java. The problem is this is just a test code and I should not package it to production environment. (I use Maven)
Or should I use mock object ?
You're on the right track. There area few ways to handle this. Here are a couple tips for unit testing with OpenEJB and Maven.
Test beans
You can write all sorts of EJBs and other testing utilities and have them deployed. All you need is a ejb-jar.xml for the test code like so:
src/main/resources/ejb-jar.xml (the normal one)
src/test/resources/ejb-jar.xml (the testing beans)
As usual the ejb-jar.xml file only needs to contain <ejb-jar/> and nothing more. Its existence simply tells OpenEJB to inspect that part of the classpath and scan it for beans. Scanning the entire classpath is very slow, so this is just convention to speed that up.
TestCase injection
With the above src/test/resources/ejb-jar.xml you could very easily add that test-only MDB and have it setup to process the request in a way that the TestCase needs. But the src/test/resources/ejb-jar.xml also opens up some other interesting functionality.
You could have the TestCase itself do it by declaring references to whatever JMS resources you need and have them injected.
import org.apache.openejb.api.LocalClient;
#LocalClient
public class ChatBeanTest extends TestCase {
#Resource
private ConnectionFactory connectionFactory;
#Resource(name = "QuestionBean")
private Queue questionQueue;
#Resource(name = "AnswerQueue")
private Queue answerQueue;
#EJB
private MyBean myBean;
#Override
protected void setUp() throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
InitialContext initialContext = new InitialContext(p);
initialContext.bind("inject", this); // here's the magic!
}
}
Now you're just one thread away from being able to respond to the JMS message the testcase itself. You can launch off a little runnable that will read a single message, send the response you want, then exit.
Maybe something like:
public void test() throws Exception {
final Thread thread = new Thread() {
#Override
public void run() {
try {
final Connection connection = connectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageConsumer incoming = session.createConsumer(requestQueue);
final String text = ((TextMessage) incoming.receive(1000)).getText();
final MessageProducer outgoing = session.createProducer(responseQueue);
outgoing.send(session.createTextMessage("Hello World!"));
} catch (JMSException e) {
e.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
myBean.doThatThing();
// asserts here...
}
See
Alternate Descriptors
If you did want to use the MDB solution and only wanted to enable it for just the one test and not all tests, you could define it in a special src/test/resources/mockmdb.ejb-jar.xml file and enable it in the specific test case(s) where it is needed.
See this doc for more information on how to enable that descriptor and the various options of alternate descriptors.
I think you should use mocks for this. If you're sending messages to a real JMS server, listening for them, replying to them, etc. then you're doing something other than a unit test. I'm not going to get into the argument about what that should be called, but I think it's pretty well universally accepted that a unit-test shouldn't be talking to live databases, message queues, etc.
If I've understood your question correct - It's a bad design to have an EJB send a JMS message and then await a response, in fact contradictory to the whole idea of EJB.
You send a JMS message, and then forget about it. You have an MDB to receive the message. If the EJB depends on a response, JMS is not the way to go, but rather use another EJB.
To test the sending, mock the JMS classes, test the MDB separately.
EJB's are designed for synchronous tasks, JMS for asynchronous tasks - if you have to do asynchronous communication to an external system, I suggest you design your system after that, and do proper asynchronous flows. An EJB that sits and waits for a JMS reply is at best an ugly hack, and will not add any good to your system design.
Thanks for David's answer, it's what I want. I know unit test should not depend on other external resource like JMS server. But if I use Maven + OpenEJB, I still can let the test code in a closed environment. It can help to do automatically test with external resource dependency, especially for some old programs which not easy to refactor.
And if you see the following error message in initialContext.bind("inject", this)
Ensure that class was annotated with #org.apache.openejb.api.LocalClient and was successfully discovered and deployed.
One reference is http://openejb.apache.org/3.0/local-client-injection.html, but add "openejb.tempclassloader.skip=annotations" doesn't work for me. Please check this doc OpenEJB Local Client Injection Fails. There is already a patch for it, I think it will be fixed in OpenEJB 3.1.5 or 4.0
Also I've found it is best practice to actually break out your logic in your MDB to a different class. This isolates your business logic from being in an MDB and allows you to expose your logic as more than one way (MDB, EJB, Web Service, POJO, etc.). It also allows you to more easily test your business logic without the need to test the protocol (JMS in this case).
As for testing JMS, mocking may be the better choice. Or if you really need to test the protocol "in container" look at using something like the JBoss Microcontainer (I believe you can get this packaged with some of the JBoss projects like Seam). Then you can fire up a mini-container for testing things like EJB and JMS.
But overall, it is best to avoid having to need a container unless absolutely necessary. That's why separating your business logic from your implementation logic (even if you don't use mocks) is a good practice.
We have a Java listener that reads text messages off of a queue in JBossMQ. If we have to reboot JBoss, the listener will not reconnect and start reading messages again. We just get messages in the listener's log file every 2 minutes saying it can't connect. Is there something we're not setting in our code or in JBossMQ? I'm new to JMS so any help will be greatly appreciated. Thanks.
You should implement in your client code javax.jms.ExceptionListener. You will need a method called onException. When the client's connection is lost, you should get a JMSException, and this method will be called automatically. The only thing you have to look out for is if you are intentionally disconnecting from JBossMQ-- that will also throw an exception.
Some code might look like this:
public void onException (JMSException jsme)
{
if (!closeRequested)
{
this.disconnect();
this.establishConnection(connectionProps, queueName, uname, pword, clientID, messageSelector);
}
else
{
//Client requested close so do not try to reconnect
}
}
In your "establishConnection" code, you would then implement a while(!initialized) construct that contains a try/catch inside of it. Until you are sure you have connected and subscribed properly, stay inside the while loop catching all JMS/Naming/etc. exceptions.
We've used this method for years with JBossMQ and it works great. We have never had a problem with our JMS clients not reconnecting after bouncing JBossMQ or losing our network connection.
I'd highly recommend you use the Spring abstractions for JMS such as the MessageListenerContainer to deal with reconnection, transactions and pooling for you. You just need to supply a MessageListener and configure the MessageListenerContainer with the ConnectionFactory and the container does the rest.
If you're purely a listener and do no other JMS calls other than connection setup, then the "onException() handler" answer is correct.
If you do any JMS calls in your code, just using onException() callback isn't sufficient. Problems are relayed from the JMS provider to the app either via an exception on a JMS method call or through the onException() callback. Not both.
So if you call any JMS methods from your code, you'll also want to invoke that reconnection logic if you get any exceptions on those calls.
Piece of advice from personal experience. Upgrade to JBoss Messaging. I've seen it in production for 4 months without problems. It has fully transparent failover - amongst many other features.
Also, if you do go with Spring, be very careful with the JmsTemplate.