I'm trying to write my first simplest JMS application using ActiveMQ embedded broker. The application is a sping desktop applction and here is its main method:
public static void main( String[] args ) throws JMSException
{
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/appContext.xml");
ConnectionFactory cf = (ConnectionFactory) context.getBean("jmsFactory");
Connection connetcion = null;
try{
connetcion = cf.createConnection();
final Session session = connetcion.createSession(false, Session.AUTO_ACKNOWLEDGE);
final Destination testQueue = (Destination) context.getBean("testQueue");
String text = "Test message";
MessageProducer mp = session.createProducer(testQueue);
Message msg = session.createTextMessage(text);
mp.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
mp.send(msg);
MessageConsumer mc = cf.createConnection().createSession(false, Session.AUTO_ACKNOWLEDGE).createConsumer(testQueue);
TextMessage recievedMsg = null;
recievedMsg = (TextMessage) mc.receive(); //1 <---------- Here is the problem
System.out.println(recievedMsg.getText());
} catch(JMSException jmse){
throw new AssertionError("JMS Exception");
}
finally{
connetcion.close();
}
}
The spring configuration contains the following beans:
<amq:broker useJmx="false" persistent="false">
<amq:transportConnectors>
<amq:transportConnector uri="tcp://localhost:61616" />
</amq:transportConnectors>
</amq:broker>
<amq:queue id="testQueue" physicalName="com.badmitrii.test" />
<amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:61616" />
I tried to send a message to the queue and then consume the message later in the same thread. But when I call receive() method at //1 the main thread ios blocked like there is no any mesasge in the queue.
What did I do wrong?
looks like you didn't start() your connection.
connetcion = cf.createConnection();
connetcion.start();
should do the trick.
Related
I want to load the JMS messages in a queue in real time, if is a consumer.setMessageListener(myListener) viable and how it works?
I'm not sure how a listener working. I wrote a listener like below.
public class JmsMessageListenerExample {
public static void main(String[] args) throws URISyntaxException, Exception {
BrokerService broker = BrokerFactory.createBroker(new URI(
"broker:(tcp://localhost:61616)"));
broker.start();
Connection connection = null;
try {
// Producer
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
"tcp://localhost:61616");
connection = connectionFactory.createConnection();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("customerQueue");
String payload = "Important Task";
Message msg = session.createTextMessage(payload);
MessageProducer producer = session.createProducer(queue);
System.out.println("Sending text '" + payload + "'");
producer.send(msg);
// Consumer
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new ConsumerMessageListener("Consumer"));
connection.start();
} finally {
if (connection != null) {
connection.close();
}
broker.stop();
}
}
After I run the JmsMessageListenerExample, it completed quickly and I didn't received any messages. If a listener should keep on running until I stop it?
When you set a JMS message listener it will receive messages asynchronously in its own thread (invoked by the JMS implementation). In your case you need to prevent main from exiting and stopping your application because when that happens the MessageListener will be terminated.
Also, when you say "load the JMS messages in a queue in real time" I assume you mean "consume JMS messages from a queue as soon as possible when queue receives them." If that's the case then a JMS message listener is the right approach.
How do I configure my J2EE application so that I can invoke ActiveMQ service along with tomcat server? I am aware about embedded broker, here asking how to start the ActiveMQ whenever I start tomcat
Current Code (works fine) :
Now I want to remove main() method and use the code to run when tomcat runs.
public class JMSService {
public void produceJMS() throws NamingException, JMSException {
ConnectionFactory connFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection conn = connFactory.createConnection();
conn.start();
Session session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("testQueue");
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
TextMessage message = session.createTextMessage("Test Message ");
// send the message
producer.send(message);
System.out.println("sent: " + message);
}}
Here is my consumer :
public class JMSReceiver implements MessageListener,ExceptionListener {
public static void main(String args[]) throws Exception {
JMSReceiver re = new JMSReceiver();
re.receiveJMS();
}
public void receiveJMS() throws NamingException, JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// Getting the queue 'testQueue'
Destination destination = session.createQueue("testQueue");
MessageConsumer consumer = session.createConsumer(destination);
// set an asynchronous message listener
JMSReceiver asyncReceiver = new JMSReceiver();
consumer.setMessageListener(asyncReceiver);
connection.setExceptionListener(asyncReceiver);
}
#Override
public void onMessage(Message message) {
System.out.println("Received message : " +message);
}
}
What #Tim Bish said is correct. You either need to have a timer say for example receiver should listen for 1 hour- or make it available until program terminate. Either case you need to start your consumer program once:
Change your receiveJMS method as follows:
public void receiveJMS() throws NamingException, JMSException {
try{
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection connection = connectionFactory.createConnection();
connection.start(); // it's the start point
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// Getting the queue 'testQueue'
Destination destination = session.createQueue("testQueue");
MessageConsumer consumer = session.createConsumer(destination);
// set an asynchronous message listener
// JMSReceiver asyncReceiver = new JMSReceiver();
//no need to create another object
consumer.setMessageListener(this);
connection.setExceptionListener(this);
// connection.close(); once this is closed consumer no longer active
Thread.sleep(60 *60 * 1000); // receive messages for 1 hour
}finally{
connection.close();// after 1 hour close it
}
}
The above program will listen upto 1 hour. If you want it as long as the program run, remove the finally block. But the recommended way is to close it somehow. since your application seems to be standalone ,you can check the java runtime shutdown hook, where you can specify how to release such resources while program terminates.
If your consumer is a web application you can close it in a ServletContextlistner.
You aren't giving the consumer application any time to actually receive a message, you create it, then you close it. You either need to use a timed receive call to do an sync receive of the message from the Queue or you need to add some sort of wait in the main method such as a CountDownLatch etc to allow the async onMessage call to trigger shutdown once processing of the message is complete.
I'm learning jms and found a sample Chat application but when i try running it, I get a security exception. It seems i need to pass in username and password when creating a new topic session, but the tutorial does not mention this.
Chat app code :
package ch02.chat;
import java.io.*;
import javax.jms.*;
import javax.naming.*;
public class Chat implements javax.jms.MessageListener {
private TopicSession pubSession;
private TopicPublisher publisher;
private TopicConnection connection;
private String username;
/* Constructor used to Initialize Chat */
public Chat(String topicFactory, String topicName, String username)
throws Exception {
// Obtain a JNDI connection using the jndi.properties file
InitialContext ctx = new InitialContext();
// Look up a JMS connection factory and create the connection
TopicConnectionFactory conFactory =
(TopicConnectionFactory)ctx.lookup(topicFactory);
TopicConnection connection = conFactory.createTopicConnection();
// Create two JMS session objects
TopicSession pubSession = connection.createTopicSession(
false, Session.AUTO_ACKNOWLEDGE);
TopicSession subSession = connection.createTopicSession(
false, Session.AUTO_ACKNOWLEDGE);
// Look up a JMS topic
Topic chatTopic = (Topic)ctx.lookup(topicName);
// Create a JMS publisher and subscriber. The additional parameters
// on the createSubscriber are a message selector (null) and a true
// value for the noLocal flag indicating that messages produced from
// this publisher should not be consumed by this publisher.
TopicPublisher publisher =
pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber =
subSession.createSubscriber(chatTopic, null, true);
// Set a JMS message listener
subscriber.setMessageListener(this);
// Intialize the Chat application variables
this.connection = connection;
this.pubSession = pubSession;
this.publisher = publisher;
this.username = username;
// Start the JMS connection; allows messages to be delivered
connection.start();
}
/* Receive Messages From Topic Subscriber */
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
System.out.println(textMessage.getText());
} catch (JMSException jmse){ jmse.printStackTrace(); }
}
/* Create and Send Message Using Publisher */
protected void writeMessage(String text) throws JMSException {
TextMessage message = pubSession.createTextMessage();
message.setText(username+": "+text);
publisher.publish(message);
}
/* Close the JMS Connection */
public void close() throws JMSException {
connection.close();
}
/* Run the Chat Client */
public static void main(String [] args) {
try {
if (args.length!=3)
System.out.println("Factory, Topic, or username missing");
// args[0]=topicFactory; args[1]=topicName; args[2]=username
Chat chat = new Chat(args[0],args[1],args[2]);
// Read from command line
BufferedReader commandLine = new
java.io.BufferedReader(new InputStreamReader(System.in));
// Loop until the word "exit" is typed
while(true) {
String s = commandLine.readLine();
if (s.equalsIgnoreCase("exit")){
chat.close();
System.exit(0);
} else
chat.writeMessage(s);
}
} catch (Exception e) { e.printStackTrace(); }
}
}
I have the following jndi.properties file in my classpath:
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
java.naming.security.principal=system
java.naming.security.credentials=manager
connectionFactoryNames = TopicCF
topic.topic1 = jms.topic1
Active mq is running (I can access the admin console). And this is the error that i get when running this app and passing parameters 'TopicCF topic1 Fred':
javax.jms.JMSException: User name [null] or password is invalid.
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:49)
at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1391)
at org.apache.activemq.ActiveMQConnection.ensureConnectionInfoSent(ActiveMQConnection.java:1496)
at org.apache.activemq.ActiveMQConnection.createSession(ActiveMQConnection.java:325)
at org.apache.activemq.ActiveMQConnection.createTopicSession(ActiveMQConnection.java:1122)
at ch02.chat.Chat.<init>(Chat.java:47)
at ch02.chat.Chat.main(Chat.java:105)
Caused by: java.lang.SecurityException: User name [null] or password is invalid.
at org.apache.activemq.security.SimpleAuthenticationBroker.addConnection(SimpleAuthenticationBroker.java:81)
at org.apache.activemq.broker.MutableBrokerFilter.addConnection(MutableBrokerFilter.java:91)
at org.apache.activemq.broker.TransportConnection.processAddConnection(TransportConnection.java:766)
at org.apache.activemq.broker.jmx.ManagedTransportConnection.processAddConnection(ManagedTransportConnection.java:79)
at org.apache.activemq.command.ConnectionInfo.visit(ConnectionInfo.java:139)
at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:329)
at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:184)
at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:50)
at org.apache.activemq.transport.WireFormatNegotiator.onCommand(WireFormatNegotiator.java:113)
at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:288)
at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:214)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:196)
at java.lang.Thread.run(Thread.java:745)
I don't yet have topic1 created anywhere, but that should throw a different exception.
The credentials that you log in with to the broker are defined in the JMS API when you establish a connection. To pass in a username and password, do the following:
TopicConnection connection =
conFactory.createTopicConnection(username, password);
The correct way is, as menstioned by Jake, to use the topic connection factory constructor with username and password :
TopicConnection connection =
conFactory.createTopicConnection(username, password);
There is another option, in activemq we can allow anonymous access by adding the simple authentication plugin in activemq.xml configuration file and setting anonymousAccessAllowed="true".
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">
<plugins>
<!-- users and passwords -->
<simpleAuthenticationPlugin anonymousAccessAllowed="true">
<users>
<authenticationUser username="system" password="manager" groups="admins,publishers,consumers" />
</users>
</simpleAuthenticationPlugin>
</plugins>
I'm trying to write a test that simulates a "broker down" phase.
Therefore I want to
start a local broker
send message1
stop the broker
send message2 (which will of course not arrive)
start the broker again
send message3
According to http://activemq.apache.org/how-do-i-restart-embedded-broker.html it is recommended to init a new BrokerService to start the broker again.
So the code looks (almost) like this:
private BrokerService _broker;
private void startBroker() throws Exception {
_broker = new BrokerService();
_broker.addConnector("vm://localhost?broker.persistent=false");
_broker.start();
_broker.waitUntilStarted();
}
private void stopBroker() throws Exception {
_broker.stop();
_broker.waitUntilStopped();
}
#Test
public void publishMessagesWithServerBreakdownInBetween()
throws Exception
{
startBroker();
... send and receive message (works fine)
stopBroker();
... send message (fails of course)
startBroker(); // this fails with java.io.IOException: VMTransportServer already bound at: vm://localhost?broker.persistent=false
... send and receive message
}
The problem is already mentioned as comment in code:
The restart of the broker fails due to the error : java.io.IOException: VMTransportServer already bound at: vm://localhost?broker.persistent=false
I found a similar problem at ActiveMQ forum (http://activemq.2283324.n4.nabble.com/VMTransportServer-already-bound-td2364603.html), but in my case the hostname isn't null.
Another idea was to set 2 different broker names, but that also didn't help.
What am I doing wrong?
You want to control what the VM Transport does by telling it not to try and create a broker for you since you are adding it to an already created broker. The rest is pretty simply then:
public class AMQRestartTest {
private BrokerService broker;
private String connectorURI;
private ActiveMQConnectionFactory factory;
#Before
public void startBroker() throws Exception {
createBroker(true);
factory = new ActiveMQConnectionFactory("failover://" + connectorURI);
}
private void createBroker(boolean deleteAllMessages) throws Exception {
broker = new BrokerService();
TransportConnector connector = broker.addConnector("vm://localhost?create=false");
broker.setPersistent(false);
broker.start();
broker.waitUntilStarted();
connectorURI = connector.getConnectUri().toString();
}
#Test(timeout = 60_000)
public void test() throws Exception {
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("test");
MessageConsumer consumer = session.createConsumer(queue);
MessageProducer producer = session.createProducer(queue);
connection.start();
broker.stop();
broker.waitUntilStopped();
createBroker(false);
producer.send(session.createTextMessage("help!"));
Message received = consumer.receive();
assertNotNull(received);
assertTrue(received instanceof TextMessage);
}
}
I am an ActiveMQ begginer. My main looks like this:
public static void main(String[] args) throws Exception {
BrokerService broker = new BrokerService();
if(isProducer(args)){
broker.addConnector("tcp://localhost:8001");
broker.start();
// start producer...
}
else{
broker.addConnector("tcp://localhost:9000");
broker.addNetworkConnector("static:(tcp://localhost:8001)");
broker.start(); // Getting stuck here!!!
// start consumer
}
waitForever();
}
I start this application twice, once a a producer and once as a consumer.
When I start the consumer, it gets stuck on the broker.start() line.
What am I missing?!
Basicly you start the broker once (embedding it into a jvm).
BrokerService broker = new BrokerService();
broker.setUseJmx(true);
broker.addConnector("tcp://localhost:61616");
broker.start();
Then you connect to the broker (this code is needed in both consumer and producer application):
url = "vm://localhost:61616" //if you are in same jvm
url2 = "tcp://localhost:61616" //if you are in diff jvm or other host
connectionFactory = new ActiveMQConnectionFactory(username,password,url);
connection = (ActiveMQConnection) connectionFactory.createConnection();
connection.start();
session = connection.createSession(transacted, ackMode);
Then setup a consumer
Destination queue = session.createQueue("queuename");
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new MessageConsumer());
Setup a producer and send a message
MessageProducer producer = session.createProducer(queue);
ObjectMessage objectMessage = session.createObjectMessage();
objectMessage.setObject(object);
producer.send(objectMessage);
look at for example: http://jmsexample.zcage.com/index2.html
or http://svn.apache.org/repos/asf/activemq/branches/activemq-5.6/assembly/src/release/example/src/