Here's my server's code
package local.xx.mavenws;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.enterprise.context.ApplicationScoped;
import org.joda.time.DateTime;
#ApplicationScoped
#ServerEndpoint("/")
public class Server {
private final ArrayList<Session> sessions;
public Server() {
this.sessions = new ArrayList<>();
}
#OnOpen
public void onOpen(Session session) {
this.sessions.add(session);
this.echo("Client connected!");
}
#OnClose
public void onClose(Session session) {
this.sessions.remove(session);
this.echo("Client disconnected!");
}
#OnError
public void onError(Throwable error) {
this.echo("Error occured!");
this.echo(error.getLocalizedMessage());
}
#OnMessage
public void onMessage(String message, Session session) {
try {
message = "[" + this.currentDate() + "] " + message;
this.echo(message);
for( Session sess : this.sessions ) {
sess.getBasicRemote().sendText(message);
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void echo(String info) {
System.out.println(info);
}
private String currentDate() {
String dateArray[] = (new DateTime()).toString().split("T");
String date = dateArray[0] + " " + (dateArray[1].split("\\.")[0]);
return date;
}
}
I want it to send received message to all the users connected. The problem is, it treats every connection individually like each one of them had it's own instance of the server. When I connect in two browser windows, messages show separately. Does anybody have any ideas on this?
Finally got this working! The solution is that the sessions variable must be static and I had to call it always by Server scope, not this. That implicates the fact that, despite there's a new instance of Server created for every user connected, the variable is mutual for everyone.
Related
I'm new to Docker and I would like to put my rmi server on docker and to access it using the client which is local, on the laptop. When the server is not on docker the application works, however when it is on docker, I get a "Connection Refused" error. Below is the code for the server:
import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.annotation.Validated;
import ro.tuc.ds2020.services.MedicationPlanService;
import ro.tuc.ds2020.servinterface.IMedicationService;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
#SpringBootApplication
#Validated
public class Ds2020Application extends SpringBootServletInitializer {
private int port;
// private Registry registry;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Ds2020Application.class);
}
public Ds2020Application() throws RemoteException{
this.port = 8889;
Registry registry = null;
try {
registry = LocateRegistry.createRegistry(this.port);
} catch (RemoteException e) {
e.printStackTrace();
}
try {
//MedicationPlanService is the stub // "name",stub
registry.bind(IMedicationService.class.getSimpleName(), new MedicationPlanService());
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
try {
SpringApplication.run(Ds2020Application.class, args);
System.out.println("The server has started.");
} catch (Exception e) {
e.printStackTrace();
}
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
and client:
package ro.tuc.ds2020.controller;
import ro.tuc.ds2020.DTO.DrugPlanDTO;
import ro.tuc.ds2020.servinterface.IMedicationService;
import ro.tuc.ds2020.view.DisplayTable;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
public class ClientController {
private static IMedicationService iMedicationService;
private Registry registry;
private String serverAddress;
private int serverPort;
private DisplayTable displayTable;
public ClientController() throws RemoteException, NotBoundException, SQLException, ParseException {
this.serverAddress = "localhost";
this.serverPort = 8889;
String patientID="334975ea-c90d-4fd2-9a0b-9e2c9f0e4cb9";
//obtain the stub for the registry
registry = LocateRegistry.getRegistry(serverAddress, serverPort);
//obtain stub for remote object from server registry
iMedicationService = (IMedicationService) (registry.lookup(IMedicationService.class.getSimpleName()));
ArrayList<DrugPlanDTO> dto= iMedicationService.getAllMedicationPlans(patientID);
for (DrugPlanDTO drugplans:
dto) {
System.out.println((String)drugplans.getBegin_time());
}
this.displayTable=new DisplayTable(dto,patientID);
System.out.println("Finished");
}
public static void sendMessage(String drugPlanID,String patientID,String medicationName,String begin_time,String end_time) throws RemoteException, SQLException {
if (iMedicationService != null) {
iMedicationService.savePillTakenLog(drugPlanID,patientID,medicationName,begin_time,end_time);
System.out.println("Finished send message"+ patientID);
}
}
}
Im not sure how to bind the client to the server when dockerizing the application.
Docker compose:
version: '3'
services:
tomcat-db-api:
image: ds_a3
ports:
- "8889:8889"
rabbitmq-container:
image: rabbitmq:management
ports:
- 5672:5672
- 15672:15672
Try setting the: this.serverAddress="tomcat-db-api;" at your client.
I am trying JMS 2.0 so I can decide if it is worth applying in my project. I could successfully create a send/receive application.
Now I would like to have listeners that will receive the message as soon as it is available on the queue (my final goal is to have different listeners to the same queue, each with a different message selector.
Currently I have this class:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType = 3;
private transient JMSConsumer consumer;
private transient JMSConsumer consumer2;
private transient JMSConsumer consumer3;
public SenderBean() {
}
#PostConstruct
public void setUp(){
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
// consumer2 = context.createConsumer(myQueue, "type=2");
// consumer2.setMessageListener(new ListenerTypeTwo());
//
// consumer3 = context.createConsumer(myQueue, "type=3");
// consumer3.setMessageListener(new ListenerTypeThree());
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
System.out.println("producer sending msg type " + nextType + "value: " + text);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeTwo implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ListenerTypeThree implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
I commented out two consumers, so I could focus on making one work.
I keep getting the following exception on the setMessageListener line:
javax.jms.IllegalStateException: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6
at org.hornetq.ra.HornetQRASession.checkStrict(HornetQRASession.java:1647)
at org.hornetq.ra.HornetQRAMessageConsumer.setMessageListener(HornetQRAMessageConsumer.java:124)
at org.hornetq.jms.client.HornetQJMSConsumer.setMessageListener(HornetQJMSConsumer.java:68)
I have no idea what could be causing this and my searches are not giving me any extra information.
I guess it could be something related to the fact that one componen should have no more than one active session. In this case, how could I create multiple listeners to listen to the queue?
(if important: I am using Wildfly 8)
EDIT
I've extracted the listener creation to a separate bean and still teh same error:
package learning.jms;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
#ApplicationScoped
public class ListenerOne {
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private JMSContext context;
#Resource(mappedName="queues/myQueue")
private Queue myQueue;
private JMSConsumer consumer;
public void setUp() {
consumer = context.createConsumer(myQueue, "type=1");
consumer.setMessageListener(new ListenerTypeOne());
System.out.println("working");
}
private class ListenerTypeOne implements MessageListener{
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne:" + msg.getBody(String.class));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
So, looking for MDBs solved the issue.
I cleaned the senderBean classfrom any traces of the consumers I was trying to create:
package learning.jms;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.Queue;
#Named(value="senderBean")
#SessionScoped
public class SenderBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Resource(mappedName="queues/myQueue")
private transient Queue myQueue;
#Inject
#JMSConnectionFactory("java:/DefaultJMSConnectionFactory")
private transient JMSContext context;
private String messageText;
private int nextType;
public SenderBean() {
// TODO Auto-generated constructor stub
}
#PostConstruct
public void init(){
nextType=2;
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public void sendJMSMessageToMyQueue() {
try {
String text = "Message from producer: " + messageText;
Message m1 = context.createTextMessage(text);
m1.setIntProperty("type", nextType);
nextType = (nextType++%3)+1;
context.createProducer().send(myQueue, m1);
FacesMessage facesMessage =
new FacesMessage("Sent message: " + text);
FacesContext.getCurrentInstance().addMessage(null, facesMessage);
} catch (JMSRuntimeException | JMSException t) {
System.out.println(t.toString());
}
}
}
(notice it is just session scope so I can iterate over the message types")
And created 3 MDBs
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=1")
})
public class ListenerOne implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeOne: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
two:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=2")
})
public class ListenerTwo implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeTwo: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
three:
package learning.jms;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationLookup",
propertyValue = "queues/myQueue"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "messageSelector",propertyValue = "type=3")
})
public class ListenerThree implements MessageListener {
#Override
public void onMessage(Message msg) {
try {
System.out.println("Msg received by typeThree: " + msg.getBody(String.class) + " type: " + msg.getIntProperty("type"));
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now they listen automatically for messages in the queue that match their selector.
Thanks #prabugp for the help :)
The above error could be because you are trying to use the client inside the same container and getting connection factory from JCA based connection factory.
Case 1: if client are remote to jms server then using jms/RemoteConnectionFactory is recommended and above problem will not be reproduced.
Case 2: if client resides in same container then connections from JCA based connection factory java/JmsXA is preferred. Since there is a limitation in JEE 7 specification under section 6:7 that JEE server does not permit EJB/Web application to have more than one active session i.e you cannot have legacy JMS application.
E.g: In a method:
public void startConnection() {
try {
TopicConnectionFactory connectionFactory = (TopicConnectionFactory) getConnectionFactory();
topicConnection = connectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
subscriber = topicSession.createSubscriber(messageTopic, selector, true);
MessageListener messageListener = new MessageListener(this);
// Code to set message listener.
subscriber.setMessageListener(messageListener);
topicConnection.start();
} catch (Exception e) {
LOG.error(e, e);
closeConnection();
throw new RuntimeException(e);
}
}
if connection factory on above code is from
#Resource(mappedName = "java:jboss/exported/jms/RemoteConnectionFactory")
then above code will work. But if we change the connection factory to
//#Resource(mappedName = "java:/JmsXA")
then above error will be thrown.
So if your client is in the same container then MDB should be used. Since container wants control of connection objects to support two phase commit protocol.
My view on this, you can not have a message listener in a JSF bean, since the bean lifecycle is controlled by the web container.
MDBs are the only components that are driven by messages, but JSF MBs, like EJBs or servlets, cannot listen for messages, they "live" in the context of a request, and instances are created, activated, passivated or destroyed by the container.
But instead, you can use receive(), in the context of a request, and set up some autorefresh on client side, to implement a flow that is server-side driven.
I have two files server.java and client.java. Basically there is no error or message like Client Connected, Connected to server, Message:..., etc.
But if i change localhost to something else client.java does give an error but if it's correct no error or response.
server.java has this:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.json.Json;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.glassfish.tyrus.server.*;
#ServerEndpoint("/game")
public class socketServer{
public static void main(String[] args) {
new socketServer();
}
public socketServer() {
runServer();
}
public static void runServer() {
Server s = new Server("localhost", 8025, "/websockets", null, socketServer.class);
try {
s.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#OnMessage
public String onMessage(String message, Session s) throws IOException {
System.out.println("User input: " + message);
s.getBasicRemote().sendText("Hello world Mr. " + message);
return message;
}
#OnOpen
public void onOpen(Session s) throws IOException {
System.out.println("Client connected");
s.getBasicRemote().sendText("wtf");
}
#OnClose
public void onClose() {System.out.println("Connection closed");}
#OnError
public void handleError(Throwable t) {t.printStackTrace();}
}
client.java has this:
import java.net.URI;
import java.net.URISyntaxException;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.ClientEndpoint;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.DeploymentException;
import org.glassfish.tyrus.client.ClientManager;
#ClientEndpoint
public class clientServer {
public static void main(String[] args) {
new clientServer();
}
public clientServer() {
ClientManager client = ClientManager.createClient();
try {
client.connectToServer(clientServer.class, new URI("ws://localhost:8025/websockets/game"));
} catch (Exception e) {
e.printStackTrace();
}
}
#OnOpen
public void onOpen(Session session) {
System.out.println("connected to server");
try {
session.getBasicRemote().sendText("start");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#OnMessage
public String onMessage(String message, Session session) {
return message;
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Client: closed");
}
}
I guess server.java and client.java have to use the same host name. If you change the URI in client.java, for example, from "ws://localhost:8025/websockets/game" to "ws://myhost:8025/websockets/game", try to change the first argument of the constructor of Server from "localhost" to "myhost".
I am not able to receive notifications using HCATALOG using JMS. I wrote simple producer consumer program.
Apache MQ service is running in background. I am able to send simple text messages using ApacheMQ. But "markPartitionForEvent()" is not able to send event to consumer's "onMessage()" call.
I refered following link :
https://cwiki.apache.org/confluence/display/Hive/HCatalog+Notification
Please guide
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.*;
import org.apache.hadoop.conf.Configuration;
import org.apache.thrift.TException;
import javax.jms.MessageConsumer;
import javax.management.*;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
import org.apache.hadoop.hive.metastore.api.PartitionEventType;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hcatalog.common.HCatConstants;
import java.util.Properties;
import javax.jms.*;
import javax.jdo.*;
import javax.naming.*;
import java.io.*;
import java.io.InputStreamReader;
import java.util.*;
import javax.jms.MessageConsumer;
import org.slf4j.LoggerFactory;
import org.datanucleus.api.jdo.*;
import javax.jdo.metadata.JDOMetadata;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.properties.PropertyStore;
import org.datanucleus.store.AbstractStoreManager;
import org.apache.hadoop.hive.metastore.api.Table;
class Consumer implements MessageListener
{
public void start(){
try
{
HiveConf hiveConf;
ActiveMQConnectionFactory connFac = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection conn = connFac.createConnection();
conn.start();
hiveConf = new HiveConf(Consumer.class);
HiveMetaStoreClient msc = new HiveMetaStoreClient(hiveConf);
Table table = msc.getTable("mydb","myTbl");
table.getParameters().put(HCatConstants.HCAT_MSGBUS_TOPIC_NAME, "TOPIC"+ ".HCATALOG");
System.out.println("TABLE = " + table.toString());
Map<String,String> map = table.getParameters();
System.out.println("MAP= " + map.toString());
String fetchTopic = map.get(HCatConstants.HCAT_MSGBUS_TOPIC_NAME);
System.out.println("fetchTopic = " + fetchTopic);
String topicName = msc.getTable("mydb",
"mytbl").getParameters().get(HCatConstants.HCAT_MSGBUS_TOPIC_NAME);
System.out.println("TOPICNAME = " + topicName);
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
if (session == null)
System.out.println("NULL");
System.out.println(session.toString());
Destination hcatTopic = session.createTopic(fetchTopic);
MessageConsumer consumer = session.createConsumer(hcatTopic);
consumer.setMessageListener(this);
}
catch(Exception e){
System.out.println("ERROR");
e.printStackTrace();
}
}
#Override
public void onMessage(Message message){
try
{
if(message.getStringProperty(HCatConstants.HCAT_EVENT).equals(HCatConstants.HCAT_PARTITION_DONE_EVENT)){
MapMessage mapMsg = (MapMessage)message;
Enumeration<String> keys = mapMsg.getMapNames();
while(keys.hasMoreElements())
{
String key = keys.nextElement();
System.out.println(key + " : " + mapMsg.getString(key));
}
System.out.println("Message: "+message);
}
}
catch(Exception e){
System.out.println("ERROR");
}
}
};
class Producer extends Thread
{
private HiveConf hiveConf;
public Producer()
{
}
#Override
public void run() {
try
{
hiveConf = new HiveConf(this.getClass());
HiveMetaStoreClient msc = new HiveMetaStoreClient(hiveConf);
HashMap<String,String> partMap = new HashMap<String, String>();
partMap.put("date","20110711");
partMap.put("date","20110712");
partMap.put("date","20110714");
while(true)
{
msc.markPartitionForEvent("mydb", "mytbl", partMap, PartitionEventType.LOAD_DONE);
Thread.sleep(1000);
}
}
catch(Exception e){
System.out.println("ERROR");
}
}
};
I've played a bit with netty and followed a video(https://www.youtube.com/watch?v=tsz-assb1X8) to build a chat server and client the server works properly(I tested with telnet and here it works) but the client does not recives data. The channelRead method in ChatClinetHandler.java were never called but the channelReadComplete were called.
ChatClient.java
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ChatClient {
public static void main(String args[]) {
try {
new ChatClient(new InetSocketAddress("localhost", 8000)).run();
} catch (Exception ex) {
Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
private final InetSocketAddress server;
public ChatClient(InetSocketAddress server) {
this.server = server;
}
public void run() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChatClientInitializer());
Channel channel = bootstrap.connect(server).sync().channel();
System.out.println("Connected to Server: " + server.toString());
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (channel.isActive()) {
String userMessage = in.readLine();
channel.writeAndFlush(userMessage + "\r\n");
if (userMessage.equalsIgnoreCase("bye")) {
group.shutdownGracefully();
break;
}
}
} finally {
group.shutdownGracefully();
}
}
}
ChatClientInitializer.java
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder;
public class ChatClientInitializer extends ChannelInitializer<SocketChannel> {
#Override
protected void initChannel(SocketChannel c) throws Exception {
ChannelPipeline pipeline = c.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ChatClientHandler());
} }
ChatClinetHandler.java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ChatClientHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg.toString());
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
}
}
Sorry my mistake i forgot to send "\r\n" with each message.
Although shim_ claims to have determined that the problem is not sending "\r\n" with each message, I see the need to provide a clarification here.
In ChatClientInitializer.java you see a handler named framer which is a DelimiterBasedFrameDecoder object with a line delimiter in that specific example (More info on DelimiterBasedFrameDecoder can be found here).
So, that means the client is expecting messages that ends with "\r\n" on that specific channel. If a message not ending with "\r\n" is received, then the channelRead() method will not be provoked.