So I have a simple JMS app, using a topic, powered by activeMQ. It works, but only 1 message is being sent (even though I am writing more lines in the console and so trying to send more stuff).
When I check the web console of ActiveMq, only 1 message is being sent (I also get this message in the ReceiverTopic class)...Why is this happening?
Below you can see my sender code:
package topic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class SenderTopic {
private ConnectionFactory factory = null;
private Connection connection = null;
private Session session = null;
private Destination destination = null;
private MessageProducer producer = null;
private boolean jmsInitialized = false;
public SenderTopic() {
}
private void initJMS() throws JMSException {
factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("SAMPLE_TOPIC");
producer = session.createProducer(destination);
jmsInitialized = true;
}
private void sendMessage(String message) {
if (!jmsInitialized) {
try {
initJMS();
sendTextMessage(message);
} catch (JMSException e) {
jmsInitialized = false;
e.printStackTrace();
}
}
}
private void sendTextMessage(String message) throws JMSException {
TextMessage textMessage = session.createTextMessage(message);
producer.send(textMessage);
}
public static void main(String[] args) throws IOException {
SenderTopic receiver = new SenderTopic();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String message = reader.readLine();
receiver.sendMessage(message);
}
}
}
Intially the value of jmsInitialized is false, so your if condition (!jmsInitialized) will be true.
On second call of sendMessage the value of jmsInitialized will be true and the if condition fails because you are using not on boolean value.
You can add a else condition with only call to sendTextMessage.
try out this
private void sendMessage(String message) {
try {
if (!jmsInitialized) {
initJMS();
sendTextMessage(message);
}else{
sendTextMessage(message);
}
} catch (JMSException e) {
jmsInitialized = false;
e.printStackTrace();
}
}
}
Related
When I'm running a Java WebSocketStompClient, I got below error:
org.eclipse.jetty.websocket.api.MessageTooLargeException: Text message size [73728] exceeds maximum size [65536]
Sample code:
import org.apache.log4j.Logger;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class HelloClient {
private static Logger logger = Logger.getLogger(HelloClient.class);
StompSession session;
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
public ListenableFuture<StompSession> connect() {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
long[] hb = stompClient.getDefaultHeartbeat();
boolean en = stompClient.isDefaultHeartbeatEnabled();
long timeout = stompClient.getReceiptTimeLimit();
String url = "https://www.test.com";
return stompClient.connect(url, headers, new MyHandler());
}
public void subscribeMsg(StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe("/topic/test", new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
logger.info("Received message " + new String((byte[]) o));
String response = new String((byte[]) o);
}
});
}
private class MyHandler extends StompSessionHandlerAdapter {
public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {
logger.info("Now connected");
session = stompSession;
}
}
public boolean isConnected() {
try {
Thread.sleep(500);
return session != null && session.isConnected();
} catch (Exception e) {
logger.warn("Error happens when checking connection status, ", e);
return false;
}
}
public static void main(String[] args) throws Exception {
HelloClient helloClient = new HelloClient();
ListenableFuture<StompSession> f = helloClient.connect();
StompSession stompSession = f.get();
helloClient.subscribeMsg(stompSession);
while (true) {
if (!helloClient.isConnected()) {
logger.info("wss diconnected ");
logger.info("need re-create ");
}
}
}
}
How to increase the limitation for a Java stomp websocket client? I found some not related answers How can I set max buffer size for web socket client(Jetty) in Java which are not suitable for stomp websocket client.
Also tried stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE); which doesn't work.
I have been looking for some documentation/example for checking if a dynamically created topic exist and if it does, how to get the subscriber count for the topic.
I use following code for sending out message to a topic -
jmsTemplate.send(destination, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage();
message.setText(commandStr);
return message;
}
});
This code seems to create the topic and publish message to topic.
I need to check if the topic exists before creating it.
Check if the topic have any subscriber.
Thanks in advance
i was able to find the solution to (1) problem (Hope this helps)-
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
ActiveMQConnection connection = (ActiveMQConnection)connectionFactory.createConnection();
connection.start();
DestinationSource ds = connection.getDestinationSource();
Set<ActiveMQTopic> topics = ds.getTopics();
To get the destination names, as you did it is correct, you can do it by JMX too specifically to get statistical information like subscriber count ...
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.activemq.broker.jmx.BrokerViewMBean;
import org.apache.activemq.broker.jmx.TopicViewMBean;
public class JMXGetDestinationInfos {
public static void main(String[] args) throws Exception {
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
Map<String, String[]> env = new HashMap<>();
String[] creds = { "admin", "admin" };
env.put(JMXConnector.CREDENTIALS, creds);
JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName activeMq = new ObjectName("org.apache.activemq:type=Broker,brokerName=localhost");
BrokerViewMBean mbean = MBeanServerInvocationHandler.newProxyInstance(conn, activeMq, BrokerViewMBean.class,
true);
for (ObjectName name : mbean.getTopics()) {
if (("YOUR_TOPIC_NAME".equals(name.getKeyProperty("destinationName")))) {
TopicViewMBean topicMbean = MBeanServerInvocationHandler.newProxyInstance(conn, name,
TopicViewMBean.class, true);
System.out.println(topicMbean.getConsumerCount());
}
}
}
}
or
import java.util.Set;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.advisory.DestinationSource;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
public class AdvisorySupportGetAllDestinationsNames {
public static void main(String[] args) throws JMSException {
Connection conn = null;
try {
ConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616");
conn = cf.createConnection();
conn.start();
DestinationSource destinationSource = ((ActiveMQConnection) conn).getDestinationSource();
Set<ActiveMQQueue> queues = destinationSource.getQueues();
Set<ActiveMQTopic> topics = destinationSource.getTopics();
System.out.println(queues);
System.out.println(topics);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
}
}
}
}
}
UPDATE
You can use AdvisorySupport.getConsumerAdvisoryTopic()
Note that the consumer start/stop advisory messages also have a
consumerCount header to indicate the number of active consumers on the
destination when the advisory message was sent.
I am trying JMS 2.0 so I can decide if it is worth applying in my project. I could successfully create a send/receive application.
Now I would like to have listeners that will receive the message as soon as it is available on the queue (my final goal is to have different listeners to the same queue, each with a different message selector.
Currently I have this class:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType = 3;
private transient JMSConsumer consumer;
private transient JMSConsumer consumer2;
private transient JMSConsumer consumer3;
public SenderBean() {
}
#PostConstruct
public void setUp(){
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
// consumer2 = context.createConsumer(myQueue, "type=2");
// consumer2.setMessageListener(new ListenerTypeTwo());
//
// consumer3 = context.createConsumer(myQueue, "type=3");
// consumer3.setMessageListener(new ListenerTypeThree());
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
System.out.println("producer sending msg type " + nextType + "value: " + text);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeTwo implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeThree implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
I commented out two consumers, so I could focus on making one work.
I keep getting the following exception on the setMessageListener line:
javax.jms.IllegalStateException: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6
at org.hornetq.ra.HornetQRASession.checkStrict(HornetQRASession.java:1647)
at org.hornetq.ra.HornetQRAMessageConsumer.setMessageListener(HornetQRAMessageConsumer.java:124)
at org.hornetq.jms.client.HornetQJMSConsumer.setMessageListener(HornetQJMSConsumer.java:68)
I have no idea what could be causing this and my searches are not giving me any extra information.
I guess it could be something related to the fact that one componen should have no more than one active session. In this case, how could I create multiple listeners to listen to the queue?
(if important: I am using Wildfly 8)
EDIT
I've extracted the listener creation to a separate bean and still teh same error:
package learning.jms;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#ApplicationScoped
public class ListenerOne {
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private JMSContext context;
#Resource(mappedName="queues/myQueue")
private Queue myQueue;
private JMSConsumer consumer;
public void setUp() {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
System.out.println("working");
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
So, looking for MDBs solved the issue.
I cleaned the senderBean classfrom any traces of the consumers I was trying to create:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType;
public SenderBean() {
// TODO Auto-generated constructor stub
}
#PostConstruct
public void init(){
nextType=2;
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
}
(notice it is just session scope so I can iterate over the message types")
And created 3 MDBs
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=1")
})
public class ListenerOne implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
two:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=2")
})
public class ListenerTwo implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
three:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=3")
})
public class ListenerThree implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now they listen automatically for messages in the queue that match their selector.
Thanks #prabugp for the help :)
The above error could be because you are trying to use the client inside the same container and getting connection factory from JCA based connection factory.
Case 1: if client are remote to jms server then using jms/RemoteConnectionFactory is recommended and above problem will not be reproduced.
Case 2: if client resides in same container then connections from JCA based connection factory java/JmsXA is preferred. Since there is a limitation in JEE 7 specification under section 6:7 that JEE server does not permit EJB/Web application to have more than one active session i.e you cannot have legacy JMS application.
E.g: In a method:
public void startConnection() {
try {
TopicConnectionFactory connectionFactory = (TopicConnectionFactory) getConnectionFactory();
topicConnection = connectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
subscriber = topicSession.createSubscriber(messageTopic, selector, true);
MessageListener messageListener = new MessageListener(this);
// Code to set message listener.
subscriber.setMessageListener(messageListener);
topicConnection.start();
} catch (Exception e) {
LOG.error(e, e);
closeConnection();
throw new RuntimeException(e);
}
}
if connection factory on above code is from
#Resource(mappedName = "java:jboss/exported/jms/RemoteConnectionFactory")
then above code will work. But if we change the connection factory to
//#Resource(mappedName = "java:/JmsXA")
then above error will be thrown.
So if your client is in the same container then MDB should be used. Since container wants control of connection objects to support two phase commit protocol.
My view on this, you can not have a message listener in a JSF bean, since the bean lifecycle is controlled by the web container.
MDBs are the only components that are driven by messages, but JSF MBs, like EJBs or servlets, cannot listen for messages, they "live" in the context of a request, and instances are created, activated, passivated or destroyed by the container.
But instead, you can use receive(), in the context of a request, and set up some autorefresh on client side, to implement a flow that is server-side driven.
I am new to JMS, after a long re search i googled out a code to connect to JMS and post a msg.
The problem is i need to post the message in a remote queue, But i am not sure how to establish connection to it and post the message.
SERVER TYPE : TIBCO EMS
SERVER HOST : **.*****.net
PORT : *
**USername : user
passsword : user123
Queue : *.*....Order.Management..1
I would like to establish connection, post a simple msg and retrieve it back.
Kindly help! thanks in advance
CODE WHICH I GOT FROM INTERNET
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class emm {
// Defines the JNDI context factory.
public final static String JNDI_FACTORY="com.tibco.tibjms.naming.TibjmsInitialContextFactory";
// Defines the JMS context factory.
public final static String JMS_FACTORY="jms/TestConnectionFactory";
// Defines the queue.
public final static String QUEUE="CPW.GBR.POR.Public.Request.Order.Management.UpdateProvisioningStatus.1";
private QueueConnectionFactory qconFactory;
private ConnectionFactory conFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueSender qsender;
private Queue queue;
private TextMessage msg;
/**
* Creates all the necessary objects for sending
* messages to a JMS queue.
*
* #param ctx JNDI initial context
* #param queueName name of queue
* #exception NamingException if operation cannot be performed
* #exception JMSException if JMS fails to initialize due to internal error
*/
public void init(Context ctx, String queueName)
throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qsender = qsession.createSender(queue);
msg = qsession.createTextMessage();
qcon.start();
}
/**
* Sends a message to a JMS queue.
*
* #param message message to be sent
* #exception JMSException if JMS fails to send message due to internal error
*/
public void send(String message) throws JMSException {
msg.setText(message);
qsender.send(msg);
}
/**
* Closes JMS objects.
* #exception JMSException if JMS fails to close objects due to internal error
*/
public void close() throws JMSException {
qsender.close();
qsession.close();
qcon.close();
}
/** main() method.
*
* #param args WebLogic Server URL
* #exception Exception if operation fails
*/
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("Usage: java examples.jms.queue.QueueSend WebLogicURL");
return;
}
InitialContext ic = getInitialContext(args[0]);
emm qs = new emm();
qs.init(ic, QUEUE);
readAndSend(qs);
qs.close();
}
private static void readAndSend(emm qs)
throws IOException, JMSException
{
BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
String line=null;
boolean quitNow = false;
do {
System.out.print("Enter message (\"quit\" to quit): \n");
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
qs.send(line);
System.out.println("JMS Message Sent: "+line+"\n");
quitNow = line.equalsIgnoreCase("quit");
}
} while (! quitNow);
}
private static InitialContext getInitialContext(String url)
throws NamingException
{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
}
Finally from different sources i found the best possible to way for this.
This code will definitely work. I have tried this out and is running currently on my machine
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.*;
public class JMSExample {
static String serverUrl = "tcp://10.101.111.101:10001"; // values changed
static String userName = "user";
static String password = "pwd";
static QueueConnection connection;
static QueueReceiver queueReceiver;
static Queue queue;
static TextMessage message;
public static void sendTopicMessage(String topicName, String messageStr) {
Connection connection = null;
Session session = null;
MessageProducer msgProducer = null;
Destination destination = null;
try {
TextMessage msg;
System.out.println("Publishing to destination '" + topicName
+ "'\n");
ConnectionFactory factory = new com.tibco.tibjms.TibjmsConnectionFactory(
serverUrl);
connection = factory.createConnection(userName, password);
session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(topicName);
msgProducer = session.createProducer(null);
msg = session.createTextMessage();
msg.setStringProperty("SourceId", userName);
msg.setStringProperty("BusinessEvent", password);
msg.setText(messageStr);
msgProducer.send(destination, msg);
System.out.println("Published message: " + messageStr);
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws JMSException {
// TODO Auto-generated method stub
JMSExample.sendTopicMessage("***.***.***.**.**.Order.Management.***.1",
"Hi");
//System.out.println(getMessage());
}
As i mentioned some code for inspiration (SLIGHTLY improved your code. Not something i would want to see in production!)
// Use the domain-agnostic API
private Connection connection;ery
private Session session;
private MessageProducer producer;
private Queue queue;
public void init(Context ctx, String queueName) {
try {
ConnectionFactory cnf = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
queue = (Queue) ctx.lookup(queueName);
connection = cnf.createConnection("user", "user123");
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(queue);
connection.start();
} catch (NamingException e) {
throw new RuntimeException(e);
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
And make your send like this (don't reuse the same Message object, but create a new one for each message you are sending)
public void send(String message) throws JMSException {
TextMessage msg = session.createTextMessage();
msg.setText(message);
producer.send(msg);
}
Put a try..finally around the code in your main method:
try {
readAndSend(qs);
} finally {
qs.close();
}
The code you are using is not very good (understatement). It is programmed too brittle for a production system. You should not use state the way this program does.
Also there is no need to use the domain specific JMS API. You can use the domain (queue/topic) agnostic one.
When you run the program pass in the JNDI URL for your TIBCO server.
THE SCENE:
I am writing a echo client and server. The data being transfered is a string:
Client encode a string,and send it to server.
Server recv data, decode string, then encode the received string, send it back to client.
The above process will be repeated 100000 times.(Note: the connection is persistent).
DEFERENT CONTIONS:
When I run ONE server and TWO client at the same time, everything is ok,every client receives 100000 messages and terminated normally.
But When I add a ExecutionHandler on server, and then run ONE server and TWO client at the same time, one client will never terminate, and the network traffic is zero.
I cant locate the key point of this problem for now, will you give me some suggestions?
MY CODE:
string encoder , string decoder, client handler , server handler , client main ,server main.
//Decoder=======================================================
import java.nio.charset.Charset;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
public class Dcd extends FrameDecoder {
public static final Charset cs = Charset.forName("utf8");
#Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer) throws Exception {
if (buffer.readableBytes() < 4) {
return null;
}
int headlen = 4;
int length = buffer.getInt(0);
if (buffer.readableBytes() < length + headlen) {
return null;
}
String ret = buffer.toString(headlen, length, cs);
buffer.skipBytes(length + headlen);
return ret;
}
}
//Encoder =======================================================
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
public class Ecd extends OneToOneEncoder {
#Override
protected Object encode(ChannelHandlerContext ctx, Channel channel,
Object msg) throws Exception {
if (!(msg instanceof String)) {
return msg;
}
byte[] data = ((String) msg).getBytes();
ChannelBuffer buf = ChannelBuffers.dynamicBuffer(data.length + 4, ctx
.getChannel().getConfig().getBufferFactory());
buf.writeInt(data.length);
buf.writeBytes(data);
return buf;
}
}
//Client handler =======================================================
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
/**
* Handler implementation for the echo client. It initiates the ping-pong
* traffic between the echo client and server by sending the first message to
* the server.
*/
public class EchoClientHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger
.getLogger(EchoClientHandler.class.getName());
private final AtomicLong transferredBytes = new AtomicLong();
private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicLong startTime = new AtomicLong(0);
private String dd;
/**
* Creates a client-side handler.
*/
public EchoClientHandler(String data) {
dd = data;
}
public long getTransferredBytes() {
return transferredBytes.get();
}
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
// Send the first message. Server will not send anything here
// because the firstMessage's capacity is 0.
startTime.set(System.currentTimeMillis());
Channels.write(ctx.getChannel(), dd);
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
// Send back the received message to the remote peer.
transferredBytes.addAndGet(((String) e.getMessage()).length());
int i = counter.incrementAndGet();
int N = 100000;
if (i < N) {
e.getChannel().write(e.getMessage());
} else {
ctx.getChannel().close();
System.out.println(N * 1.0
/ (System.currentTimeMillis() - startTime.get()) * 1000);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
// Close the connection when an exception is raised.
logger.log(Level.WARNING, "Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
}
}
//Client main =======================================================
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* Sends one message when a connection is open and echoes back any received data
* to the server. Simply put, the echo client initiates the ping-pong traffic
* between the echo client and server by sending the first message to the
* server.
*/
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() {
// Configure the client.
final ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Set up the pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new Dcd(), new Ecd(),
new EchoClientHandler("abcdd"));
}
});
bootstrap.setOption("sendBufferSize", 1048576);
bootstrap.setOption("receiveBufferSize", 1048576);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("writeBufferLowWaterMark", 32 * 1024);
bootstrap.setOption("writeBufferHighWaterMark", 64 * 1024);
List<ChannelFuture> list = new ArrayList<ChannelFuture>();
for (int i = 0; i < 1; i++) {
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(
host, port));
// Wait until the connection is closed or the connection
// attempt
// fails.
list.add(future);
}
for (ChannelFuture f : list) {
f.getChannel().getCloseFuture().awaitUninterruptibly();
}
// Shut down thread pools to exit.
bootstrap.releaseExternalResources();
}
private static void testOne() {
final String host = "192.168.0.102";
final int port = 8000;
new EchoClient(host, port).run();
}
public static void main(String[] args) throws Exception {
testOne();
}
}
//server handler =======================================================
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
/**
* Handler implementation for the echo server.
*/
public class EchoServerHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger
.getLogger(EchoServerHandler.class.getName());
private final AtomicLong transferredBytes = new AtomicLong();
public long getTransferredBytes() {
return transferredBytes.get();
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
// Send back the received message to the remote peer.
transferredBytes.addAndGet(((String) e.getMessage()).length());
Channels.write(ctx.getChannel(), e.getMessage());
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
// Close the connection when an exception is raised.
logger.log(Level.WARNING, "Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
}
}
//Server main =======================================================
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
/**
* Echoes back any received data from a client.
*/
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void run() {
// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
System.out.println(Runtime.getRuntime().availableProcessors() * 2);
final ExecutionHandler executionHandler = new ExecutionHandler(
new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576));
// Set up the pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
System.out.println("new pipe");
return Channels.pipeline(new Dcd(), new Ecd(),
executionHandler, new EchoServerHandler());
}
});
bootstrap.setOption("child.sendBufferSize", 1048576);
bootstrap.setOption("child.receiveBufferSize", 1048576);
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.writeBufferLowWaterMark", 32 * 1024);
bootstrap.setOption("child.writeBufferHighWaterMark", 64 * 1024);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(port));
}
public static void main(String[] args) throws Exception {
int port = 8000;
new EchoServer(port).run();
}
}
I have found the reason now, it is a hard work, but full with pleasure.
When added a ExecutionHandler, the message will be wrapped into a Runnable task, and will be executed in a ChildExecutor. The key point is here : A task maybe added to ChildExecutor when the executor almostly exit , then is will be ignored by the ChildExecutor.
I added three lines code and some comments, the final code looks like below, and it works now,should I mail to the author? :
private final class ChildExecutor implements Executor, Runnable {
private final Queue<Runnable> tasks = QueueFactory
.createQueue(Runnable.class);
private final AtomicBoolean isRunning = new AtomicBoolean();
public void execute(Runnable command) {
// TODO: What todo if the add return false ?
tasks.add(command);
if (!isRunning.get()) {
doUnorderedExecute(this);
} else {
}
}
public void run() {
// check if its already running by using CAS. If so just return
// here. So in the worst case the thread
// is executed and do nothing
boolean acquired = false;
if (isRunning.compareAndSet(false, true)) {
acquired = true;
try {
Thread thread = Thread.currentThread();
for (;;) {
final Runnable task = tasks.poll();
// if the task is null we should exit the loop
if (task == null) {
break;
}
boolean ran = false;
beforeExecute(thread, task);
try {
task.run();
ran = true;
onAfterExecute(task, null);
} catch (RuntimeException e) {
if (!ran) {
onAfterExecute(task, e);
}
throw e;
}
}
//TODO NOTE (I added): between here and "isRunning.set(false)",some new tasks maybe added.
} finally {
// set it back to not running
isRunning.set(false);
}
}
//TODO NOTE (I added): Do the remaining works.
if (acquired && !isRunning.get() && tasks.peek() != null) {
doUnorderedExecute(this);
}
}
}
This was a bug and will be fixed in 3.4.0.Alpha2.
See https://github.com/netty/netty/issues/234