Sending messages to specific Smack domains after initial broadcast message - java

I'm creating an instant messaging client using Smack 3.1.0 and Java. The problem I'm running in to has to do with sending messages to the user on a specific domain.
For example, I have two users, 1#gmail.com and 2#gmail.com. 1#gmail.com logs in to XMPP through my IM client. 2#gmail.com logs in to GChat through gmail.com AND a second time through pidgin. So now I have one instance of 1#gmail.com and 2 instances of 2#gmail.com.
The way gmail works, if 1#gmail.com sends a message to 2#gmail.com, the gmail and the pidgin client both get the initial message. But then if the gmail instance responds to the message, every message from then on only goes between 1#gmail.com and the gmail instance of 2#gmail.com.
I would like to mimic this behavior with my IM client. I would think the way to do it would be to set up a Chat, send the initial IM to all instances of the recipient. Then I'd set up a MessageListener to listen for a response. When I get the response, I'd have to create a new chat, specifying the 2#gmail.com/resource. But then I'd have to write the MessageListener twice. Any ideas? Here's some sample code that I'm using (the method AddText() simply appends the message to my conversation pane):
recipient = buddy;
setTitle("Instant Message - "+recipient);
chat = com.andreaslekas.pim.PIM.connection.getChatManager().createChat(recipient.getUser(), new MessageListener() {
public void processMessage(Chat chat, Message msg) {
//if(chat.getParticipant().indexOf('/')!=-1)
addText(msg.getBody(), chat.getParticipant(), true);
}
});
UPDATE
I wanted to supplement the answer below with actual code that I used to make this work:
chat = com.andreaslekas.pim.PIM.connection.getChatManager().createChat(recipient.getUser(), new MessageListener() {
public void processMessage(Chat new_chat, Message msg) {
if(msg.getFrom().replaceFirst("/.*", "").equals(recipient.getUser()))
{
if(buddy_resource==null || !msg.getFrom().replaceFirst(".*?/", "").equals(buddy_resource.getResource()))
{
buddy_resource = recipient.getResource(msg.getFrom().replaceFirst(".*?/", ""));
chat = null;
chat = com.andreaslekas.pim.PIM.connection.getChatManager().createChat(recipient.getUser()+"/"+buddy_resource.getResource(), new MessageListener(){
public void processMessage(Chat new_chat2, Message msg) {
addText(msg.getBody(), new_chat2.getParticipant(), true);
}
});
}
addText(msg.getBody(), chat.getParticipant(), true);
}
}
});
To summarize, I send the first message to all resources of the recipient's address and wait for a response. When I get the response, I replace the current Chat object with a new one that specifies the individual resource that responded to the initial message. The code is a little messy with two different MessageListener objects that could probably be combined into a new class. But it works.

So far I understood Message Carbon (XEP - 0280) will solve your problem.
If you enable carbon it will distribute messages to all logged resources of a user. In your case if 1#gmail.com send message to 2#gmail.com it will be distributed to all logged resources of 2#gmail.com.
Here's a code sample using smack,
CarbonManager cm = CarbonManager.getInstanceFor(connection);
cm.enableCarbons();
cm.sendCarbonsEnabled();
First make sure that your server is supported Message Carbon.
Then send message as usual.

