JMS Consumer terminates and doesn't receive Message - eclipse

So I'm following this youtube tutorial on Java Message Service with JBoss. My codes are the same to the video however when I run my TopicConsumer and TopicProducer applications, both terminates and don't stay alive for me to receive my message. I read that setMessageListener would have created a new thread so the message should be received even if the main thread was terminated but I'm still not receiving the message.
I found out that it's not calling onMessage, is it because TopicConsumer was terminated before it gets a chance to?
I've my JBoss 5.0 server running, just like in the video I run TopicConsumer first (but it terminates after the print statement unlike in the video) then TopicProduver (which also terminates right after the print statement) and I don't receive my message.
Thanks.
TopicConsumer.java
package jmspubsubtutorial;
import java.util.Properties;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class TopicConsumer implements MessageListener {
public static void main(String[] args) throws JMSException, NamingException{
System.out.println("---Starting TopicConsumer---");
Context context = TopicConsumer.getInitialContext();
TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) context.lookup("ConnectionFactory");
Topic topic = (Topic) context.lookup("topic/JMS_tutorial");
TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();
TopicSession topicSession = topicConnection.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
topicSession.createSubscriber(topic).setMessageListener(new TopicConsumer());
topicConnection.start();
System.out.println("---Exiting TopicConsumer---");
}
#Override
public void onMessage(Message message) {
System.out.println("--- onMessage ---");
try {
System.out.println("Incoming message: " + ((TextMessage)message).getText());
} catch (JMSException e) {
System.out.println("onMessage failed");
e.printStackTrace();
}
}
public static Context getInitialContext() throws JMSException, NamingException {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
props.setProperty("java.naming.provider.url", "localhost:1099");
Context context = new InitialContext(props);
return context;
}
}
TopicProducer.java
package jmspubsubtutorial;
import javax.jms.JMSException;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.NamingException;
public class TopicProducer {
public static void main(String[] args) throws JMSException, NamingException{
System.out.println("---Starting TopicProducer---");
Context context = TopicConsumer.getInitialContext();
TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) context.lookup("ConnectionFactory");
Topic topic = (Topic) context.lookup("topic/JMS_tutorial");
TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();
TopicSession topicSession = topicConnection.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
topicConnection.start();
TopicProducer topicProducer = new TopicProducer();
String text = "message 1 from TopicProducer...";
topicProducer.sendMessage(text, topicSession, topic);
System.out.println("---Exiting TopicProducer---");
}
public void sendMessage(String text, TopicSession topicSession, Topic topic) throws JMSException {
System.out.println("Send Message: " + text + " " + topicSession + " " + topic);
TopicPublisher topicPublisher = topicSession.createPublisher(topic);
TextMessage textMessage = topicSession.createTextMessage(text);
topicPublisher.publish(textMessage);
topicPublisher.close();
}
}

So the problem is that you are relying on the JMS library to maintain at least one non-daemon thread in order to keep your application alive after you create the consumer and assign the message listener but in reality there is no guarantee that it will do any such thing.
It's true that many JMS providers do indeed attempt to always have a single non-daemon thread running internally but assuming that this will always be the case is not really advisable. You've seemed to find that the your particular provider does not do this for you, so if you want to ensure your application stays running you should make this happen yourself.

Related

Messages are not consumed when connection use JNDI or ActiveMQConnectionFactory to connect to EmbeddedActiveMQ

