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>
Related
Hello I have problem with my jms code when I try to send over 1000 messages to MDB. Following code:
#Stateless(mappedName = "RequestProcessingQueue")
public class RequestProcessingQueue {
private static final Logger logger = Logger.getLogger(RequestProcessingQueue.class);
#Resource(mappedName = "jmsRequestsFactory")
private ConnectionFactory connectionFactory;
#Resource(mappedName = "jmsRequestsDestination")
private Queue queue;
public void add(String participant, String password, List<Long> documents) throws JmsAppException {
try {
logger.debug("requests to process " + documents);
Connection connecton = connectionFactory.createConnection();
connecton.start();
Session session = connecton.createSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender sender = (QueueSender) session.createProducer(queue);
Message msg = msg = session.createMessage();
msg.setStringProperty("participant", participant);
msg.setStringProperty("password", password);
for (Long id : documents) {
msg.setLongProperty("request", id);
sender.send(msg);
}
sender.close();
session.close();
connecton.close();
} catch (JMSException e) {
throw new JmsAppException(e);
} catch (Throwable e) {
throw new JmsAppException("Fatal error occured while sending request to be processed", e);
}
}
}
throws
MQJMSRA_DS4001: JMSServiceException on send message:sendMessage: Sending message failed. Connection ID: 2979509408914231552 com.sun.messaging.jms.ra.DirectSession._sendMessage(DirectSession.java:1844) / sendMessage: Sending message failed. Connection ID: 2979509408914231552 com.sun.messaging.jmq.jmsserver.service.imq.IMQDirectService.sendMessage(IMQDirectService.java:1955) / transaction failed: [B4303]: The maximum number of messages [1 000] that the producer can process in a single transaction (TID=2979509408914244096) has been exceeded. Please either limit the # of messages per transaction or increase the imq.transaction.producer.maxNumMsgs property. com.sun.messaging.jmq.jmsserver.data.handlers.DataHandler.routeMessage(DataHandler.java:467)'}
at jms.example.RequestProcessingQueue.add(RequestProcessingQueue.java:48)
I do not understand why cus when I create session I pass false as first param indicating that session is non transactional mode.
Your code does not work because the basic JMS API was designed to work in any environment, not just from within an EJB container. Runtime environment programming restrictions and behaviour are described in the EJB specifications and JavaDoc, in particular javax.jms.Connection.createSession(boolean transacted, int acknowledgeMode).
Your code can be simplified (assuming you're using at least Java 7) to:
#TransactionAttribute(TransactionAttributeType.NOTSUPPORTED)
public void add(String participant, String password, List<Long> documents) throws OgnivoException {
try (Connection connection = connectionFactory.createConnection();
Session session = connection.createSession();
// session.start() not required
MessageProducer sender = session.createProducer(queue)) {
logger.debug("requests to process " + documents);
for (Long id : documents) {
Message msg = msg = session.createMessage();
msg.setStringProperty("participant", participant);
msg.setStringProperty("password", password);
msg.setLongProperty("request", id);
sender.send(msg);
}
} catch (JMSException e) {
throw new JmsAppException(e);
}
// Don't catch throwable because it hides bugs
}
Remember that EJB methods are automatically associated with a transaction unless you specify otherwise. Additionally, be sure to check the javadoc for javax.jms.Connection.createSession() and associated methods, particularly the sections describing behaviour in different runtime environments.
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.
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 dont understand what is not good and i need help with this code
so this is the code
package chat;
import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.io.InputStreamReader;
import java.util.Properties;
public class pub implements javax.jms.MessageListener{
private TopicSession pubSession;
private TopicSession subSession;
private TopicPublisher publisher;
private TopicConnection connection;
private String username;
/* Constructor. Establish JMS publisher and subscriber */
public pub(String topicName, String username, String password)
throws Exception {
// Obtain a JNDI connection
Properties env = new Properties( );
env.put(Context.SECURITY_PRINCIPAL, "guest");
env.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
// ... specify the JNDI properties specific to the vendor
InitialContext jndi = new InitialContext(env);
// Look up a JMS connection factory
TopicConnectionFactory conFactory =
(TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");
// Create a JMS connection
TopicConnection connection =
conFactory.createTopicConnection(username,password);
// 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)jndi.lookup("topic/testTopic");
// Create a JMS publisher and subscriber
TopicPublisher publisher1 =
pubSession.createPublisher(chatTopic);
TopicSubscriber subscriber =
subSession.createSubscriber(chatTopic);
// Set a JMS message listener
subscriber.setMessageListener(this);
// Intialize the Chat application
set(connection, pubSession, subSession, publisher1, username);
// Start the JMS connection; allows messages to be delivered
connection.start( );
}
/* Initialize the instance variables */
public void set(TopicConnection con, TopicSession pubSess,
TopicSession subSess, TopicPublisher pub,
String username) {
this.connection = con;
this.pubSession = pubSess;
this.subSession = subSess;
this.publisher = pub;
this.username = username;
}
/* Receive message from topic subscriber */
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText( );
System.out.println(text);
} catch (JMSException jmse){ jmse.printStackTrace( ); }
}
/* Create and send message using topic 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("Topic or username missing");
// args[0]=topicName; args[1]=username; args[2]=password
pub chat = new pub("chat","temp","111");
// 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( ); // close down connection
System.exit(0);// exit program
} else
chat.writeMessage(s);
}
} catch (Exception e){ e.printStackTrace( ); }
}
}
and this is the erorr i get when i run it in eclipce:
javax.jms.JMSSecurityException: User: temp is NOT authenticated
at org.jboss.mq.security.SecurityManager.authenticate(SecurityManager.java:230)
at org.jboss.mq.security.ServerSecurityInterceptor.authenticate(ServerSecurityInterceptor.java:66)
at org.jboss.mq.server.TracingInterceptor.authenticate(TracingInterceptor.java:613)
at org.jboss.mq.server.JMSServerInvoker.authenticate(JMSServerInvoker.java:172)
at org.jboss.mq.il.uil2.ServerSocketManagerHandler.handleMsg(ServerSocketManagerHandler.java:238)
at org.jboss.mq.il.uil2.SocketManager$ReadTask.handleMsg(SocketManager.java:419)
at org.jboss.mq.il.uil2.msgs.BaseMsg.run(BaseMsg.java:398)
at EDU.oswego.cs.dl.util.concurrent.PooledExecutor$Worker.run(PooledExecutor.java:756)
at java.lang.Thread.run(Unknown Source)
i am not understand if the problem emanative from the jboss connection or the code is not ok pleas i need help to run this program thanks!!!!
It seems that the user you are attempting to access the JMS provider with is likely not permitted to access the service.
Are you sure that the username and password are correct and have been granted the correct privileges to access the provider?
There are two programs: subscriber and publisher...
Subscriber is able to put the message onto the topic and the message is sent successfully.
When I check the activemq server on my browser it shows 1 msg enqueued . But when I run the consumer code, it is not receiving the message
Here is the producer code:
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class producer {
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
// JMS messages are sent and received using a Session. We will
// create here a non-transactional session object. If you want
// to use transactions you should set the first parameter to 'true'
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("testt");
MessageProducer producer = session.createProducer(topic);
// We will send a small text message saying 'Hello'
TextMessage message = session.createTextMessage();
message.setText("HELLO JMS WORLD");
// Here we are sending the message!
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
connection.close();
}
}
After I run this code the output at the console is:
26 Jan, 2012 2:30:04 PM org.apache.activemq.transport.failover.FailoverTransport doReconnect
INFO: Successfully connected to tcp://localhost:61616
Sent message 'HELLO JMS WORLD'
And here is the consumer code:
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class consumer {
// URL of the JMS server
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
// Name of the topic from which we will receive messages from = " testt"
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("testt");
MessageConsumer consumer = session.createConsumer(topic);
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listner);
connection.close();
}
}
After I run this code it doesnt show anything.
Can someone help to me to overcome this problem?
Your issue is that your consumer is running and then shutting down immediately.
Try adding this into your consumer:
consumer.setMessageListener(listner);
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
connection.close();
This will wait until you hit a key before stopping.
Other things to consider:
Use a finally block for the close
Java naming conventions encourage using uppercase for the first letter of a class
The main problem (besides the app closing down to quickly) is that you are sending to a Topic. Topics don't retain messages so if you run your application that produces and then run the consumer, the consumer won't receive anything because it was not subscribed to the topic at the time the message was sent. If you fix the shutdown issue and then run the consumer in one terminal and then run the producer you should then see the message received by your consumer. If you want message retention then you need to use a Queue which will hold onto the message until someone consumes it.
Your producer class is correct. It runs smoothly.
But, your consumer is incorrect & you have to modify it.
First, add setClientID("any_string_value") after creating connection object;
eg: Connection connection = connectionFactory.createConnection();
// need to setClientID value, any string value you wish
connection.setClientID("12345");
secondly, use createDurableSubscriber() method instead of createConsumer() for transmitting message via topic.
MessageConsumer consumer = session.createDurableSubscriber(topic,"SUB1234");
Here is the modified comsumer class:
package mq.test;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class consumer {
// URL of the JMS server
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
// Name of the topic from which we will receive messages from = " testt"
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
// need to setClientID value, any string value you wish
connection.setClientID("12345");
try{
connection.start();
}catch(Exception e){
System.err.println("NOT CONNECTED!!!");
}
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("test_data");
//need to use createDurableSubscriber() method instead of createConsumer() for topic
// MessageConsumer consumer = session.createConsumer(topic);
MessageConsumer consumer = session.createDurableSubscriber(topic,
"SUB1234");
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message"
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
e.printStackTrace();
}
}
};
consumer.setMessageListener(listner);
//connection.close();
}
}
Now, your code will run successfully.
just some:
work with a queue not a topic. messages in topics will be discarded when no consumer is available, they are NOT persistend.
add connection.start() after setting the message listener. you should start a connection when all consumers/producers are properly set up.
wait some time before before closing the connection again.
the topic will probably be your most important source of failure.