In your MessageListener why not always respond to the sender? I think you get it by calling something like msg.getSender() or getFrom() (I'm on mobile right now, cannot check)

Related

unable to de-queue previous messages from a queue

I am creating an chat app with android and spring using stomp over websocket. when i send more than one message to a specific client, it shows only the last message from the queue not all the previous messages. I want to see all the messages will from the queue.
Client side:
mStompClient =Stomp.over(WebSocket.class, "url");
mStompClient.connect();
mStompClient.topic("/topic/queue"+clientid).subscribe(topicMessage -> {
textView.setText(topicMessage.getPayload());
});
Server side:
#MessageMapping("/chat")
#SendTo("/topic/queue")
public String sendMessage(String msg) {
simpMessagingTemplate.convertAndSend("/topic/queue"+clientid,msg );
return "";
}
In my vision, you should persist the data which sent before, then append the new incoming messages from the queue and feed data store asynchronously.
Step: User opens chat windows, then get data from database.
Step: Send message to queue and consumers should listen the queue and feed new message to chat window and data store.
Step: If user exit from application, No matter what happened. You can present data from data store.

Correct handling of snmp INFORM by receiver

I have some strage behaviour of SNMP4J which I can't explain. It is likely is due to my misunderstanding of snmp basics.
I deal with server which receives snmp traps. Everything is going on well. Now I need to change traps to inform.
The issue is my server is trying to handle received message twice. I occured with duplicating of INFORM message.
Trying to explain... I've enabled logger to DEBUG level.
When INFORM is arrived I see in log file two same records from class org.snmp4j.transport.DefaultUdpTransportMapping.ListenThread which has following code in run() method:
if (logger.isDebugEnabled()) {
logger.debug("Received message from "+packet.getAddress()+"/"+
packet.getPort()+
" with length "+packet.getLength()+": "+
new OctetString(packet.getData(), 0,
packet.getLength()).toHexString());
}
Also I have class which implements interface org.snmp4j.CommandResponder. His method void processPdu(CommandResponderEvent event) is invoked twice for inform and once for a trap.
#Override
public void processPdu(final CommandResponderEvent evt) {
final Address address = getAgentAddress(evt);
final PDU command = evt.getPDU();
boolean isInform = command.getType() == PDU.INFORM // this is true for both invocations of this method while receiving INFORM
}
Details about versions:
snmp v2, snmp4j version 2.3.0
Help me to realize: Does exist some bug here of I should filter second invocation of processPdu method by command.getRequestID(), for example?
The INFORM must be acknowledged by Receiver.
I believe the problem is that the sender have not received the RESPONSE PDU back for this INFORM REQUEST for some reason. So it sends it once again according to the number of retries. Make sure you send RESPONSE PDU to the sender to confirm that you've received REQUEST PDU (INFORM). The API should do it for you. So make sure you call the default handler.

How to design push notifications using Atmosphere

I want to use atmosphere to develop a notification System.
I am very new to Atmosphere so apologies if I am wrong somewhere.
What i understood is when a Actor publishes something I save the notification action to the database.
What i don't understand how the receiver will receive those notifications in realtime.
The sender i know will do something like following
event.getBroadcaster().broadcast(
objectMapper.writeValueAsString("Some Message"));
Now i am not able to figure out how the receiver can receive this message.
For example . I want to add a User Object as Friend. So when User1 adds User2 User1 broadcast but than how i push the notification to User2. I have difficulty in understanding this.
Technically i want something similar like facebook or gmail notification where on user activity other users get notifications.
Basically what you need is to implement Publish-subscribe on top of Atmosphere.
Atmosphere consists of two parts: client-side (javascript-based) and server-side(java-based).
First of all you need to configure server-side: Installing Atmosphere
Namely servlet or filter, it is required so that it could add AtmosphereResource to the HttpServletRequest.
AtmosphereResource represents a single client connection on the server-side.
Broadcaster is actually a container for these resources, so that you don't need to handle lookup/iteration/concurrency when you need to send to multiple connections. (Note that multiple connections can be produced by single client).
On the server-side you need to provide clients an endpoint to subscribe for notifications.
For example, if you are using Spring-MVC, it could go like this (omitting validations/authentications, etc.):
#RequestMapping(value = "/user-notifications/{userId}")
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public void watch(#PathVariable("userId") String userId,
HttpServletRequest request) throws Exception {
//Atmosphere framework puts filter/servlet that adds ATMOSPHERE_RESOURCE to all requests
AtmosphereResource resource = (AtmosphereResource)request.getAttribute(ApplicationConfig.ATMOSPHERE_RESOURCE);
//suspending resource to keep connection
resource.suspend();
//find broadcaster, second parameter says to create broadcaster if it doesn't exist
Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(userId,true);
//saving resource for notifications
broadcaster.addAtmosphereResource(resource);
}
When something happens you can notify clients like this:
public void notify(User user, Event event){
Broadcaster b = BroadcasterFactory.getDefault().lookup(user.getId());
if (b!=null){
b.broadcast(event);
}
}
On the client side you need to send a subscribe request and listen for subsequent events, like this:
var request = new atmosphere.AtmosphereRequest();
request.url = '/user-notifications/'+userId;
request.transport = 'websocket';
request.fallbackTransport = 'streaming';
request.contentType = 'application/json';
request.reconnectInterval = 60000;
request.maxReconnectOnClose = 1000;
request.onMessage = function(response){
console.log(response);
alert('something happend<br>'+response);
};
that.watcherSocket = atmosphere.subscribe(request);
So, to sum it up:
Client sends request "I want to receive this kind of notifications".
Server receives request, suspends and saves connection somewhere (either in your code or in Broadcaster).
When something happens server looks for suspended connection and sends notification in it.
Client receives notification and callback is invoked.
Profit!!!
This wiki has explanations for some concepts behind Atmosphere and links to other documentation.

Smack chats creating two threads

I'm having trouble establishing a proper chat in the Smack messaging library for Java. It works just fine if the chat was started locally, but if the chat is started from another client then two different chat threads are created, instead of just the one that is needed. My code for sending a message is below:
public void sendMessage(String input) throws XMPPException
{
Chat chat = connection.getChatManager().getThreadChat("test#server");
if(chat != null)
{
System.out.println("Chat exists!");
chat.addMessageListener(messageListener);
}
else
{
System.out.println("Create new chat");
chat = connection.getChatManager().createChat("test#server", "test#server", messageListener);
System.out.println(chat.getThreadID());
}
chat.sendMessage(input);
}
Below I have my listener class which processes incoming messages. It is originally set up when the program is initialized so that messages can be recieved straight after log in, and chats established. It is also called by the messageListener variable in the sendMessage function you can see above.
class MyMessageListener implements MessageListener {
#Override
public void processMessage(Chat chat, Message message) {
String from = message.getFrom();
String body = message.getBody();
System.out.println(chat.getThreadID());
System.out.println(String.format("Received message '%1$s' from %2$s", body, from));
}
}
I'm very new to the Smack library and finding the available documentation and examples a bit vague. Anyone have any pointers as to how I could check if a chat was created on another client and somehow fetch the thread ID of this chat or find a chat object from the ChatManager by knowing the name of the user that sent the message?
You need to register a ChatManagerListener to listen for incoming chats, as described in the Incoming Chats section in documentation. A listener on a chat created this way will receive the incoming messages, assuming they are responding with the same thread id (not all clients use a thread id).
By the way, you are looking up an existing chat by thread, but that is not a the thread id of an incoming chat. The code snippet you have shown will only match on the chat that you have created yourself, so there is no point in setting the message listener every time you are going to send a message, you may as well just hold a reference to it once it is create.
You will have to retrieve the thread id from the incoming chat to have this work properly, and that is assuming that the incoming chat actually has a chat id.

How to initiate chatting between two clients and two clients only, using applets and servlets?

I first need to apologize for my earlier questions. (You can check my profile for them)They seemed to ask more questions than give answers. Hence, I am laying down the actual question that started all them absurd questions.
I am trying to design a chat applet. Till now, I have coded the applet, servlet and communication between the applet and the servlet. The code in the servlet side is such that I was able to establish chatting between clients using the applets, but the code was more like a broadcast all feature, i.e. all clients would be chatting with each other. That was my first objective when I started designing the chat applet. The second step is chatting between only two specific users, much like any other chat application we have. So this was my idea for it:
I create an instance of the servlet that has the 'broadcast-all' code.
I then pass the address of this instance to the respective clients.
2 client applets use the address to then chat. Technically the code is 'broadcast-all', but since only 2 clients are connected to it, it gives the chatting between two clients feature. Thus, groups of 2 clients have different instances of the same servlet, and each instance handles chatting between two clients at a max.
However, as predicted, the idea didn't materialize!
I tried to create an instance of the servlet but the only solution for that was using sessions on the servlet side, and I don't know how to use this session for later communications.
I now know how to use the request.getSession(). So I set the session for an applet in its param tag and use it for further communications with the servlet. But how do I use this data to establish chatting between two clients? As I wrote earlier, I have the code for broadcast_all chatting as follows:
public class CustomerServlet extends HttpServlet {
public String getNextMessage() {
// Create a message sink to wait for a new message from the
// message source.
return new MessageSink().getNextMessage(source);
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ObjectOutputStream dout = new ObjectOutputStream(response.getOutputStream());
String recMSG = getNextMessage();
dout.writeObject(recMSG);
dout.flush();
}
public void broadcastMessage(String message) {
// Send the message to all the HTTP-connected clients by giving the
// message to the message source
source.sendMessage(message);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
ObjectInputStream din= new ObjectInputStream(request.getInputStream());
String message = (String)din.readObject();
ObjectOutputStream dout = new ObjectOutputStream(response.getOutputStream());
dout.writeObject("1");
dout.flush();
if (message != null) {
broadcastMessage(message);
}
// Set the status code to indicate there will be no response
response.setStatus(response.SC_NO_CONTENT);
} catch (Exception e) {
e.printStackTrace();
}
}
MessageSource source = new MessageSource();
}
class MessageSource extends Observable {
public void sendMessage(String message) {
setChanged();
notifyObservers(message);
}
}
class MessageSink implements Observer {
String message = null; // set by update() and read by getNextMessage()
// Called by the message source when it gets a new message
synchronized public void update(Observable o, Object arg) {
// Get the new message
message = (String)arg;
// Wake up our waiting thread
notify();
}
// Gets the next message sent out from the message source
synchronized public String getNextMessage(MessageSource source) {
// Tell source we want to be told about new messages
source.addObserver(this);
// Wait until our update() method receives a message
while (message == null) {
try {
wait();
} catch (Exception e) {
System.out.println("Exception has occured! ERR ERR ERR");
}
}
// Tell source to stop telling us about new messages
source.deleteObserver(this);
// Now return the message we received
// But first set the message instance variable to null
// so update() and getNextMessage() can be called again.
String messageCopy = message;
message = null;
return messageCopy;
}
}
On the applet side, I have a thread that will connect to the servlet above using GET method to get new messages. It uses a while loop, and blocks until it gets a message from the servlet. The main thread communicates with the servlet using POST method whenever the client has entered the message. Currently all clients chat with everyone. I want to use the same methods used above (or if possible any other way) to establish chatting between two clients and two clients only. I could possibly have another thread in the applet to check if any other user wishes to chat with it and then exchange some data so that only those two user chat...
I then tried to modify my broadcast-all code. In that code, I was using classes that implemented Observer and Observable interfaces. So the next idea that I got was:
Create a new object of the Observable class(say class_1). This object be common to 2 clients.
2 clients that wish to chat will use same object of the class_1.
2 other clients will use a different object of class_1.
But the problem here lies with the class that implements the Observer interface(say class_2). Since this has observers monitoring the same type of class, namely class_1, how do I establish an observer monitoring one object of class_1 and another observer monitoring another object of the same class class_1 (Because notifyObservers() would notify all the observers and I can't assign a particular observer to a particular object)?
I first decided to ask individual problems, like how to create instances of servlets, using objects of observable and observer and so on in stackoverflow... but I got confused even more. Can anyone give me an idea how to establish chatting between two clients only?(I am using Http and not sockets or RMI).
Regards,
Mithun.
P.S. Thanks to all who replied to my previous (absurd) queries. I should have stated the purpose earlier so that you guys could help me better.
You need to store all connected users in a Map<String, User> in the application scope using ServletContext#setAttribute(). The String denotes the unique user identifier (chat nickname?). You need to store the specific chat User as well in the session scope using HttpSession#setAttribute(). You also need to store the other user in individual chats in a Map<String, User> in the session scope of the users in question. You can obtain the attribute by the getAttribute() method.
This way you know which users are all available and which user is in the current session and with which users it is individually chatting.
This is a crude way to do it, but I just couldn't find a feasible solution. What I did was that I made all users connect to the servlet that had the broadcastAll code. Each user would be aware of which other user it is chatting with. Hence, while sending a message, the user would append his name and the name of the user that he is chatting with to the message. Since it is a broadcastAll code, every connected user would receive the message. After receiving the message, the user would parse the message to get the user who sent the message, and the name of the user for whom the message was intended. It would compare these two names with its records - see the statement in bold earlier. If matched it would display the message, else ignore it.
Again, its a crude way to do it and I am sure there are better solution out there.

Categories