This is follow-up to this question.
My code can initiate connection, session etc., however messages are not consumed. I don't see any exceptions in logs.
This test reproduces the problem:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
import java.io.File;
import java.util.Hashtable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory;
import org.junit.After;
import org.junit.Before;
public class Test {
EmbeddedActiveMQ jmsServer;
final String QUEUE_NAME = "myQueue";
#Before
public void setUp() throws Exception {
final String baseDir = File.separator + "tmp";
final EmbeddedActiveMQ embeddedActiveMQ = new EmbeddedActiveMQ();
final Configuration config = new ConfigurationImpl();
config.setPersistenceEnabled(true);
config.setBindingsDirectory(baseDir + File.separator + "bindings");
config.setJournalDirectory(baseDir + File.separator + "journal");
config.setPagingDirectory(baseDir + File.separator + "paging");
config.setLargeMessagesDirectory(baseDir + File.separator + "largemessages");
config.setSecurityEnabled(false);
AddressSettings adr = new AddressSettings();
adr.setDeadLetterAddress(new SimpleString("DLQ"));
adr.setExpiryAddress(new SimpleString("ExpiryQueue"));
config.addAddressSetting("#", adr);
config.addAcceptorConfiguration("invmConnectionFactory", "vm://0");
embeddedActiveMQ.setConfiguration(config);
this.jmsServer = embeddedActiveMQ;
this.jmsServer.start();
System.out.println("creating queue");
final boolean isSuccess = jmsServer.getActiveMQServer().createQueue(new QueueConfiguration(QUEUE_NAME)) != null;
if(isSuccess) {
System.out.println(QUEUE_NAME + "queue created");
}
}
#After
public void tearDown() {
try {
this.jmsServer.stop();
} catch(Exception e) {
// ignore
}
}
#org.junit.Test
public void simpleTest() throws Exception {
Hashtable d = new Hashtable();
d.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
d.put("connectionFactory.invmConnectionFactory", "vm://0");
final ActiveMQInitialContextFactory activeMQInitialContextFactory = new ActiveMQInitialContextFactory();
Context initialContext = activeMQInitialContextFactory.getInitialContext(d);
ConnectionFactory connectionFactory = (ConnectionFactory) initialContext.lookup("invmConnectionFactory");
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Queue queue = session.createQueue(QUEUE_NAME);
MessageProducer producer = session.createProducer(queue);
MessageConsumer consumer = session.createConsumer(queue);
CountDownLatch latch = new CountDownLatch(1);
consumer.setMessageListener(message -> {
System.out.println("=== " + message);
try {
message.acknowledge();
session.commit();
latch.countDown();
} catch(JMSException e) {
e.printStackTrace();
}
});
connection.start();
producer.send(session.createMessage());
session.commit();
if(!latch.await(2, TimeUnit.SECONDS)) {
throw new IllegalStateException();
}
connection.close();
}
}
The problem with this code is subtle but important. When configuring the broker you're creating a queue like so:
...
final String QUEUE_NAME = "myQueue";
...
jmsServer.getActiveMQServer().createQueue(new QueueConfiguration(QUEUE_NAME))
...
This is perfectly valid in and of itself, but for this use-case involving a JMS queue it's important to note that this will result in an address named myQueue and a multicast queue named myQueue since the default routing type is MULTICAST and you didn't specify any routing type on your QueueConfiguration. This is not the kind of configuration you want for a JMS queue. You want an address and an ANYCAST queue of the same name (i.e. myQueue in this case) as noted in the documentation. Therefore, you should use:
...
import org.apache.activemq.artemis.api.core.RoutingType;
...
jmsServer.getActiveMQServer().createQueue(new QueueConfiguration(QUEUE_NAME).setRoutingType(RoutingType.ANYCAST))
When you use the multicast queue the message sent by the JMS client will not actually be routed because it is sent with the anycast routing type.
Another option would be to not create the queue explicitly at all and allow it to be auto-created.

1000s of Management Queues in Artemis Web Console

