I have an JavaFx application that has no stage. It only runs at system tray. Basically it listen to a service and show notification according to it.
The connection between app and service is done using Socket.
However, service can send a priority message, which will be shown first than others.
The problem: I have all my messages in a PriorityQueue but I don't know how to handle a notification await for the other finish to show. Is that the best approach? Is the architecture correct? Also, since TrayNotification class will show a Scene, I'm afraid of having problems with UI Thread.
This is Message class:
public class Message implements Comparable<Message> {
private int priority;
private String notificationType;
private String title;
private String message;
public Message() {
}
public Message (int priority, String notificationType, String title, String message) {
this.priority = priority;
this.notificationType = notificationType;
this.title = title;
this.message = message;
}
public void setPriority(int priority) {
this.priority = priority;
}
public int getPriority() {
return this.priority;
}
public void setNotificationType(String notificationType) {
this.notificationType = notificationType;
}
public NotificationType getNotificationType() {
if (this.notificationType.equals(NotificationType.CUSTOM.toString())) {
return NotificationType.CUSTOM;
}
else if (this.notificationType.equals(NotificationType.ERROR.toString())) {
return NotificationType.ERROR;
}
else if (this.notificationType.equals(NotificationType.INFORMATION.toString())) {
return NotificationType.INFORMATION;
}
else if (this.notificationType.equals(NotificationType.NOTICE.toString())) {
return NotificationType.NOTICE;
}
else if (this.notificationType.equals(NotificationType.SUCCESS.toString())) {
return NotificationType.SUCCESS;
}
else if (this.notificationType.equals(NotificationType.WARNING.toString())) {
return NotificationType.WARNING;
}
else {
throw new IllegalArgumentException("Invalid notification type.");
}
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
#Override
public int compareTo(Message otherMessage) {
return Integer.compare(this.priority, otherMessage.getPriority());
}
}
My application class, SystemtrayLauncher, has this code on start method, after configuring tray:
/** Start to listen to service **/
ServiceConnector connector = new ServiceConnector(8888);
new Thread(connector).start();
ServiceConnector (which I think needs to be improved to handle PriorityQueue):
public class ServiceConnector extends Task<Void> {
private ServerSocket socket;
private int port;
public static PriorityQueue<Message> messageQueue = new PriorityQueue<>();
public ServiceConnector(int port) {
this.port = port;
}
public void connect() {
try {
System.out.println("Opening connection...");
socket = new ServerSocket(this.port);
socket.setSoTimeout(0);
System.out.println("Connection opened at port " + this.port);
while (true) {
System.out.println("Awaiting service connection...");
Socket service = socket.accept();
System.out.println(
"Service at " + service.getInetAddress().getHostAddress() + " connected");
Message message = MessageListener.getMessage(service);
if (message != null) {
messageQueue.offer(message);
// get top priority message
Platform.runLater(() -> MessageListener.notifyUser(messageQueue.peek()));
}
else {
CustomAlert dialog = new CustomAlert(Alert.AlertType.ERROR);
dialog.setContentText(SystemConfiguration.LOCALE.getString("MESSAGE_ERROR"));
dialog.showAndWait();
}
service.close();
}
} catch (IOException exc) {
exc.printStackTrace();
}
}
#Override
protected Void call() throws Exception {
this.connect();
return null;
}
}
MessageListener
public class MessageListener {
private static TrayNotification trayNotification;
public static Message getMessage(Socket service) {
System.out.println("Processing message...");
try {
BufferedReader inputReader =
new BufferedReader(new InputStreamReader(service.getInputStream()));
/**
* JSON format:
* {
* "priority": "1 for urgent and greater with less priority",
* "notificationType": "ERROR|INFORMATION|NOTICE|SUCCESS|WARNING",
* "title": "A string to be show as notification windows title",
* "message": "A string to be show as message"
* }
*/
JSONObject jsonMessage = new JSONObject(inputReader.readLine());
Message message = new Message();
message.setPriority(jsonMessage.getInt("priority"));
message.setNotificationType(jsonMessage.getString("notificationType"));
message.setTitle(jsonMessage.getString("title"));
message.setMessage(jsonMessage.getString("message"));
inputReader.close();
service.close();
System.out.println("Message with priority " + message.getPriority() + " processed.");
return message;
} catch (IOException exc) {
exc.printStackTrace();
return null;
}
}
/**
* Notify user with processed service message.
* #param message
*
*/
public static void notifyUser(Message message) {
System.out.println("Priority: " + message.getPriority());
trayNotification = new TrayNotification();
trayNotification.setAnimationType(AnimationType.POPUP);
trayNotification.setRectangleFill(Paint.valueOf("#0277BD"));
trayNotification.setImage(new Image(SystemConfiguration.ICON));
trayNotification.setNotificationType(message.getNotificationType());
trayNotification.setTitle(message.getTitle());
trayNotification.setMessage(message.getMessage());
trayNotification.showAndDismiss(Duration.seconds(3.5));
ServiceConnector.messageQueue.poll();
}
}
Related
I'm trying to implement a fake broker (actually it is an mqtt publisher client in an mqtt subscriber's callback). There are 3 separated publisher clients which are publishing random numbers between 0 and 1. This fake broker just summarizes this random numbers, and publishes away to an other topic. (Maybe not in the right way, but for now it is ok) This solution is working but after a few incoming messages this broker stops to work. I Tried to debug it, but I found only ClassNotFound Exceptions... Here is my FakeBroker and it's Callback implementation.
public class FakeBroker implements Runnable{
public static final String BROKER_URL = "";
public static final String TOPIC_FAKE_A = "";
public static final String TOPIC_FAKE_B = "";
public static final String TOPIC_FAKE_C = "";
public static final String USER_NAME = "";
public static final char[] USER_PSW = "".toCharArray();
private MqttClient client;
private MqttConnectOptions options;
private SubscriberCallback callback;
public FakeBroker() {
options = new MqttConnectOptions();
options.setUserName(USER_NAME);
options.setPassword(USER_PSW);
options.setCleanSession(false);
callback = new SubscriberCallback();
try {
client = new MqttClient(BROKER_URL, MqttClient.generateClientId()+"-sub");
client.setCallback(callback);
} catch (MqttException e) {
e.printStackTrace();
System.exit(1);
}
}
public void start() {
try {
client.connect(options);
System.out.println("Fake Broker are connected to the cloud.");
client.subscribe(TOPIC_FAKE_A);
client.subscribe(TOPIC_FAKE_B);
client.subscribe(TOPIC_FAKE_C);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void run() {
start();
}
}
And here is it's Callback
public class SubscriberCallback implements MqttCallback {
public static final String BROKER_URL = "";
public static final String TOPIC_FAKE_BROKER = "";
public static final String USER_NAME = "";
public static final char[] USER_PSW = "".toCharArray();
private MqttClient client;
private MqttConnectOptions options;
private int counter = 1;
private int result = 0;
public SubscriberCallback() {
try {
client = new MqttClient(BROKER_URL, "4-pub");
options = new MqttConnectOptions();
options.setPassword(USER_PSW);
options.setUserName(USER_NAME);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void connectionLost(Throwable throwable) {
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
System.out.println("Message Arrived. Topic " + topic + " message: " +mqttMessage + " ---- Message Counter: " + counter);
int number = Integer.parseInt(mqttMessage.toString());
result += number;
if (counter%3 == 0) {
publishAway(new MqttMessage(Integer.toString(result).getBytes()));
result = 0;
}
incrementCounter();
}
private void publishAway(MqttMessage mqttMessage) throws MqttException {
client.connect(options);
final MqttTopic topicFakeBroker = client.getTopic(TOPIC_FAKE_BROKER);
topicFakeBroker.publish(mqttMessage);
client.disconnect();
System.out.println("Fake broker got the message " + mqttMessage + " and published away to" + topicFakeBroker.getName());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
private void incrementCounter() {
counter++;
}
}
Of course I use valid BROKER_URL and TOPICS but these informations are confidential. Thanks for Your answers! :)
I am trying to communicate with an external TCP server using TcpOutboundGateway and a client TcpConnectionFactory. In my scenario, each connection should be associated with different thread (each connection on the thread might be used for more then one request/response).
So I used a ThreadAffinityClientConnectionFactory from this topic: Spring Integration tcp client multiple connections
It worked fine until I tried to open more than 4 concurrent connections, the fifth (and over) connection is failing on timeout.
I figured out that org.springframework.integration.ip.tcp.TcpOutboundGateway uses semaphore in handleRequestMessage method to acquire a connection, so I overridden TcpOuboundGateway like this:
public class NoSemaphoreTcpOutboundGateway extends TcpOutboundGateway {
private volatile AbstractClientConnectionFactory connectionFactory;
private final Map<String, NoSemaphoreTcpOutboundGateway.AsyncReply> pendingReplies = new ConcurrentHashMap();
#Override
public boolean onMessage(Message<?> message) {
String connectionId = (String)message.getHeaders().get("ip_connectionId");
if(connectionId == null) {
this.logger.error("Cannot correlate response - no connection id");
this.publishNoConnectionEvent(message, (String)null, "Cannot correlate response - no connection id");
return false;
}
if(this.logger.isTraceEnabled()) {
this.logger.trace("onMessage: " + connectionId + "(" + message + ")");
}
NoSemaphoreTcpOutboundGateway.AsyncReply reply = (NoSemaphoreTcpOutboundGateway.AsyncReply)this.pendingReplies.get(connectionId);
if(reply == null) {
if(message instanceof ErrorMessage) {
return false;
} else {
String errorMessage = "Cannot correlate response - no pending reply for " + connectionId;
this.logger.error(errorMessage);
this.publishNoConnectionEvent(message, connectionId, errorMessage);
return false;
}
} else {
reply.setReply(message);
return false;
}
}
#Override
protected Message handleRequestMessage(Message<?> requestMessage) {
connectionFactory = (AbstractClientConnectionFactory) this.getConnectionFactory();
Assert.notNull(this.getConnectionFactory(), this.getClass().getName() + " requires a client connection factory");
TcpConnection connection = null;
String connectionId = null;
Message var7;
try {
/*if(!this.isSingleUse()) {
this.logger.debug("trying semaphore");
if(!this.semaphore.tryAcquire(this.requestTimeout, TimeUnit.MILLISECONDS)) {
throw new MessageTimeoutException(requestMessage, "Timed out waiting for connection");
}
haveSemaphore = true;
if(this.logger.isDebugEnabled()) {
this.logger.debug("got semaphore");
}
}*/
connection = this.getConnectionFactory().getConnection();
NoSemaphoreTcpOutboundGateway.AsyncReply e = new NoSemaphoreTcpOutboundGateway.AsyncReply(10000);
connectionId = connection.getConnectionId();
this.pendingReplies.put(connectionId, e);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Added pending reply " + connectionId);
}
connection.send(requestMessage);
//connection may be closed after send (in interceptor) if its disconnect message
if (!connection.isOpen())
return null;
Message replyMessage = e.getReply();
if(replyMessage == null) {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Remote Timeout on " + connectionId);
}
this.connectionFactory.forceClose(connection);
throw new MessageTimeoutException(requestMessage, "Timed out waiting for response");
}
if(this.logger.isDebugEnabled()) {
this.logger.debug("Response " + replyMessage);
}
var7 = replyMessage;
} catch (Exception var11) {
this.logger.error("Tcp Gateway exception", var11);
if(var11 instanceof MessagingException) {
throw (MessagingException)var11;
}
throw new MessagingException("Failed to send or receive", var11);
} finally {
if(connectionId != null) {
this.pendingReplies.remove(connectionId);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Removed pending reply " + connectionId);
}
}
}
return var7;
}
private void publishNoConnectionEvent(Message<?> message, String connectionId, String errorMessage) {
ApplicationEventPublisher applicationEventPublisher = this.connectionFactory.getApplicationEventPublisher();
if(applicationEventPublisher != null) {
applicationEventPublisher.publishEvent(new TcpConnectionFailedCorrelationEvent(this, connectionId, new MessagingException(message, errorMessage)));
}
}
private final class AsyncReply {
private final CountDownLatch latch;
private final CountDownLatch secondChanceLatch;
private final long remoteTimeout;
private volatile Message<?> reply;
private AsyncReply(long remoteTimeout) {
this.latch = new CountDownLatch(1);
this.secondChanceLatch = new CountDownLatch(1);
this.remoteTimeout = remoteTimeout;
}
public Message<?> getReply() throws Exception {
try {
if(!this.latch.await(this.remoteTimeout, TimeUnit.MILLISECONDS)) {
return null;
}
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
for(boolean waitForMessageAfterError = true; this.reply instanceof ErrorMessage; waitForMessageAfterError = false) {
if(!waitForMessageAfterError) {
if(this.reply.getPayload() instanceof MessagingException) {
throw (MessagingException)this.reply.getPayload();
}
throw new MessagingException("Exception while awaiting reply", (Throwable)this.reply.getPayload());
}
NoSemaphoreTcpOutboundGateway.this.logger.debug("second chance");
this.secondChanceLatch.await(2L, TimeUnit.SECONDS);
}
return this.reply;
}
public void setReply(Message<?> reply) {
if(this.reply == null) {
this.reply = reply;
this.latch.countDown();
} else if(this.reply instanceof ErrorMessage) {
this.reply = reply;
this.secondChanceLatch.countDown();
}
}
}
}
the configurations of SpringContext looks like this:
#Configuration
#ImportResource("gateway.xml")
public class Conf {
#Bean
#Autowired
#ServiceActivator(inputChannel = "clientOutChannel")
public NoSemaphoreTcpOutboundGateway noSemaphoreTcpOutboundGateway(ThreadAffinityClientConnectionFactory cf, DirectChannel clientInChannel){
NoSemaphoreTcpOutboundGateway gw = new NoSemaphoreTcpOutboundGateway();
gw.setConnectionFactory(cf);
gw.setReplyChannel(clientInChannel);
gw.setRequestTimeout(10000);
return gw;
}
<int-ip:tcp-connection-factory
id="delegateCF"
type="client"
host="${remoteService.host}"
port="${remoteService.port}"
single-use="true"
lookup-host="false"
ssl-context-support="sslContext"
deserializer="clientDeserializer"
serializer="clientSerializer"
interceptor-factory-chain="clientLoggingTcpConnectionInterceptorFactory"
using-nio="false"/>
The delegateCF is passed to ThreadAffinityClientConnectionFactory constructor
So, the question is:
Is it OK to use NoSemaphoreTcpOutboundGateway in conjunction with ThreadAffinityClientConnectionFactory in terms of concurrency?
Looks like you go right way, but at the same time I think you don't need custom TcpOutboundGateway. The semaphore logic is based on the:
if (!this.isSingleUse) {
logger.debug("trying semaphore");
if (!this.semaphore.tryAcquire(this.requestTimeout, TimeUnit.MILLISECONDS)) {
throw new MessageTimeoutException(requestMessage, "Timed out waiting for connection");
}
at the same time look at Gary's solution for the ThreadAffinityClientConnectionFactory:
#Bean
public TcpNetClientConnectionFactory delegateCF() {
TcpNetClientConnectionFactory clientCF = new TcpNetClientConnectionFactory("localhost", 1234);
clientCF.setSingleUse(true); // so each thread gets his own connection
return clientCF;
}
#Bean
public ThreadAffinityClientConnectionFactory affinityCF() {
return new ThreadAffinityClientConnectionFactory(delegateCF());
}
Pay attention to the comment. Only you need is delegate isSingleUse().
I have the problem regarding the implementation of One Publisher - Multiple Subscribers pattern. The Publisher uses the fixed-size buffer and queue the messages. The messages are send to all subscribers. The ordering of messages get by subscribers must be the same as the ordering of publishing messages.
I use BlockingQueue to hold publisher messages (publisherQueue) and pass them to each subscriber BlockingQueue (subscriberQueue).
The issue is that the buffer and subscribers are working correctly, but the buffer size (publisherQueue.size()) always returns 1.
System.out.println("Actual number of messages in buffer: " + publisherQueue.size());
Here is my full code:
PublisherSubscriberService.java
package program;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class PublisherSubscriberService {
private int buffer;
private int subscribersNumber;
static Set<subscriber> subscribers = new HashSet<subscriber>();
public PublisherSubscriberService(int buffer, int subscribersNumber) {
this.buffer = buffer;
this.subscribersNumber = subscribersNumber;
}
public void addsubscriber(subscriber subscriber) {
subscribers.add(subscriber);
}
public void start() {
publisher publisher = new publisher(buffer);
System.out.println("publisher started the job");
for (int i = 0; i < subscribersNumber; i++) {
subscriber subscriber = new subscriber(buffer);
subscriber.setName(Integer.toString(i + 1));
subscribers.add(subscriber);
new Thread(subscriber).start();
System.out.println("Subscriber " + subscriber.getName() + " started the job");
}
new Thread(publisher).start();
}
public class Publisher implements Runnable {
private int buffer;
final BlockingQueue<Message> publisherQueue;
public Publisher(int buffer) {
this.buffer = buffer;
publisherQueue = new LinkedBlockingQueue<>(buffer);
}
#Override
public void run() {
for (int i = 1; i < 100; i++) {
Message messageObject = new Message("" + i);
try {
Thread.sleep(50);
publisherQueue.put(messageObject);
System.out.println("Queued message no " + messageObject.getMessage());
System.out.println("Actual number of messages in buffer: " + publisherQueue.size());
for (subscriber subscriber : subscribers) {
subscriber.subscriberQueue.put(messageObject);
}
publisherQueue.take();
} catch (InterruptedException e) {
System.out.println("Some error");
e.printStackTrace();
}
}
}
}
class Subscriber implements Runnable {
private String name;
private int buffer;
final BlockingQueue<Message> subscriberQueue;
public Subscriber(int buffer) {
this.buffer = buffer;
subscriberQueue = new LinkedBlockingQueue<>(buffer);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public void run() {
try {
Message messageObject;
while (true) {
Thread.sleep(100);
messageObject = subscriberQueue.take();
System.out.println(this.getName() + " got message: " + messageObject.getMessage());
}
} catch (InterruptedException e) {
System.out.println("Some error");
e.printStackTrace();
}
}
}
class Message {
private String message;
public Message(String str) {
this.message = str;
}
public String getMessage() {
return message;
}
}
}
PublisherSubscriberProgram.java
package program;
public class ProducerConsumerProgram {
public static void main(String[] args) {
ProducerConsumerService service = new ProducerConsumerService(10, 3);
service.start();
}
}
Your publisher never has more than 1 item in the queue. Each time through your loop you put and take a single item:
**publisherQueue.put(messageObject);**
System.out.println("Queued message no " + messageObject.getMessage());
System.out.println("Actual number of messages in buffer: " + publisherQueue.size());
for (subscriber subscriber : subscribers) {
subscriber.subscriberQueue.put(messageObject);
}
**publisherQueue.take();**
With the code you have provided, there is point in even having the publisher queue.
According to wikipedia: "The race conditions that cause spurious wakeups should be considered rare".
But when I run this code, it is showing me that spurious wakeup happens quite often.
Is this actually spurious wakeup or there's just a sneaky race condition in my code?
import java.util.Random;
public class Main {
public static void main(String[] args) throws Exception {
// Message message = new SafeMessage();
Message message = new SpuriousMessage();
String[] producerNames = { "p01", "p02", "p03", "p04", "p05", "p06", "p07", "p08", "p09" };
for (String producerName : producerNames) {
Producer producer = new Producer(producerName, message);
new Thread(producer).start();
}
String[] consumerNames = { "c-01", "c-02", "c-03", "c-04" };
for (String consumerName : consumerNames) {
Consumer consumer = new Consumer(consumerName, message);
new Thread(consumer).start();
}
}
}
abstract class Message {
protected String message;
protected boolean empty = true;
public abstract String getMessage() throws InterruptedException;
public abstract void setMessage(String message) throws InterruptedException;
protected static String avoidNull(String obj) {
return obj != null ? obj : "Default message";
}
}
class SpuriousMessage extends Message {
#Override
public synchronized String getMessage() throws InterruptedException {
wait();
empty = true;
String temp = message;
message = "---------------------------------------- Spurious wakeup";
return temp;
}
#Override
public synchronized void setMessage(String message) throws InterruptedException {
this.message = avoidNull(message);
this.empty = false;
notifyAll();
}
}
class SafeMessage extends Message {
#Override
public synchronized String getMessage() throws InterruptedException {
while (empty) {
wait();
}
empty = true;
notifyAll();
String temp = message;
message = "---------------------------------------- Spurious wakeup";
return temp;
}
#Override
public synchronized void setMessage(String message) throws InterruptedException {
while (!empty) {
wait();
}
this.message = avoidNull(message);
this.empty = false;
notifyAll();
}
}
class Producer implements Runnable {
private static final Random RANDOM = new Random();
private String producerName = "Default";
private Message message;
public Producer(String producerName, Message message) {
this.producerName = producerName;
this.message = message;
}
#Override
public void run() {
while (true) {
try {
message.setMessage(producerName + " :: " + randomMessage());
rest(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static String randomMessage() {
final String[] messageArray = { "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
"Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar",
"Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
"Xray", "Yankee", "Zulu" };
return messageArray[RANDOM.nextInt(messageArray.length)];
}
private void rest(long millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private final long TIMEOUT = 5;
private String consumerName = "Default";
private Message message;
public Consumer(String consumerName, Message message) {
this.consumerName = consumerName;
this.message = message;
}
#Override
public void run() {
while (true) {
try {
System.out.println(consumerName + " :: " + message.getMessage());
rest(TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void rest(long millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace();
}
}
}
When a spurious wakeup happens, wait() exits although notify/notifyAll has not been called. In your case you call notifyAll from the producer so it is normal that wait exits...
To observe a spurious wakeup, you would need to run your Consumers only. If they print the "spurious wakeup" message then that will be a real spurious wakeup because it won't be caused by notify/All any more. However it may never happen.
See also: Do spurious wakeups actually happen?.
As part of my lab this week I am suppose to convert a socket based chat application to RMI. So far I managed to connect server and client together and transfer data between them but the transfer is not continuous. What I mean is that when the client first connects t the server it broadcasts a message "X has entered the conversation" but that is all. Anything I type after that wont get broadcasted. I am about to pull out my hair. Please help.
public class ChatServer extends UnicastRemoteObject implements ChatMessage {
private static final long serialVersionUID = 1L;
private String sender;
private String message;
private ChatMessageType t;
public ChatServer() throws RemoteException {
super();
}
#Override
public void Message(String sender, ChatMessageType t, String message)
throws RemoteException {
this.sender = sender;
this.message = message;
this.t = t;
}
#Override
public String getSender() throws RemoteException {
return sender;
}
#Override
public String getMessage() throws RemoteException {
return message;
}
#Override
public ChatMessageType getType() throws RemoteException {
return t;
}
public String ToString() throws RemoteException{
String strMessage;
switch (t) {
case SETUP:
strMessage = sender + " has entered the conversation.";
break;
case TEARDOWN:
strMessage = sender + " has left the conversation.";
break;
case MESSAGE:
strMessage = sender + ": " + message;
break;
default:
strMessage = "";
}
return strMessage;
}
// driver.
public static void main(String arg[]) {
try {
ChatServer c = new ChatServer();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("Server", c);
System.out.println("Server bound in registry");
} catch (Exception e) {
System.out.println("Server error: " + e.getMessage());
e.printStackTrace();
}
}
}
public class ChatClient implements ActionListener {
// static private Socket c;
static ChatMessage obj = null;
// static private ObjectInputStream in;
// static private ObjectOutputStream out;
static private String name;
static private String host;
static private Integer port;
/**
* Launches this application
*/
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (args.length != 3) {
System.out
.println("Client requires exactly three args to run.");
System.exit(-1);
}
name = args[0];
host = args[1];
port = new Integer(args[2]);
final ChatClient application = new ChatClient();
application.getJFrame().setVisible(true);
try {
System.out.println("client: connecting to server...");
// c = new Socket(host, port);
obj = (ChatMessage) Naming.lookup("//" + host + ":" + port
+ "/Server");
System.out.println("client: connected!");
} catch (Exception e) {
System.out.println("client: " + e.getMessage());
System.exit(-1);
}
try {
// out = new ObjectOutputStream(c.getOutputStream());
// in = new ObjectInputStream(c.getInputStream());
// announce to other clients that you're here
// out.writeObject(new ChatMessage(name,
// ChatMessageType.SETUP, ""));
obj.Message(name, ChatMessageType.SETUP, "");
} catch (Exception e) {
}
// set up the client's listener as an anonymous thread that's
// always running
// new Thread(new Runnable(){
// public void run()
// {
// while(true)
// {
try {
System.out.println(name + ": waiting for data");
ChatMessage m = (ChatMessage) Naming.lookup("//" + host
+ ":" + port + "/Server");
System.out.println(name + ": data received");
application.updateTextArea(m.ToString());
} catch (Exception e) {
}
// }
// }
// }).start();
}
});
}
public void updateTextArea(final String message) {
conversation.setText(conversation.getText() + message + "\n");
// this will guarantee that the bottom of the conversation is visible.
conversation.setCaretPosition(conversation.getText().length());
}
// send button has been pressed, send the message to the server.
public void actionPerformed(ActionEvent e) {
if (send.getText().equals("Send")) {
try {
System.out.println(name + ": sending data");
// ChatMessage m = new ChatMessage(name,
// ChatMessageType.MESSAGE, message.getText());
// out.writeObject(m);
obj.Message(name, ChatMessageType.MESSAGE, message.getText());
message.setText(""); // clear the text box.
System.out.println(name + ": data sent");
} catch (Exception ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
}
}
enum ChatMessageType{
SETUP,
MESSAGE,
TEARDOWN
}public interface ChatMessage extends Remote{
public String getSender() throws RemoteException;
public String getMessage() throws RemoteException;
public ChatMessageType getType() throws RemoteException;
public void Message(String sender, ChatMessageType t, String message) throws RemoteException;
public String ToString() throws RemoteException;
I realize this question is pretty old and you probably figured out an answer for this, but, I thought I'd share an approach I took for going from Java sockets to RMI. Maybe it is useful for others looking to do the same thing.
I basically abstracted out the socket portion into a "Tunnel" object that represents a communication path between hosts. And the tunnel consists of several "channels", that represent a one-way communication between the source and destination.
You can check out more details at my blog here: http://www.thecodespot.com/?p=1