I'm studing how RabbitMQ works with Java, it is almost clear how Producer works and how to implement the Consumer, but I'm still not sure how to deploy the Consumer application to server with a correct threading handling.
Let's say I have a web-application that has to sent some transaction email as producer, this application will push in the queue the messages and it is managed by a container, like Tomcat, that increase/descrease threads to serve multiple requests.
What is the best practice to deployt the consumer application?
I have read many tutorials and the RabbitMQ commands are well explained, usually this is the code (from official RabbitMq Hello World):
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.nio.charset.StandardCharsets;
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
}
}
However it is not clear to me how I should run this application in my server (sure I can start it by command line!) and how am I sure the process will not crash for some reason?
Should I put the channel opening commands in a loop inside a try/catch to be sure it will not exit?
Am I'm in charge of handling thread pool opening/increasing?
Does exists something for this purpose?
Related
I'm writing a main class that will create a few clients and test them subscribing and publishing. I'd like to display information of the clients connection, like the data & time connected, clientId, clientIP used to connect, whether they connected gracefully or not. I'm new to using tools like Logger so I'm not sure how I would do this. I left a link to the HiveMQ community edition (broker) and the client. I'd like to display this information in my main class in the HiveMQ client project but there a log file in the community edition called event.log which contains exactly the kind of information I want to display. I left an image below.
HiveMQ:
https://github.com/hivemq/hivemq-community-edition
https://github.com/hivemq/hivemq-mqtt-client
There is an event.log file in hivemq-community-edition that has the kind of information I'd like to display. It was generated when I build the project as a Gradle project so it won't be found unless you imported into Eclipse and built in with Gradle so I left a screenshot of what it looks like.
Code in my Main class in HiveMQ Client:
package com.main;
import java.util.UUID;
import com.hivemq.client.mqtt.MqttGlobalPublishFilter;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient.Mqtt5Publishes;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish;
import java.util.logging.Logger;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.concurrent.TimeUnit;
public class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); // Creates a logger instance
public static void main(String[] args) {
Mqtt5BlockingClient client1 = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString()) // the unique identifier of the MQTT client. The ID is randomly generated between
.serverHost("localhost") // the host name or IP address of the MQTT server. Kept it 0.0.0.0 for testing. localhost is default if not specified.
.serverPort(1883) // specifies the port of the server
.buildBlocking(); // creates the client builder
client1.connect(); // connects the client
System.out.println("Client1 Connected");
System.out.println(client1.toString());
String testmessage = "How is it going!";
byte[] messagebytesend = testmessage.getBytes(); // stores a message as a byte array to be used in the payload
try {
Mqtt5Publishes publishes = client1.publishes(MqttGlobalPublishFilter.ALL); // creates a "publishes" instance thats used to queue incoming messages
client1.subscribeWith() // creates a subscription
.topicFilter("test1/#") // filters to receive messages only on this topic (# = Multilevel wild card, + = single level wild card)
.qos(MqttQos.AT_LEAST_ONCE) // Sets the QoS to 2 (At least once)
.send();
System.out.println("The client1 has subscribed");
client1.publishWith() // publishes the message to the subscribed topic
.topic("test/pancakes/topic") // publishes to the specified topic
.qos(MqttQos.AT_LEAST_ONCE)
.payload(messagebytesend) // the contents of the message
.send();
System.out.println("The client1 has published");
Mqtt5Publish receivedMessage = publishes.receive(5,TimeUnit.SECONDS).get(); // receives the message using the "publishes" instance waiting up to 5 seconds // .get() returns the object if available or throws a NoSuchElementException
byte[] tempdata = receivedMessage.getPayloadAsBytes(); // converts the "Optional" type message to a byte array
System.out.println();
String getdata = new String(tempdata); // converts the byte array to a String
System.out.println(getdata);
}
catch (InterruptedException e) { // Catches interruptions in the thread
LOGGER.log(Level.SEVERE, "The thread was interrupted while waiting for a message to be received", e);
}
catch (NoSuchElementException e){
System.out.println("There are no received messages"); // Handles when a publish instance has no messages
}
client1.disconnect();
System.out.println("Client1 Disconnected");
}
}
You can obtain information about the client with the method getConfig e.g.
Mqtt5ClientConfig config = client.getConfig();
config.getClientIdentifier();
To get the information of the current connection use getConnectionConfig e.g.
Optional<Mqtt5ClientConnectionConfig> connectionConfig = config.getConnectionConfig();
if (connectionConfig.isPresent()) {
MqttClientTransportConfig transportConfig = connectionConfig.get().getTransportConfig();
}
You can also use listeners which are notified when the client is connected or disconnected e.g.
Mqtt5Client.builder()
.addConnectedListener(context -> System.out.println("connected"))
.addDisconnectedListener(context -> System.out.println("disconnected"))
...
I am currently researching how to connect to Azure Service Bus using Qpid JMS (qpid-jms-client-0.11.1.jar).
I have created a Demo Java application SimpleSenderReceiver which connects to an already configured Azure Service Bus using the following guide (#link1).
This code seems to work using a "very" old version om the Qpid JMS client (version 0.32). I am now trying to get it to work with the latest stable version of Qpid JMS (qpid-jms-client-0.11.1.jar), And so far I have not been successful.
Going through the documentation #link2 of Qpid JMS 0.11.1, you can see that the way that the in the properties file the property connectionfactory is different to that in version 0.32.
How can i setup a correct connection amqp connection string in the
properties file?
How can I setup de Qpid JMS - Azure Service Bus Demo to work with the latest Qpid stable version?
I keep running in the following problem:
731 [AmqpProvider:(1):[amqps://example-bus.servicebus.windows.net?transport.connectTimeout=60000]] INFO org.apache.qpid.jms.sasl.SaslMechanismFinder - Best match for SASL auth was: SASL-PLAIN
javax.jms.JMSException: Idle timeout value specified in connection OPEN ('30000 ms') is not supported. Minimum idle timeout is '60000' ms. TrackingId:238849ced1em4cd3a093261372f4fc1e_G21, SystemTracker:gateway6, Timestamp:10/27/2016 8:16:23 AM [condition = amqp:internal-error]
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:150)
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:105)
at org.apache.qpid.jms.provider.amqp.AmqpAbstractResource.remotelyClosed(AmqpAbstractResource.java:147)
at org.apache.qpid.jms.provider.amqp.AmqpAbstractResource.processRemoteClose(AmqpAbstractResource.java:251)
at org.apache.qpid.jms.provider.amqp.AmqpProvider.processUpdates(AmqpProvider.java:771)
at org.apache.qpid.jms.provider.amqp.AmqpProvider.access$1900(AmqpProvider.java:90)
at org.apache.qpid.jms.provider.amqp.AmqpProvider$17.run(AmqpProvider.java:699)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
I have the follwing properties file servicebus.properties:
# servicebus.properties - sample JNDI configuration
# Register a ConnectionFactory in JNDI using the form:
# connectionfactory.[jndi_name] = [ConnectionURL]
connectionfactory.myFactoryLookup = amqps://example-open-bus.servicebus.windows.net?jms.username=somePolicy&jms.password=aM2k3PaZY5jdIkmGKm7G%2FcH%2BUFQaFAgHIYc3dSsuiLI%3D&transport.connectTimeout=6000
# Register some queues in JNDI using the form
# queue.[jndi_name] = [physical_name]
# topic.[jndi_name] = [physical_name]
queue.myQueueLookup = queue1
I have the flowing class SimpleSenderReceiver.java:
package com.demo.AzureTest;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Random;
public class SimpleSenderReceiver implements MessageListener {
private static boolean runReceiver = false;
private Connection connection;
private Session sendSession;
private Session receiveSession;
private MessageProducer sender;
private MessageConsumer receiver;
private static Random randomGenerator = new Random();
public SimpleSenderReceiver() throws Exception {
// Configure JNDI environment
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.qpid.jms.jndi.JmsInitialContextFactory");
env.put(Context.PROVIDER_URL, "C://PATH//servicebus.properties");
Context context = new InitialContext(env);
// Look up ConnectionFactory and Queue
ConnectionFactory cf = (ConnectionFactory) context.lookup("myFactoryLookup");
System.out.println("lookup: " + context.lookup("myFactoryLookup"));
System.out.println("cf:"+cf);
Destination queue = (Destination) context.lookup("myQueueLookup");
System.out.println("queue:");
// Create Connection
connection = cf.createConnection();
System.out.println("connection :"+connection);
// // Create sender-side Session and MessageProducer
sendSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("Session open.");
sender = sendSession.createProducer(queue);
System.out.println(sender.getDestination());
System.out.println("sender:"+sender);
if (runReceiver) {
// Create receiver-side Session, MessageConsumer,and MessageListener
receiveSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
receiver = receiveSession.createConsumer(queue);
receiver.setMessageListener(this);
connection.start();
}
}
public static void main(String[] args) {
try {
if ((args.length > 0) && args[0].equalsIgnoreCase("sendonly")) {
runReceiver = false;
}
SimpleSenderReceiver simpleSenderReceiver = new SimpleSenderReceiver();
System.out.println("Press [enter] to send a message. Type 'exit' + [enter] to quit.");
BufferedReader commandLine = new java.io.BufferedReader(new InputStreamReader(System.in));
while (true) {
String s = commandLine.readLine();
if (s.equalsIgnoreCase("exit")) {
simpleSenderReceiver.close();
System.exit(0);
} else {
simpleSenderReceiver.sendMessage();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMessage() throws JMSException {
TextMessage message = sendSession.createTextMessage();
message.setText("Hello from SIS Test AMQP message from Java JMSaaa");
long randomMessageID = randomGenerator.nextLong() >>>1;
message.setStringProperty("TenantId", "klant");
message.setStringProperty("EventType", "bericht");
message.setStringProperty("EventTypeVersion", "1.0");
message.setStringProperty("MessageType", "DocumentMessage");
message.setStringProperty("OperationType", "Create");
message.setStringProperty("SourceSystem", "sis_sender");
message.setStringProperty("EnterpriseKey", "sis_sender-klant-bericht");
message.setJMSMessageID("ID:" + randomMessageID);
sender.send(message);
System.out.println("Sent message with JMSMessageID = " + message.getJMSMessageID());
System.out.println("Sent message with Text = " + message.getText());
}
public void close() throws JMSException {
connection.close();
}
public void onMessage(Message message) {
try {
System.out.println("Received message with JMSMessageID = " + message.getJMSMessageID());
TextMessage txtmessage = (TextMessage) message;
System.out.println("Received message with Text = " + txtmessage.getText());
message.acknowledge();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Maven dependencies:
<dependencies>
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-jms-client</artifactId>
<version>0.11.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
--- Update ---
I have since gotten a little further but still a bit stuck.
Update to the connectionfactory property:
connectionfactory.myFactoryLookup = connectionfactory.myFactoryLookup = amqps://example-open-bus.servicebus.windows.net?amqp.idleTimeout=150000&jms.username=somePolicy&jms.password=aM2k3PaZY5jdIkmGKm7G%2FcH%2BUFQaFAgHIYc3dSkuiLI%3D
I now am getting the following stacktrace:
842 [AmqpProvider:(1):[amqps://example-open-bus-bus.servicebus.windows.net]] INFO org.apache.qpid.jms.sasl.SaslMechanismFinder - Best match for SASL auth was: SASL-PLAIN
1014 [AmqpProvider:(1):[amqps://example-open-bus-bus.servicebus.windows.net]] INFO org.apache.qpid.jms.JmsConnection - Connection ID:543efe98-3ecc-485e-9f7f-3046c40db0cb:1 connected to remote Broker: amqps://example-open-bus-bus.servicebus.windows.net
1301 [AmqpProvider:(1):[amqps://example-open-bus-bus.servicebus.windows.net]] WARN org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder - Open of resource:(JmsProducerInfo { ID:546efe78-3ecc-485d-9f6f-3065c40db1ce:1:1:1, destination = klant }) failed: Attempted to perform an unauthorized operation. TrackingId:2950b1ed7a0d4e0a97b0k32b25434ac2_G10, SystemTracker:gateway6, Timestamp:10/27/2016 1:36:21 PM [condition = amqp:unauthorized-access]
Caught exception, exiting.
javax.jms.JMSSecurityException: Attempted to perform an unauthorized operation. TrackingId:2890b0ed9a0d4e0a97b1k32b25434ac2_G10, SystemTracker:gateway6, Timestamp:10/27/2016 1:36:21 PM [condition = amqp:unauthorized-access]
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:129)
at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToException(AmqpSupport.java:105)
at org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder.handleClosed(AmqpResourceBuilder.java:167)
at org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder.processRemoteClose(AmqpResourceBuilder.java:113)
at org.apache.qpid.jms.provider.amqp.AmqpProvider.processUpdates(AmqpProvider.java:795)
at org.apache.qpid.jms.provider.amqp.AmqpProvider.access$1900(AmqpProvider.java:90)
at org.apache.qpid.jms.provider.amqp.AmqpProvider$17.run(AmqpProvider.java:699)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
The newer client enables AMQP heartbeating/idle-timeout by default, while the older client did not. The client sets a default 60second timeout, and in turn this means it requests a 30sec (30000ms) idle-timeout value in its AMQP Open frame when connecting to the server, in accordance with the specifications defined behaviour (where peers advertise half their actual timeout to help avoid spurious timeouts).
ServiceBus is refusing the 30000ms Open frame value, and indicating it needs a value of at least 60000ms (or presumably also 0, which means it is disabled). To acheive this you will need to configure the client to have its timeout set to at least 120000ms, which will result in the required minimum 60000ms Open frame idle-timeout value ServiceBus is mandating (or again, perhaps disable the clients timeout handling by setting it to 0).
You can do this using the "amqp.idleTimeout" URI option, as per http://qpid.apache.org/releases/qpid-jms-0.11.1/docs/index.html#amqp-configuration-options
EDIT: I see you figured that out at the same time I was typing my answer.
The new exception is from ServiceBus saying you arent authorized to do something you are trying. It should be easy enough to catch the exception at its source and determine what.
Your URI seems fine (though I assume your username isnt actually 'somePolicy' and the double connectionfactory.myFactoryLookup = connectionfactory.myFactoryLookup = at the start is a c&p error). I haven't used the client with ServiceBus personally, but I've seen questions from various folks that have, so I'm not aware of a particular issue outright stopping them working together.
I ran into the same security issue referred to above and spent a while tracking it down so for anyone else my issue was caused by the key value specified in the user.password query param containing the + character.
There is usually an = on the end of the value which I encoded as %3D in the string and I encoded the + as %2B however if you put a breakpoint at the point the ConnectionFactory is instantiated and look at the password attribute you will see that the = is correctly unencoded but the + has been stripped and is a SPACE hence the unauthorized access issues.
My workaround was just to regenerate the primary key in Azure so it didn't have a + in it (yuk) but it worked. Possibly a bug in the AQPID libs.
I'm trying connect from a amqp client to a aqtivemq server with default settings. It always gives the error message saying connection refused. Then I tried it with a rabbitmq server instead of a activemq server and it works fine. I wonder whether activemq needs a linux library to communicate.
Activemq server versions used which does not connects: 5.4.2 / 5.10.0
Rabitmq version used: 3.3.5
rabitmq sample client code
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
public class Cache {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException {
//creating the connection factory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
//Creating a connection to the server
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//declaring a queuw
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
//publishing the queue the queue
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//closing the connection
channel.close();
connection.close();
}
}
Fails in the following line of code
//Creating a connection to the server
Connection connection = factory.newConnection();
How can I solve this issue ?
I found a similar issue and I fixed checking the exchange declared was equals to the channel used to publish, in this way:
#Test
public void test() throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, IOException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("10.211.55.20");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("KipcastDirect", "direct",
true, /* durable */
true, /* autodelete */
null); /* */
byte[] messageBodyBytes = "Hello, world!".getBytes();
AMQP.BasicProperties.Builder basic = new AMQP.BasicProperties.Builder();
AMQP.BasicProperties minBasic = basic.build();
minBasic = basic.priority(0).deliveryMode(1).build();
channel.basicPublish("KipcastDirect", "KipcastRouting", minBasic, messageBodyBytes);
System.out.println(" [x] Sent ");
channel.close();
}
Please be carefoul: the URI (from and to) on Camel Spring DSL context and JUnit class must refer to same Exchange and Queue to prevent a reply-text=PRECONDITION_FAILED – parameters for queue ‘QUEUE’ in vhost ‘/’ not equivalen error or similar. To check the queues / exchanges configuration parameter use:
rabbitmqadmin -V / list queue
rabbitmqadmin -V test list exchanges
Take a look to this: http://www.andreagirardi.it/blog/camel-and-rabbitmq-finally-how-to/
I have a java application that publishes messages to a RabbitMQ server.
When the available disk space drops below rabbit's low watermark I get unexpected behavior.
The expected behavior is that the connection will become blocking, making my application hang on the call to Channel.basicPublish.
The actual behavior is that the connection appears to be blocking in the management console, but calls to Channel.basicPublish return with no errors and the messages that were supposed to be published are lost.
This behavior undermines the most important feature of RabbitMQ, which is robustness.
Below is a minimal version of my application for testing. All it does is publish a message every second with an incrementing index (1, 2, 3, ...). The messages are received just fine by the RabbitMQ server, until I set the low watermark to a very high value, by putting the following line in the rabbitmq.config file:
[
{rabbit, [{disk_free_limit, 60000000000}]}
].
After restarting the server, I get a low disk space notification in the management console, the connection is marked as 'blocking', and no more messages are received by the server. However, the application keeps running and sending messages as if nothing is wrong. When I reduce the watermark back to a normal value messages are received by the server again, but all the messages that were sent while the connection was blocking are lost.
Am I doing something wrong?
Is this a bug in RabbitMQ?
If so, is there a workaround?
OS: Windows 8 64bit
RabbitMQ server version: 3.1.1
RabbitMQ Java client version: 3.1.0
Test application code:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class Main {
private final static Logger logger = LoggerFactory.getLogger(Main.class);
private final static String QUEUE_NAME = "testQueue";
private static Channel channel = null;
private static void connectToRabbitMQ() throws IOException {
ConnectionFactory factory = new ConnectionFactory();
Connection connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(
QUEUE_NAME,
true, // Durable - survive a server restart
false, // Not exclusive to this connection
false, // Do not autodelete when no longer in use
null // Arguments
);
}
private static void disposeChannel()
{
if (channel == null) {
return;
}
try {
channel.close();
} catch (Exception e) {
} finally {
channel = null;
}
}
public static void main(String[] args) throws Exception {
boolean interrupted = false;
int messageNumber = 1;
while (!interrupted) {
byte[] message = Integer.toString(messageNumber).getBytes();
try {
if (channel == null) {
connectToRabbitMQ();
}
channel.basicPublish(
"",
QUEUE_NAME,
MessageProperties.MINIMAL_PERSISTENT_BASIC,
message
);
logger.info("Published message number {}", messageNumber);
messageNumber++;
} catch (Exception e) {
logger.info("Unable to connect to RabbitMQ...");
disposeChannel();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.info("Interrupted");
interrupted = true;
}
}
}
}
I am trying to create a Java Application Client project that sends a JMS message to a queue on a Glassfish server.
The problem is that after the app sends the message, it hangs when it's supposed to exit. The message is transmitted successfully, but for some reason the app doesn't exit. I have tried to debug the application, and I can step trough it all the way to the end of static void main, and that's where it hangs.
Here is the code:
import javax.jms.*;
import javax.naming.InitialContext;
public class Main {
public void SendMessage() throws Exception {
InitialContext ctx = new InitialContext();
ConnectionFactory cf = (ConnectionFactory) ctx.lookup("jms/TestFactory");
Queue queue = (Queue)ctx.lookup("jms/TestQueue");
Connection conn = cf.createConnection();
Session s = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod = s.createProducer(queue);
TextMessage txt = s.createTextMessage("testing");
prod.send(txt);
prod.close();
s.close();
conn.close();
}
public static void main(String[] args) throws Exception {
Main m = new Main();
m.SendMessage();
}
public Main() {
super();
}
}
How can I make it stop hanging?
It's been a bug in Glassfish for a long time.
There is a bug recorded here (reported back in version 9 of Sun App Server, predating Glassfish), but I suspect there will be lots of duplicate reports:
http://java.net/jira/browse/GLASSFISH-1429
My only known fix is to System.exit(0) (in a finally block), which closes all threads.
Horrible, yes.
Good call on the thread dump. Try issuing a Conn.stop(). It seems the JMS client still has non daemon threads running