I'm using the management API for ActiveMQ Artemis 2.17.0 to get the count on 1 queue using the following code;
val message = session!!.createMessage(false)
ManagementHelper.putAttribute(message, "queue.$queueName", "messageCount")
val requestor = ClientRequestor(session, "activemq.management")
val reply = requestor.request(message)
val count = (ManagementHelper.getResult(reply) as Long).toInt()
However, this code runs every 30 seconds so after about an hour it results in 1000s of new temporary queues being created which in turn, pollutes and slows the Management Console. Is there anyway to remove these queues (or better yet not have them created when retrieving the message count)? (Note: The queues are removed once the service is restarted)
I believe you need to call close() on your requestor so that the underlying temporary queue used to implement the request-reply pattern is removed.
I created a test in the ActiveMQ Artemis test-suite and the only way I could get it to fail was by not calling close() on requestor:
package org.apache.activemq.artemis.tests.integration;
import org.apache.activemq.artemis.api.core.*;
import org.apache.activemq.artemis.api.core.client.*;
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Before;
import org.junit.Test;
public class SimpleTest extends ActiveMQTestBase {
protected ActiveMQServer server;
protected ClientSession session;
protected ClientSessionFactory sf;
protected ServerLocator locator;
#Override
#Before
public void setUp() throws Exception {
super.setUp();
server = createServer(false, createDefaultInVMConfig());
server.start();
}
#Test
public void simpleTest() throws Exception {
locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(INVM_CONNECTOR_FACTORY));
sf = locator.createSessionFactory();
session = sf.createSession(false, true, true);
session.start();
server.createQueue(new QueueConfiguration("myQueue"));
long start = server.getPostOffice().getAllBindings().count();
for (int i = 0; i < 100; i++) {
ClientMessage message = session.createMessage(false);
ManagementHelper.putAttribute(message, "queue.myQueue", "messageCount");
ClientRequestor requestor = new ClientRequestor(session, "activemq.management");
requestor.request(message);
requestor.close(); // if I remove this line the test fails
}
assertEquals(0, server.getPostOffice().getAllBindings().count() - start);
}
}

can Flink receive http requests as datasource?

Flink can read a socket stream, can it read http requests? how?
// socket example
DataStream<XXX> socketStream = env
.socketTextStream("localhost", 9999)
.map(...);
There's an open JIRA ticket for creating an HTTP sink connector for Flink, but I've seen no discussion about creating a source connector.
Moreover, it's not clear this is a good idea. Flink's approach to fault tolerance requires sources that can be rewound and replayed, so it works best with input sources that behave like message queues. I would suggest buffering the incoming http requests in a distributed log.
For an example, look at how DriveTribe uses Flink to power their website on the data Artisans blog and on YouTube.
I write one custom http source. please ref OneHourHttpTextStreamFunction. you need create a fat jar to include apache httpserver classes if you want run my code.
package org.apache.flink.streaming.examples.http;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.examples.socket.SocketWindowWordCount.WordWithCount;
import org.apache.flink.util.Collector;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.bootstrap.HttpServer;
import org.apache.http.impl.bootstrap.ServerBootstrap;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.apache.flink.util.Preconditions.checkArgument;
import static org.apache.flink.util.Preconditions.checkNotNull;
public class HttpRequestCount {
public static void main(String[] args) throws Exception {
// the host and the port to connect to
final String path;
final int port;
try {
final ParameterTool params = ParameterTool.fromArgs(args);
path = params.has("path") ? params.get("path") : "*";
port = params.getInt("port");
} catch (Exception e) {
System.err.println("No port specified. Please run 'SocketWindowWordCount "
+ "--path <hostname> --port <port>', where path (* by default) "
+ "and port is the address of the text server");
System.err.println("To start a simple text server, run 'netcat -l <port>' and "
+ "type the input text into the command line");
return;
}
// get the execution environment
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// get input data by connecting to the socket
DataStream<String> text = env.addSource(new OneHourHttpTextStreamFunction(path, port));
// parse the data, group it, window it, and aggregate the counts
DataStream<WordWithCount> windowCounts = text
.flatMap(new FlatMapFunction<String, WordWithCount>() {
#Override
public void flatMap(String value, Collector<WordWithCount> out) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (String word : value.split("\\s")) {
out.collect(new WordWithCount(word, 1L));
}
}
})
.keyBy("word").timeWindow(Time.seconds(5))
.reduce(new ReduceFunction<WordWithCount>() {
#Override
public WordWithCount reduce(WordWithCount a, WordWithCount b) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new WordWithCount(a.word, a.count + b.count);
}
});
// print the results with a single thread, rather than in parallel
windowCounts.print().setParallelism(1);
env.execute("Http Request Count");
}
}
class OneHourHttpTextStreamFunction implements SourceFunction<String> {
private static final long serialVersionUID = 1L;
private final String path;
private final int port;
private transient HttpServer server;
public OneHourHttpTextStreamFunction(String path, int port) {
checkArgument(port > 0 && port < 65536, "port is out of range");
this.path = checkNotNull(path, "path must not be null");
this.port = port;
}
#Override
public void run(SourceContext<String> ctx) throws Exception {
server = ServerBootstrap.bootstrap().setListenerPort(port).registerHandler(path, new HttpRequestHandler(){
#Override
public void handle(HttpRequest req, HttpResponse rep, HttpContext context) throws HttpException, IOException {
ctx.collect(req.getRequestLine().getUri());
rep.setStatusCode(200);
rep.setEntity(new StringEntity("OK"));
}
}).create();
server.start();
server.awaitTermination(1, TimeUnit.HOURS);
}
#Override
public void cancel() {
server.stop();
}
}
Leave you comment, if you want the demo jar.

