Spring Websocket + Stomp + SockJs - spring

I built a chat application using the portfolio websocket sample as a guide. I am using spring boot 1.3.3, ActiveMQ, STOMP and the UI is built with KnockoutJs and running on Windows 2012 server.
My issue is after appx 1000 connections to the chat server (spring boot) the server stop accepting any more connections.
I played with different heartbeat settings and also messages size settings etc to no avail.
Did anyone build such a chat / websocket application and is able to achieve more than 1000 concurrent connections?
I spent over a week researching, tweaking the code and I also changed the Windows 2012 server connection limit ( seem like 2012 removed TCP connection limit).
Any help or pointers will be greatly appreciated.
/**
*
*/
package com.test.chat;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.config.StompBrokerRelayRegistration;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.SockJsServiceRegistration;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
import com.test.chat.application.event.StompConnectEvent;
import com.test.chat.application.event.StompConnectedEvent;
import com.test.chat.application.event.StompDisconnectEvent;
/**
* #author pgobin
*
* https://www.youtube.com/watch?v=mmIza3L64Ic
*
*/
#Configuration
#EnableWebSocketMessageBroker
#ComponentScan("com.test.chat")
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private static final Logger log = Logger.getLogger(WebSocketConfig.class);
#Value("${StompBrokerRelay.host}")
String StompBrokerRelayHost;
#Value("${StompBrokerRelay.port}")
int StompBrokerRelayPort;
#Value("${MessageBroker.User}")
String brokerUser;
#Value("${MessageBroker.Password}")
String brokerPassword;
#Value("${MessageBrokerStompClient.User}")
String stompClientUser;
#Value("${MessageBrokerStompClient.Password}")
String stompClientPassword;
#Value("${sockjs.setHttpMessageCacheSize}")
int sockjs_setHttpMessageCacheSize;
#Value("${sockjs.setStreamBytesLimit}")
int sockjs_setStreamBytesLimit;
#Value("${sockjs.setDisconnectDelay:20000}")
int sockjs_setDisconnectDelay;
#Value("${sockjs.setHeartbeatTime:30000}")
int sockjs_setHeartbeatTime;
// WebSocketTransport settings
#Value("${WebSocketTransportRegistration.MessageSizeLimit:131072}")
int MessageSizeLimit;
#Value("${WebSocketTransportRegistration.SendTimeLimit:15000}")
int SendTimeLimit;
#Value("${WebSocketTransportRegistration.SendBufferSizeLimit:524288}")
int SendBufferSizeLimit;
// ClientOutboundChannel configs
#Value("${ClientOutboundChannel.corePoolSize:25}")
int ClientOutboundChannelcorePoolSize;
#Value("${ClientOutboundChannel.maxPoolSize:50}")
int ClientOutboundChannelmaxPoolSize;
// ClientInboundChannel configs
#Value("${ClientInboundChannel.corePoolSize:25}")
int ClientInboundChannelcorePoolSize;
#Value("${ClientInboundChannel.maxPoolSize:50}")
int ClientInboundChannelmaxPoolSize;
/****
*
*/
#Override
public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry)
{
// Destination Prefix - Connect to default in-memory broker
// messageBrokerRegistry.enableSimpleBroker("/topic/", "/queue/");
// connect to AMQ
StompBrokerRelayRegistration broker = messageBrokerRegistry.enableStompBrokerRelay("/queue/", "/topic/");
broker.setRelayHost(StompBrokerRelayHost);
broker.setRelayPort(StompBrokerRelayPort);
broker.setSystemLogin(brokerUser);
broker.setSystemPasscode(brokerPassword);
broker.setClientLogin(stompClientUser);
broker.setClientPasscode(stompClientPassword);
// broker.setVirtualHost(virtualHost)
messageBrokerRegistry.setApplicationDestinationPrefixes("/app");
}
/*****
* https://github.com/rstoyanchev/spring-websocket-test/issues/4
*/
#Override
public void registerStompEndpoints(StompEndpointRegistry stompRegistry)
{
String wsOrigins = AppConfig.getEnv().getProperty("websocket.security.allow.origins", "http://localhost:8080");
log.info("#### ALLOWING MESSAGING ONLY FROM ORIGINS:" + wsOrigins + ". ALL OTHERS WILL BE BLOCKED ####");
String[] cors = StringUtils.split(AppConfig.getEnv().getProperty("websocket.security.allow.origins", "http://localhost:8080"), ",");
// WebSocket URL prefix
SockJsServiceRegistration reg = stompRegistry.addEndpoint("/chat").setAllowedOrigins(cors).withSockJS()
.setStreamBytesLimit(sockjs_setStreamBytesLimit).setDisconnectDelay(sockjs_setDisconnectDelay)
.setHttpMessageCacheSize(sockjs_setHttpMessageCacheSize).setHeartbeatTime(sockjs_setHeartbeatTime).setWebSocketEnabled(true)
.setSupressCors(false);
}
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
{
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
container.setAsyncSendTimeout(5000);
container.setMaxSessionIdleTimeout(600000);
return container;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration)
{
registration.setMessageSizeLimit(MessageSizeLimit);
registration.setSendTimeLimit(SendTimeLimit);
registration.setSendBufferSizeLimit(SendBufferSizeLimit);
}
/**
* Configure the {#link org.springframework.messaging.MessageChannel} used
* for outgoing messages to WebSocket clients. By default the channel is
* backed by a thread pool of size 1. It is recommended to customize thread
* pool settings for production use.
*/
#Override
public void configureClientOutboundChannel(ChannelRegistration registration)
{
registration.taskExecutor().corePoolSize(ClientOutboundChannelcorePoolSize).maxPoolSize(ClientOutboundChannelmaxPoolSize);
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration)
{
registration.taskExecutor().corePoolSize(ClientInboundChannelcorePoolSize).maxPoolSize(ClientInboundChannelmaxPoolSize);
}
/***
* Intercepts a connect event
*
* #return
*/
#Bean
public StompConnectEvent presenceChannelInterceptorOnConnect(SimpMessagingTemplate messagingTemplate)
{
return new StompConnectEvent(messagingTemplate);
}
/*
* #Bean public StompConnectedEvent
* presenceChannelInterceptorOnConnected(SimpMessagingTemplate
* messagingTemplate) { return new StompConnectedEvent(messagingTemplate); }
*/
#Bean
public StompDisconnectEvent presenceChannelInterceptorOnDisconnect(SimpMessagingTemplate messagingTemplate)
{
return new StompDisconnectEvent(messagingTemplate);
}
}
And my client test code:
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.simp.stomp.ConnectionLostException;
import org.springframework.messaging.simp.stomp.StompCommand;
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.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.WebSocketHttpHeaders;
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;
public static void runTest(final long userUid, final int clientNum)
{
//String stompUrl = "ws://localhost:8080/chat";
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(webSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
// stompClient.setMessageConverter(new StringMessageConverter());
stompClient.setMessageConverter(new org.springframework.messaging.converter.MappingJackson2MessageConverter());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setDefaultHeartbeat(new long[] { 0, 0 });
ConsumerStompSessionHandler handler = new ConsumerStompSessionHandler(BROADCAST_MESSAGE_COUNT, connectLatch, subscribeLatch, messageLatch,
disconnectLatch, failure, clientNum);
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("userid", userUid);
WebSocketHttpHeaders wsHeaders = new WebSocketHttpHeaders();
wsHeaders.add("userid", "" + userUid);
StompHeaders stompHeaders = new StompHeaders();
stompHeaders.add("userid", "" + userUid);
stompHeaders.add("channelID", "java-" + System.currentTimeMillis());
stompHeaders.add("platform", "Windows");
stompHeaders.add("clientIP", "10.1.1.1");
// stompClient.connect(stompUrl, handler, params);
stompClient.connect(stompUrl, wsHeaders, stompHeaders, handler, params);
}
private static class ConsumerStompSessionHandler extends StompSessionHandlerAdapter {
private final int expectedMessageCount;
private final CountDownLatch connectLatch;
private final CountDownLatch subscribeLatch;
private final CountDownLatch messageLatch;
private final CountDownLatch disconnectLatch;
private final AtomicReference<Throwable> failure;
private AtomicInteger messageCount = new AtomicInteger(0);
int clientNum = 0;
public ConsumerStompSessionHandler(int expectedMessageCount, CountDownLatch connectLatch, CountDownLatch subscribeLatch,
CountDownLatch messageLatch, CountDownLatch disconnectLatch, AtomicReference<Throwable> failure, int clientNum)
{
this.expectedMessageCount = expectedMessageCount;
this.connectLatch = connectLatch;
this.subscribeLatch = subscribeLatch;
this.messageLatch = messageLatch;
this.disconnectLatch = disconnectLatch;
this.failure = failure;
this.clientNum = clientNum;
}
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders)
{
__ActiveConn = __ActiveConn + 1;
this.connectLatch.countDown();
session.setAutoReceipt(true);
final RequestUserList req = new RequestUserList();
req.setCustomerid(customerID);
String channelID = System.currentTimeMillis() + "";
String subscribeChannel = __SUBSCRIBE_PREDICATE_QUEUE + channelID;
final String sendChannel = __SEND_PREDICATE + "userListOnline";
req.setChannelID(channelID);
// session.send(sendChannel, req);
// System.out.println("Client " + clientNum + " connected");
session.subscribe(subscribeChannel, new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers)
{
System.out.println("Got ResponseH");
return String.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload)
{
System.out.println("Got ResponseA");
/* if (messageCount.incrementAndGet() == expectedMessageCount)
{
messageLatch.countDown();
disconnectLatch.countDown();
session.disconnect();
}*/
}
}).addReceiptTask(new Runnable() {
#Override
public void run()
{
System.out.println("Got Response for client " + clientNum);
//subscribeLatch.countDown();
}
});
// session.send(sendChannel, req);
}
#Override
public void handleTransportError(StompSession session, Throwable exception)
{
__ErrorConn = __ErrorConn + 1;
logger.error("Transport error", exception);
this.failure.set(exception);
if (exception instanceof ConnectionLostException)
{
this.disconnectLatch.countDown();
}
}
#Override
public void handleException(StompSession s, StompCommand c, StompHeaders h, byte[] p, Throwable ex)
{
logger.error("Handling exception", ex);
this.failure.set(ex);
}
#Override
public void handleFrame(StompHeaders headers, Object payload)
{
System.out.println("Got ResponseF");
Exception ex = new Exception(headers.toString());
logger.error("STOMP ERROR frame", ex);
this.failure.set(ex);
}
#Override
public String toString()
{
return "ConsumerStompSessionHandler[messageCount=" + this.messageCount + "]";
}
}
public static void main(String[] args)
{
try
{
int clientCount = 3000;
for (int x = 0; x < clientCount; x++){
runTest(121807, x+1);
}
System.out.println("DONE...");
System.out.println("Live Connections = " + __ActiveConn);
System.out.println("Error Connections = " + __ErrorConn);
/*
for (int x = 0; x < clientCount; x++)
{
final int clientNum = x;
ThreadPoolManager.executorService.execute(new Runnable() {
#Override
public void run()
{
try
{
Thread.sleep(500);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
runTest(121807, clientNum);
}
});
}
*/
System.out.println("DONE..Waiting..");
Thread.sleep(1000000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Related

how to pass the parameters to Websocket endpoint hander

When I create jetty websocket, I register my endpoint handler like this:
public class MyWebSocketEndpoint extends WebSocketServlet {
#Override
public void configure(WebSocketServletFactory webSocketServletFactory) {
webSocketServletFactory.register(MyEndpointHandler.class);
}
}
for MyEndpoingHandler class, I can't define a constructor with some parameters, or it will got runtime exception. How can I pass some parameters when create the MyEndpointHandler instance?
Use a WebSocketCreator.
When you call WebSocketServletFactory.register(MyEndpoingHandler.class) all that's happening internally is the equivalent of ...
#Override
public void register(Class<?> websocketPojo)
{
this.setCreator(new SingleEndpointCreator(websocketPojo));
}
Complete example on WebSocketCreator:
package websocket;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
public class DemoWebSocketCreator
{
public static class MyWebSocketServlet extends WebSocketServlet
{
#Override
public void configure(WebSocketServletFactory wsFactory)
{
wsFactory.setCreator(new MyWebSocketCreator());
}
}
public static class MyWebSocketCreator implements WebSocketCreator
{
private AtomicInteger idGen = new AtomicInteger(0);
#Override
public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse)
{
String id = "ws" + idGen.incrementAndGet();
return new MyWebSocket(id);
}
}
#WebSocket
public static class MyWebSocket
{
private final String id;
public MyWebSocket(String id)
{
this.id = id;
}
#OnWebSocketMessage
public void onMessage(Session session, String msg)
{
try
{
session.getRemote().sendString("Hello, my id is [" + id + "]: You said: " + msg);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(MyWebSocketServlet.class, "/ws/");
// always last, and on default pathspec
context.addServlet(DefaultServlet.class, "");
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
server.start();
server.join();
}
}

How to access 2 Elasticsearch Instance through RestHighLevelClient in SpringBoot

I trying to access 2 Elasticsearch Server instance through HighLevelRestClient but I couldn't through this array object
HttpHost[] httpHost = new HttpHost(hostName[i...], Integer.parseInt(hostName[i..]), "http");
In hostname i have 2 values in the restHighLevelClient = new RestHighLevelClient( RestClient.builder(httpHost));
I'm also unable to access through second array instance.
Can I have 2 configuration Class if such way a how to create 2 instance of HighLevelRestClient
Or is it any possible way through 2 bean instance if such a way how it is possible
Since we need to have 2 different restHighLevelClient instance.
Kindly let me know in case of more information needed.
Code
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppElasticSearchConfiguration extends AbstractFactoryBean<RestHighLevelClient> {
private static final Logger LOG = LoggerFactory.getLogger(AppElasticSearchConfiguration.class);
#Value("${application.elasticsearch.host}")
private String hostName[];
private RestHighLevelClient restHighLevelClient;
#Override
public void destroy() {
try {
if (restHighLevelClient != null) {
restHighLevelClient.close();
}
} catch (final Exception e) {
LOG.error("Error closing ElasticSearch client: ", e);
}
}
#Override
public Class<RestHighLevelClient> getObjectType() {
return RestHighLevelClient.class;
}
#Override
public boolean isSingleton() {
return false;
}
#Override
public RestHighLevelClient createInstance() {
return buildClient();
}
private RestHighLevelClient buildClient() {
try {
HttpHost[] httpHost = null;
if(hostName!=null) {
httpHost = new HttpHost[hostName.length];
for (int i = 0; i < httpHost.length; i++) {
httpHost[i] = new HttpHost(hostName[i].split(":")[0],
Integer.parseInt(hostName[i].split(":")[1]), "http");
}
}
restHighLevelClient = new RestHighLevelClient( RestClient.builder(httpHost));
} catch (Exception e) {
LOG.error(e.getMessage());
}
return restHighLevelClient;
}
//public RestHighLevelClient getAppRestHighLevelClient() { return restHighLevelClient; }
}
Hi Just pass the secondary instance in the constructor of HttpHost.
#Bean(destroyMethod = "close")
public RestHighLevelClient buildClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost(hostPrimary, Integer.valueOf(portPrimary), "http"),
new HttpHost(hostSecondary, Integer.valueOf(portSecondary), "http"));
RestHighLevelClient client = new RestHighLevelClient(builder);
LOG.info("RestHighLevelClient has been created with:{}", client);
return client;
}

Message is not consumed by all consumers when network brokers is configured in ActiveMQ

I have 2 instances of my application on the same machine (although it could be on different machines as well) with two Tomcat instances with different ports and Apache ActiveMQ is embedded in the application.
I have configured a static network of brokers so that the message from one instance can be consumed by all other instance as well (each instance can be producer and consumer).
servlet:
package com.activemq.servlet;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.jms.JMSException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.activemq.ActiveMQStartup;
import com.activemq.MQPublisher;
import com.activemq.SendMsg;
import com.activemq.SendMsgToAllInstance;
import com.activemq.TestPublisher;
/**
* Servlet implementation class ActiveMQStartUpServlet
*/
#WebServlet(value = "/activeMQStartUpServlet", loadOnStartup = 1)
public class ActiveMQStartUpServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private ActiveMQStartup mqStartup = null;
private static final Map pooledPublishers = new HashMap();
#Override
public void init(ServletConfig config) throws ServletException {
System.out.println("starting servelt--------------");
super.init(config);
//Apache Active MQ Startup
mqStartup = new ActiveMQStartup();
mqStartup.startBrokerService();
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getParameter("distributedMsg"));
String mqConfig = null;
String distributedMsg = req.getParameter("distributedMsg");
String simpleMsg = req.getParameter("simpleMsg");
if (distributedMsg != null && !distributedMsg.equals(""))
mqConfig = "distributedMsg";
else if (simpleMsg != null && !simpleMsg.equals(""))
mqConfig = "simpleMsg";
MQPublisher publisher = acquirePublisher(mqConfig);
try {
publisher.publish(mqConfig);
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
releasePublisher(publisher);
}
}
#SuppressWarnings("unchecked")
private void releasePublisher(MQPublisher publisher) {
if (publisher == null) return;
#SuppressWarnings("rawtypes")
LinkedList publishers;
TestPublisher poolablePublisher = (TestPublisher)publisher;
publishers = getPooledPublishers(poolablePublisher.getConfigurationName());
synchronized (publishers) {
publishers.addLast(poolablePublisher);
}
}
private MQPublisher acquirePublisher(String mqConfig) {
LinkedList publishers = getPooledPublishers(mqConfig);
MQPublisher publisher = getMQPubliser(publishers);
if (publisher != null) return publisher;
try {
if (mqConfig.equals("distributedMsg"))
return new TestPublisher(MQConfiguration.getConfiguration("distributedMsg"), new SendMsgToAllInstance());
else
return new TestPublisher(MQConfiguration.getConfiguration("simpleMsg"), new SendMsg());
}catch(Exception e){
e.printStackTrace();
}
return null;
}
private LinkedList getPooledPublishers(String mqConfig) {
LinkedList publishers = null;
publishers = (LinkedList) pooledPublishers.get(mqConfig);
if (publishers == null) {
synchronized(pooledPublishers) {
publishers = (LinkedList) pooledPublishers.get(mqConfig);
if (publishers == null) {
publishers = new LinkedList();
pooledPublishers.put(mqConfig, publishers);
}
}
}
return publishers;
}
private MQPublisher getMQPubliser(LinkedList publishers) {
synchronized (publishers) {
while (!publishers.isEmpty()) {
TestPublisher publisher = (TestPublisher)publishers.removeFirst();
return publisher;
}
}
return null;
}
}
Configuration:
package com.activemq.servlet;
import java.util.HashMap;
import java.util.Map;
import javax.jms.JMSException;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import org.apache.activemq.ActiveMQConnectionFactory;
import com.activemq.ActiveMQContext;
public class MQConfiguration {
private static final Map configurations = new HashMap();
private String mqConfig;
private String topicName;
private TopicConnection topicConnection = null;
private MQConfiguration(String mqConfig, String string, String string2) {
this.mqConfig = mqConfig;
try {
String topicFactoryConName = ActiveMQContext.getProperty(mqConfig);
this.topicName = (mqConfig.equals("distributedMsg") ? ActiveMQContext.getProperty("distributedTopic"):ActiveMQContext.getProperty("normalTopic"));
TopicConnectionFactory factory = (ActiveMQConnectionFactory) ActiveMQContext.getContext()
.lookup(topicFactoryConName);
this.topicConnection = factory.createTopicConnection();
this.topicConnection.start();
} catch (Exception e) {
System.out.println("error: " + e);
}
}
public static MQConfiguration getConfiguration(String mqConfig) {
if (mqConfig == null || "".equals(mqConfig)) {
throw new IllegalArgumentException("mqConfig is null or empty");
}
MQConfiguration config = null;
if (config != null) {
return config;
}
synchronized (configurations) {
config = (MQConfiguration) configurations.get(mqConfig);
if (config == null) {
config = new MQConfiguration(mqConfig, "userName", "userPassword");
}
configurations.put(mqConfig, config);
}
return config;
}
public String getMqConfig() {
return this.mqConfig;
}
public TopicSession createTopicSession(boolean isTransacted, int autoAcknowledge) throws JMSException {
if (this.topicConnection == null) {
IllegalStateException ise = new IllegalStateException("topic connection not configured");
throw ise;
}
return this.topicConnection.createTopicSession(isTransacted, autoAcknowledge);
}
public Topic getTopic() {
try {
return (Topic) ActiveMQContext.getContext().lookup(this.topicName);
} catch (Exception e) {
e.getMessage();
}
return null;
}
}
publisher:
package com.activemq;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import com.activemq.servlet.MQConfiguration;
public class TestPublisher implements MQPublisher {
private final String configurationName;
private TopicSession topicSession = null;
private TopicPublisher topicPublisher = null;
public TestPublisher(MQConfiguration config, Object messageListener) throws JMSException {
if (config == null) {
throw new IllegalArgumentException("config == null");
}
Topic topic = config.getTopic();
this.configurationName = config.getMqConfig();
this.topicSession = config.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
this.topicPublisher = this.topicSession.createPublisher(topic);
MessageConsumer msgConsumer = this.topicSession.createConsumer(topic);
msgConsumer.setMessageListener((MessageListener) messageListener);
}
#Override
public void publish(String msg) throws JMSException {
this.topicPublisher.publish(createMessage(msg, this.topicSession));
}
private Message createMessage(String msg, Session session) throws JMSException {
TextMessage message = session.createTextMessage(msg);
return message;
}
public String getConfigurationName() {
return this.configurationName;
}
}
Consumer:
package com.activemq;
import javax.jms.Message;
import javax.jms.MessageListener;
public class SendMsgToAllInstance implements MessageListener {
#Override
public void onMessage(Message arg0) {
System.out.println("distributed message-------------");
// We have call to dao layer to to fetch some data and cached it
}
}
JNDI:activemq-jndi.properties
# JNDI properties file to setup the JNDI server within ActiveMQ
#
# Default JNDI properties settings
#
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://localhost:61616
activemq.network.connector=static:(tcp://localhost:61620)
#activemq.network.connector=broker:(tcp://localhost:61619,network:static:tcp://localhost:61620)?persistent=false&useJmx=true
activemq.data.directory=data61619
activemq.jmx.port=1099
#
# Set the connection factory name(s) as well as the destination names. The connection factory name(s)
# as well as the second part (after the dot) of the left hand side of the destination definition
# must be used in the JNDI lookups.
#
connectionFactoryNames = distributedMsgFactory,simpleMsgFactory
topic.jms/distributedTopic=distributedTopic
topic.jms/normalTopic=normalTopic
distributedMsg=distributedMsgFactory
simpleMsg=simpleMsgFactory
distributedTopic=jms/distributedTopic
normalTopic=jms/normalTopic
ActiveMQStartup:
package com.activemq;
import java.net.URI;
import org.apache.activemq.broker.BrokerPlugin;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.TransportConnector;
import org.apache.activemq.broker.jmx.ManagementContext;
import org.apache.activemq.network.NetworkConnector;
import org.apache.activemq.security.JaasAuthenticationPlugin;
public class ActiveMQStartup {
private final String bindAddress;
private final String dataDirectory;
private BrokerService broker = new BrokerService();
protected final int numRestarts = 3;
protected final int networkTTL = 2;
protected final int consumerTTL = 2;
protected final boolean dynamicOnly = true;
protected final String networkBroker;
protected final String jmxPort;
public ActiveMQStartup() {
ActiveMQContext context = new ActiveMQContext();
context.loadJndiProperties();
bindAddress = ActiveMQContext.getProperty("java.naming.provider.url");
dataDirectory = ActiveMQContext.getProperty("activemq.data.directory");
networkBroker = ActiveMQContext.getProperty("activemq.network.connector");
jmxPort = ActiveMQContext.getProperty("activemq.jmx.port");
}
// Start activemq broker service
public void startBrokerService() {
try {
broker.setDataDirectory("../" + dataDirectory);
broker.setBrokerName(dataDirectory);
broker.setUseShutdownHook(true);
TransportConnector connector = new TransportConnector();
connector.setUri(new URI(bindAddress));
//broker.setPlugins(new BrokerPlugin[]{new JaasAuthenticationPlugin()});
ManagementContext mgContext = new ManagementContext();
if (networkBroker != null && !networkBroker.isEmpty()) {
NetworkConnector networkConnector = broker.addNetworkConnector(networkBroker);
networkConnector.setName(dataDirectory);
mgContext.setConnectorPort(Integer.parseInt(jmxPort));
broker.setManagementContext(mgContext);
configureNetworkConnector(networkConnector);
}
broker.setNetworkConnectorStartAsync(true);
broker.addConnector(connector);
broker.start();
} catch (Exception e) {
System.out.println("Failed to start Apache MQ Broker : " + e);
}
}
private void configureNetworkConnector(NetworkConnector networkConnector) {
networkConnector.setDuplex(true);
networkConnector.setNetworkTTL(networkTTL);
networkConnector.setDynamicOnly(dynamicOnly);
networkConnector.setConsumerTTL(consumerTTL);
//networkConnector.setStaticBridge(true);
}
// Stop broker service
public void stopBrokerService() {
try {
broker.stop();
} catch (Exception e) {
System.out.println("Unable to stop the ApacheMQ Broker service " + e);
}
}
}
I am starting the tomcat instance one by one and seeing the network connection between the broker is getting established.
When I am sending messge from instance1 or instance2(first time) it is consuming on that instance only, but when I am sending message from the second instance it is consumed by both;
Code in git: https://github.com/AratRana/ApacheActiveMQ
Could you point me where I am wrong?
Finally, I am able to do it. When I started the consumer during server startup then I am able to see the message consumer in all instances. So to achieve this the consumers needs to be started before publishing any message.

Server Client Communication on Socket not working bi-directionally

I am new to Socket Programming and very well know that i am making a silly mistake. Unfortunately not able to figure out as to what is going on. I have created a basic multi Client-Server system using Java socket on same machine(shouldn't make any difference), however to my surprise either i am able to only
Send Message from Server to Client and then read it on Client.
OR
Send Message from Client to Server and then read it on Server.
What i am not able to achieve is
Send Message from Server to Client and then read it on Client.
AND
Send Message from Client to Server and then read it on Server.
I am attaching my code.
import java.io.IOException;
import java.net.ServerSocket;
public class ServerSocketMain {
public static void main(String[] args) throws IOException {
#SuppressWarnings("resource")
ServerSocket serverSocket = new ServerSocket(1234);
while(true){
System.out.println("Server Waiting for Client to Connect ....");
Thread thread = new Thread(new ServerReaderWriter(serverSocket.accept()));
System.out.println("Client connected....");
thread.start();
}
}
}
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderWriter implements Runnable {
private Socket socket;
public ServerReaderWriter(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try {
readFromClient();
writeToClient();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeToClient() throws IOException {
DataOutputStream writeToClient = new DataOutputStream(this.socket.getOutputStream());
writeToClient.writeBytes("Communication from Client Thread "
+ Thread.currentThread().getName() + "\n");
}
private void readFromClient() throws IOException {
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(
this.socket.getInputStream()));
System.out.println("Client Communication : " + inFromClient.readLine());
}
}
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ClientSocketMain {
public static void main(String[] args) throws UnknownHostException, IOException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.submit(new ClientReaderWriter(new Socket("localhost", 1234)));
}
executorService.shutdown();
}
}
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class ClientReaderWriter implements Runnable {
private Socket socket;
public ClientReaderWriter(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try {
readFromServer(socket.getInputStream());
writeToServer(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void writeToServer(OutputStream outputStream) throws IOException {
DataOutputStream writeToServer = new DataOutputStream(outputStream);
writeToServer.writeBytes("Communication from Client Thread "
+ Thread.currentThread().getName() + "\n");
}
private void readFromServer(InputStream inputStream) throws IOException {
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(
inputStream));
System.out.println(inFromServer.readLine());
}
}
I am just wondering if my client socket closes even before read and write operations are done..not sure just guessing out loud. please help!!
As it turned out...i was making a silly mistake....Ordering is very important in the code snippet that i had shared above and i was ignoring the fact.
readFromClient();
writeToClient();
readFromServer(socket.getInputStream());
writeToServer(socket.getOutputStream());
In the above scenario both server and client are trying to read...instead one should be writing and other reading. So all i needed to do was change the order
writeToClient();
readFromClient();
readFromServer(socket.getInputStream());
writeToServer(socket.getOutputStream());

Jetty Websocket Compilation Errors

I am trying to do an Jetty Web Socket example .
I copied a example from internet , which was working fine when i deployed directly into server without making any chnages .
But when i copied the Source (the servlet) into Eclipse IDE , it was giving Compilation
Exceptions related to
The method onClose(int, String) of type Html5Servlet.StockTickerSocket must override a superclass method
- The method onOpen(WebSocket.Connection) of type Html5Servlet.StockTickerSocket must override a superclass method
The method onMessage(String) of type Html5Servlet.StockTickerSocket must override a superclass method
This is my servlet , i kept the jars as it is mentioned in that example
package org.ajeesh.app;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
public class Html5Servlet extends WebSocketServlet {
private AtomicInteger index = new AtomicInteger();
private static final List<String> tickers = new ArrayList<String>();
static{
tickers.add("ajeesh");
tickers.add("peeyu");
tickers.add("kidillan");
tickers.add("entammo");
}
/**
*
*/
private static final long serialVersionUID = 1L;
public WebSocket doWebSocketConnect(HttpServletRequest req, String resp) {
System.out.println("On server");
return new StockTickerSocket();
}
protected String getMyJsonTicker(){
StringBuilder start=new StringBuilder("{");
start.append("\"stocks\":[");
int counter=0;
for (String aTicker : tickers) {
counter++;
start.append("{ \"ticker\":\""+aTicker +"\""+","+"\"price\":\""+index.incrementAndGet()+"\" }");
if(counter<tickers.size()){
start.append(",");
}
}
start.append("]");
start.append("}");
return start.toString();
}
public class StockTickerSocket implements WebSocket.OnTextMessage{
private Connection connection;
private Timer timer;
#Override
public void onClose(int arg0, String arg1) {
System.out.println("Web socket closed!");
}
#Override
public void onOpen(Connection connection) {
System.out.println("onOpen!");
this.connection=connection;
this.timer=new Timer();
}
#Override
public void onMessage(String data) {
System.out.println("onMessage!");
if(data.indexOf("disconnect")>=0){
connection.close();
timer.cancel();
}else{
sendMessage();
}
}
private void sendMessage() {
System.out.println("sendMessage!");
if(connection==null||!connection.isOpen()){
System.out.println("Connection is closed!!");
return;
}
timer.schedule(new TimerTask() {
#Override
public void run() {
try{
System.out.println("Running task");
connection.sendMessage(getMyJsonTicker());
}
catch (IOException e) {
e.printStackTrace();
}
}
}, new Date(),5000);
}
}
}

Resources