The question is simple: How do I properly close and release JMS resources in a Java SE application. I use IBM MQ v8.
My problem is that IBM MQ for JMS seems to start a thread named "JMSCCThreadPoolMaster" which doesn't die when there's no more JMS in the application. It lives on until the application itself dies. This is not what I want. I want to be able to close down that thread pool programmatically. My application only does 2 secs of JMS on startup and then goes on to to do other things for weeks or even months in a fairly resource constrained environment. It seems unreasonable that that I'm not able to tell JMS that I'm done with using it after the first 2 secs, so that resources are properly released.
Here's a little test to prove the point:
package org.test.jms.producer.simpleconsumer;
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsConstants;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
public class MyTest {
private static final Logger LOGGER = Logger.getLogger(MyTest.class.getName());
public static void main(String[] args) throws JMSException, InterruptedException {
// Do some JMS stuff in a separate thread.
Thread jmsThread = new Thread(new JmsRunnable());
jmsThread.start();
// Wait some time to allow any background thread to properly
// close down.
Thread.sleep(60*1000);
// Print the threads that exist at this point in time.
Thread[] threads = new Thread[100];
int noOfThreads = Thread.enumerate(threads);
for (Thread t : threads) {
if (t != null) {
System.out.println(" Active thread : " + t.getName());
}
}
}
private static class JmsRunnable implements Runnable {
#Override
public void run() {
try {
// just do a connect and then immediately disconnect.
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(JmsConstants.WMQ_PROVIDER);
JmsConnectionFactory factory = ff.createConnectionFactory();
Map<String, Object> props = new HashMap<>();
props.put(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
props.put(WMQConstants.WMQ_HOST_NAME, "localhost");
props.put(WMQConstants.WMQ_QUEUE_MANAGER, "dev");
props.put(WMQConstants.WMQ_CHANNEL, "mychannel");
factory.setBatchProperties(props);
Connection connection = factory.createConnection(); // ++
connection.close();
LOGGER.log(Level.INFO, "Thread is closing");
} catch (JMSException ex) {
LOGGER.log(Level.SEVERE, "Something bad happened", ex);
}
}
}
}
At the point where I list active threads there should no longer be any active JMS resources held onto by application. Yet there's this background thread.
The line marked with "++" is what triggers the creation of the background thread named "JMSCCThreadPoolMaster".
How to properly close down? A solution specific to IBM MQ is acceptable, i.e. it will be ok for me to cast to IBM MQ specific JMS class.
Related
I'm trying to migrate from Vert.x to Quarkus and in Vert.x when I write message consumers like Kafka/AMQP etc. I have to scale the number of verticals to maximize performance across multiple cores i.e. Vertical Scaling - is this possible in Quarkus? I see a similar question here but it wasn't answered.
For example, with Kafka I might create a consumer inside a vertical and then scale that vertical say 10 times (that is specify the number of instances in the deployment to be 10) after doing performance testing to determine that's the optimal number. My understanding is that by default, 1 vertical = 1 event loop and does not scale across multiple cores.
I know that it's possible to use Vert.x verticals in Quarkus but is there another way to scale things like the number of Kafka consumers across multiple core?
I see that this type of scalability is configurable for things like Quarkus HTTP but I can't find anything about message consumers.
Here's the Vert.x Verticle approach that overall I'm very happy with, but I wish there were better documentation on how to do this.
UPDATE - Field injection doesn't work with this example but constructor injection does work.
Lets say I want to inject this
#ApplicationScoped
public class CoffeeRepositoryService {
public CoffeeRepositoryService() {
System.out.println("Injection succeeded!");
}
}
Here's my Verticle
package org.acme;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.core.AbstractVerticle;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.mutiny.core.eventbus.EventBus;
import io.vertx.mutiny.rabbitmq.RabbitMQClient;
import io.vertx.mutiny.rabbitmq.RabbitMQConsumer;
import io.vertx.rabbitmq.QueueOptions;
import io.vertx.rabbitmq.RabbitMQOptions;
public class RQVerticle extends AbstractVerticle {
private final Logger LOGGER = LoggerFactory.getLogger(org.acme.RQVerticle.class);
//This doesn't work - returns null
#Inject
CoffeeRepositoryService coffeeRepositoryService;
RQVerticle() {} // dummy constructor needed
#Inject // constructor injection - this does work
RQVerticle(CoffeeRepositoryService coffeeRepositoryService) {
//Here coffeeRepositoryService is injected properly
}
#Override
public Uni<Void> asyncStart() {
LOGGER.info(
"Creating RabbitMQ Connection after Quarkus successful initialization");
RabbitMQOptions config = new RabbitMQOptions();
config.setUri("amqp://localhost:5672");
RabbitMQClient client = RabbitMQClient.create(vertx, config);
Uni<Void> clientResp = client.start();
clientResp.subscribe()
.with(asyncResult -> {
LOGGER.info("RabbitMQ successfully connected!");
});
return clientResp;
}
}
Main Class - injection doesn't work like this
package org.acme;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.vertx.core.DeploymentOptions;
import io.vertx.mutiny.core.Vertx;
#QuarkusMain
public class Main {
public static void main(String... args) {
Quarkus.run(MyApp.class, args);
}
public static class MyApp implements QuarkusApplication {
#Override
public int run(String... args) throws Exception {
var vertx = Vertx.vertx();
System.out.println("Deployment Starting");
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticleAndAwait(RQVerticle::new, options);
System.out.println("Deployment completed");
Quarkus.waitForExit();
return 0;
}
}
}
Main Class with working injection but cannot deploy more than one instance
package org.acme;
import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.core.Vertx;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import org.jboss.logging.Logger;
#ApplicationScoped
public class MainVerticles {
private static final Logger LOGGER = Logger.getLogger(MainVerticles.class);
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
public void init(#Observes StartupEvent e, Vertx vertx, RQVerticle verticle) {
DeploymentOptions options = new DeploymentOptions()
.setInstances(2);
vertx.deployVerticle(verticle,options).await().indefinitely();
}
}
Std Out - first main class looks good
2021-09-15 15:48:12,052 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-2) Creating RabbitMQ Connection after Quarkus successful initialization
2021-09-15 15:48:12,053 INFO [org.acm.RQVerticle] (vert.x-eventloop-thread-3) Creating RabbitMQ Connection after Quarkus successful initialization
Std Out - second main class
2021-09-22 15:48:11,986 ERROR [io.qua.run.Application] (Quarkus Main
Thread) Failed to start application (with profile dev):
java.lang.IllegalArgumentException: Can't specify > 1 instances for
already created verticle
Requirement: I need to get all the messages from all the sessions from the queue. registerSessionHandler should consume messages as soon as they appear in the queue.
Issue: the code accepts the messages that are pertaining to one of the session id (xyz) and then it just waits. And even when more messages are pushed with that session id (xyz), it fails to consume it.
Any suggestions - what obvious thing I am missing here.
I have registerSessionHandler for receiving session messages continuously from the queue. Whenever i start new session, i am getting message of only one sessionID.
Test Queue: In this queue, there are 20 messages available with 4 different sessionIDs. Whenever i run Java application (QueueSessionReceiveTest.java), i get only 5 messages which is associated with single session ID.
MAVEN - azure-servicebus - 1.1.1
RECEIVE CODE:
import java.time.Duration;
import com.microsoft.azure.servicebus.IMessage;
import com.microsoft.azure.servicebus.IQueueClient;
import com.microsoft.azure.servicebus.QueueClient;
import com.microsoft.azure.servicebus.ReceiveMode;
import com.microsoft.azure.servicebus.SessionHandlerOptions;
import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder;
public class QueueSessionReceiveTest {
private static final String connectionString = "Endpoint=sb://XXXXXX";
private static final String queueName = "test";
private static IQueueClient queueClient;
public static void main(String[] args) throws Exception {
queueClient = new QueueClient(new ConnectionStringBuilder(connectionString, queueName), ReceiveMode.PEEKLOCK);
queueClient.registerSessionHandler(new QueueMessageSessionHandler(), new SessionHandlerOptions(1, false, Duration.ofMinutes(1)));
}}
SESSION HANDLER CODE:
import java.util.concurrent.CompletableFuture;
import com.microsoft.azure.servicebus.ExceptionPhase;
import com.microsoft.azure.servicebus.IMessage;
import com.microsoft.azure.servicebus.IMessageSession;
import com.microsoft.azure.servicebus.IQueueClient;
import com.microsoft.azure.servicebus.ISessionHandler;
public class QueueMessageSessionHandler implements ISessionHandler {
#Override
public CompletableFuture<Void> onMessageAsync(IMessageSession session, IMessage iMessage) {
return session.completeAsync(iMessage.getLockToken()).thenRunAsync(() -> Logger.debug("some log") );
}
#Override
public CompletableFuture<Void> OnCloseSessionAsync(IMessageSession session) {
return null;
}
#Override
public void notifyException(Throwable exception, ExceptionPhase exceptionPhase) {
// Do nothing
}}
Please refer below link for solution.
https://github.com/Azure/azure-service-bus-java/issues/187
Im trying to develop a "Message Driven Bean" to handle all the local ActiveMQ messages, but it's the first time that i try to do something like this.
The most part of the material that i found explain how to write a MDB using JBOSS server, in this case there's a xml file with some queue information, but in all wildfly tutorials there's no mention to any kind of configuration like that.
I have the following scenario:
A simple java project like message producer
An ActiveMQ instance running local
An EJB project deployed into Wildfly 10
My producer project is able to send messages to ActiveMQ queue, this part its working,but my EJB project just have a single class called TestMDBHandle with #MessageDriven annotation. Is this enough to receive my queue messages? Because the MDB isnt working, i imagine must be a kind of configuration or property in EJB to specify the host of the message-broker.
My message producer:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MessageSender {
public static void main(String args[]) throws NamingException, JMSException {
MessageSender sender = new MessageSender();
sender.sender();
}
public void sender() throws NamingException, JMSException {
InitialContext jndi = null;
Session session = null;
Connection connection = null;
try {
jndi = new InitialContext();
ConnectionFactory factory = (ConnectionFactory)jndi.lookup("connectionFactory");
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination)jndi.lookup("MyQueue");
MessageProducer producer = session.createProducer(destination);
TextMessage mensagem = session.createTextMessage("Eu enviei uma mensagem!");
producer.send(mensagem);
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
connection.close();
jndi.close();
}
}
}
My jms properties located inside my producer project
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://localhost:61616
connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactory
queue.MyQueue=jms/myqueue
Finally, my ejb project have this single class, without any kind of property file or xml.
package br.com.jms.mdb;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
#MessageDriven(name = "meuHandler", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/myqueue") })
public class Teste implements MessageListener {
#Resource
private MessageDrivenContext mdctx;
public Teste() {
}
#Override
public void onMessage(Message message) {
TextMessage objectMessage = null;
try {
objectMessage = (TextMessage)message;
System.out.println("Achei a mensagem : " + objectMessage.getText().toString());
}catch(JMSException e) {
e.printStackTrace();
}
}
}
Maybe you can provide a little more information such as the xml file with the queue information and the annotation properties of the MDB? Because it sounds you are heading in the right direction. The two main things:
You have to specify the exact queue that the MDB is listening to, for example through the properties of the #MessageDriven annotation (such as "name", "mappedName", "activationConfig"). And of course override the onMessage() method to process the messages.
You also have to make sure that this specific queue is available as a resource for your application. You have to provide jms configuration for this, which also defines the resource type (Queue or Topic). From your question I can't tell which of these steps you have (partly) completed.
I need to make a client queue with ThreadPoolExecutor and an ability to drop clients if it exceeds some number (5 for example). It is kinda DDOS protection. When client #6 is requesting my server - it got dropped, etc. I got my server and client code, but I don't know how to realize ThreadPoolExecutor and DiscardPolicy. Ideas or examples?
Simple server:
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
ServerSocket server = new ServerSocket (3000);
ExecutorService es = Executors.newFixedThreadPool(2);
Semaphore semaphore = new Semaphore (2);
while(true){
semaphore.acquire();
Socket accept2 = server.accept();
es.execute(()->{
try (Socket accept = accept2) {
serve(accept);
} catch (Exception exception) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, exception);
}
finally {
semaphore.release();
}
});
}
}
private static void serve(final Socket accept) throws ClassNotFoundException, IOException {
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
ObjectInputStream inputStream2 = new ObjectInputStream (inputStream);
while (true){
Object readObject = inputStream2.readObject();
System.out.println(readObject);
}
}
}
And a simple client:
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = new Socket ("localhost", 3000);
ObjectOutputStream oos = new ObjectOutputStream (
socket.getOutputStream());
oos.writeObject("First!");
Thread.sleep(10000);
oos.writeObject("First again!");
Thread.sleep(10000);
oos.writeObject("First again again!");
}
}
Use ThreadPoolExecutor with DiscardPolicy as below:
int poolSize=1;
int maxPoolSize=2;
int queueSize=5;
long aliveTive=1000;
ArrayBlockingQueue<Runnable> queue= new ArrayBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor executor= new ThreadPoolExecutor(poolSize,maxPoolSize,aliveTive,
TimeUnit.MILLISECONDS,queue,new ThreadPoolExecutor.DiscardPolicy());
}
Rejected tasks:
New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated.
In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) method of its RejectedExecutionHandler.
Four predefined handler policies are provided:
In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.
In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.
In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.
In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)
Have a look at this documentation page for more details
I have a JNLP downloader application deployed on remote user machines that downloads files.
I need to get some error feedback mailed to me. Not so much exceptions, just things getting stuck, or stalled or in infinite loops.
Currently I have a basic handler:
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class javaerrorlog {
private static Logger l = Logger.getLogger("");
public static void main(String args[]) throws Exception{
FileHandler handler = new FileHandler("log.txt");
l.addHandler(handler);
l.setLevel(Level.ALL);
l.info("Error logs");
try {
} catch (Error ex) {
l.log(Level.INFO, "", ex);
}
l.fine("");
}
}
Also, should I prompt for the client's permission to send error reporting data?
If you just need notifications you could use something like SMTPHandler. If you need it more fancy you could use JMS with an MDB.