Californium Framework CoAP and PUT request

I am trying to do a request to coap server (er-rest-example) using Californium.
I succesfully do a POST request.
But with PUT I am getting a BAD REQUEST, I try using this URLs in url:
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds?
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds?color=r
But with no one get success.
What I am doing wrong?.
This is my simple script:
package coap_client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
public class cliente {
public static void main(String[] args) throws Exception {
Timer timer;
timer = new Timer();
TimerTask task = new TimerTask(){
#Override
public void run(){
String url="coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds";
URI uri= null;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
e.printStackTrace();
}
CoapClient client = new CoapClient(uri);
CoapResponse response = client.put("color=r",MediaTypeRegistry.TEXT_PLAIN);
System.out.println(response.isSuccess());
if (response!=null) {
byte[] myreponse=response.getPayload();
String respuesta2 = new String(myreponse);
System.out.println(respuesta2);
}
}
};
timer.schedule(task, 10,10*1000);
}
}
In Contiki er-rest-example, see the POST/PUT handler(1) for the LED CoAP resource. It expects a mode param without which you will get a BAD_REQUEST as response. I assume that has to go in the request body.

get active connection on HikariDataSource

I am trying to figure out how many connections are currently opened and I can't seem to find an obvious way to do that with Hikari.
HikariPool exposes that information (getActiveConnections) but I don't see an easy way to access that from HikariDataSource.
If you are using spring boot:
new HikariDataSourcePoolMetadata(dataSource).getActive();
You'll have to get it via JMX programmatic access. First, enable MBean registration through the registerMbeans property or by calling setRegisterMeans(). Then consult this page for how to perform programmatic access:
https://github.com/brettwooldridge/HikariCP/wiki/JMX-Monitoring
This can be done very directly.
dataSource.hikariPoolMXBean.activeConnections
You can use below class for better monitoring:
import javax.sql.DataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import lombok.extern.slf4j.Slf4j;
#Aspect
#Component
#Slf4j
public class DataSourceAspectLogger {
private HikariPool pool;
#Autowired
private HikariDataSource ds;
#Before("execution(* com.x.common.sql.repo.*.*(..))")
public void logBeforeConnection(JoinPoint jp) throws Throwable {
logDataSourceInfos("Before", jp);
}
#After("execution(* com.x.common.sql.repo.*.*(..)) ")
public void logAfterConnection(JoinPoint jp) throws Throwable {
logDataSourceInfos("After", jp);
}
#Autowired
public void getPool() {
try {
java.lang.reflect.Field field = ds.getClass().getDeclaredField("pool");
field.setAccessible(true);
this.pool = (HikariPool) field.get(ds);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void logDataSourceInfos(final String time, final JoinPoint jp) {
final String method = String.format("%s:%s", jp.getTarget().getClass().getName(), jp.getSignature().getName());
int totalConnections = pool.getTotalConnections();
int activeConnections = pool.getActiveConnections();
int freeConnections = totalConnections - activeConnections;
int connectionWaiting = pool.getThreadsAwaitingConnection();
log.info(String.format("%s %s: number of connections in use by the application (active): %d.", time, method, activeConnections));
log.info(String.format("%s %s: the number of established but idle connections: %d.", time, method, freeConnections));
log.info(String.format("%s %s: number of threads waiting for a connection: %d.", time, method, connectionWaiting));
log.info(String.format("%s %s: max pool size: %d.", time, method, ds.getMaximumPoolSize()));
}
}