Few questions around getting Chat working with Smack (3.2.1)/Openfire(3.7.1Alpha).
I am currently testing it using a unit test. My unit test creates a connection, creates account, logs in, adds a new user to its roster, attempts to send a chat message to the new user and eventually deletes the users. Apart from my confusions around getting chat to work, others seem to work (verified using openfire admin dashboard).
A. When I do the following
public void sendChatMessage(String sender, String receiver, String message) {
Chat chat = chatManager.createChat(receiver, messageListener);
chat.sendMessage(message);
}
Current connection is of the 'sender' (i.e. sender is logged in) and my attempt is to send a message to 'receiver'. When I get callback in my listener, message.getFrom() returns the 'receiver' and message.getBody() returns null. I am obviously trying to send a message on behalf of 'sender' to 'receiver'. What am I missing?
B. My 'sender' and 'receiver' are simply unique 'usernames' (without
any #domain) and my server is simply 'localhost'.
connection = new XMPPConnection("localhost");
Do I need to modify the 'receiver' to be of different value to make it a valid JID (there are no errors at the moment)? What if I change my server (& the openfire server configurations)?
C. I am assuming there will always be one XMPPConnection per user? Is this correct?
D.
XMPPConnection.DEBUG_ENABLED = true;
When I have XMPPConnection in debug mode, a new window opens up, however, it is tied with my IDE. How can I have it not tied to the ide so I can look into the logs while trying to debug the code?
Related
Realising this is gonna be a very general question but I am gonna try to be as specific as possible:
What is the best way to design/structure an Socket.IO app?
I have a NodeJS backend with React frontend, with authentication (user must log in). I have several REST endpoints, for example /foo, /bar, /baz.
I know you can use rooms and namespaces, and I know you can add authentication to the connect as middleware, but I have no idea what the best solution is to glue this all together. I will be using this socket for multiple purposes. For each purpose I am curious what the best way to go is (flow).
General CRUD messages: When someone posts a "foo" on the server side, it needs to also send this to that particular user. WHen someone deletes a "foo", it also needs to send something to this user. So this CRUD messaging should only be for one specific user (based on logged in user ID). How would structure those messages? Namespace for "foo"? Multiple event listeners: on "foo create", on "foo delete", on "foo update?" How to make sure you only send to this user?
I have multiple pages on the client side, for the respective CRUD endpoint. So when I am on the "foo" page, I need to get updates on the "foo" backend object. How can I accomplish this?
General server side messages: I will be running long-running scripts on the server side, started by a user (or by a time trigger). If I go to that page in react and if there are long running scripts active that belong to me, I need to see those logging. (but again, they are personal so those messages are only for me).
Thanks in advance if you need more clarification just ask me and I will add this to my question.
EDIT:
I think the CRUD part can better be created as having only an "listener for updates" (like the firebase onSnapshot). So on page foo, I will listen to updates in the foo database, but the updates or creations are dont through normal REST API. Is that indeed the better way?
You can authenticate socket.io connection in 'connection' event or using middleware - doc.
Also you can use some package from npm, for example this
After authentication store user data in socket object or as separate object in 'connection' event scope.
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
});
So after you can use user object in other events for this connection.
Private messages according to your task I implemented in my project using rooms. Here abstract example:
// this is just a helper to get room name according to userId
getUserRoomName(userId) {
return `user_${userId}`;
}
// function to send data to user
sendToUser(userId, event, data) {
io.to(getUserRoomName(userId)).emit(event, data);
}
// in 'connection' event add join to user room
io.on('connection', (socket) => {
const handshake = socket.handshake;
const user = // fetch user obj according data in handshake, for example, from jwt token in header
// join to private room
socket.join(getUserRoomName(user.userId), () => {
// some logic
});
});
So, when the user connected to socket.io we join his connection to private user room. And every connection of same user will be joined to same room, so we can isolate data sending messages to this room.
Using sendToUser method you can send any type of data to all user connections from any part of your application:
sendToUser(userId, 'foo_create', data);
OR
sendToUser(userId, 'foo', {
action: 'create',
// some other data
});
I am developing one application using easyrtc tool with wavemaker tool.For a new user easy rtc provides automatically created easyrtc id.
In the chat window the random id are shown..i want to replace these ids with applications username..
I have find one solution where we have to set easyrtc.setUsername("") in client js file before calling easyrtc.connect function..
But this not solves the problem...
any help would be appriciated
Now, you can do it easyer, use this function:
easyrtc.idToName(easyrtcid)
Their is no easy way to solve this. However, it is possible using a mixture of server-side and client-side events to pass/receive user metadata when connected/disconnected. Here is a simple way to achieve this:
When a client connects to the server send user metadata via sendServerMessage on the connected event listener via client-side library. The server then receives the message from the client and stores the metadata about the user with that particular easyrtcid in a central location (ex. redis). The message sent to the server can be a json object with user metadata in a structured format. See details on connecting and sending a message to the server here: easyRTC Client-Side Documentation
When a client disconnects from the server remove their information from the data store using the onDisconnect event on the server side. This event provides a connectionObj which includes the easyrtcid of the user who disconnected. Use this identifier to remove the user from the datastore. You could also call generateRoomList() on the connectionObj to remove the user by easyrtcid and room from your datastore. You can read about the connection object here: connectionObj easyRTC documentation
Here is some example code of how to do this:
// Client-Side Javascript Code (Step 1)
easyrtc.connect('easyrtc.appname', function(easyrtcid){
// When we are connected we tell the server who we are by sending a message
// with our user metadata. This way we can store it so other users can
// access it.
easyrtc.sendServerMessage('newConnection', {name: 'John Smith'},
function(type, data){
// Message Was Successfully Sent to Server and a response was received
// with a the data available in the (data) variable.
}, function(code, message) {
// Something went wrong with sending the message... To be safe you
// could disconnect the client so you don't end up with an orphaned
// user with no metadata.
}
}, function(code, message) {
// Unable to connect! Notify the user something went wrong...
}
Here is how things would work on the server-side (node.js)
// Server-Side Javascript Code (Step 2)
easyrtc.events.on('disconnect', function(connectionObj, next){
connectionObj.generateRoomList(function(err, rooms){
for (room in rooms) {
// Remove the client from any data storage by room if needed
// Use "room" for room identifier and connectionObj.getEasyrtcid() to
// get the easyrtcid for the disconnected user.
}
});
// Send all other message types to the default handler. DO NOT SKIP THIS!
// If this is not in place then no other handlers will be called for the
// event. The client-side occupancy changed event depends on this.
easyrtc.events.emitDefault("disconnect", connectionObj, next);
});
Redis is a great way to keep track of the users connected if using rooms. You can use an hash style object with the first key being the room and each sub key/value being the users easyrtcid with a JSON hash of the metadata stored as it's value. It would have to be serialized to a string FYI and de-serialized on the lookup but this is simple using Javascript using the JSON.stringify and JSON.parse methods.
To detect occupancy changes in your application you could add a event listener to the easyrtc.setRoomOccupantListener method on the client-side and then when this event is fired send another message to the server to get all the users connected to it from the datastore.You would have to listen for a separate message on the server-side and return the users in the store deserialized back to the client. However, depending on your application this may or may not be needed.
Can I implement groupchat like WhatsApp or BBM in XMPP Asmack? I'm using Openfire Server.
I already implemented the basic multiuserchat in XMPP (http://xmpp.org/extensions/xep-0045.html), but it's not contain all the features that i needed.
I need the full features of group chat like :
groups can persist user no matter if they are online or not.
deliver offline messages to a group member (when he comes online).
Should I customize the server? or there are any Standard about this group feature?
I really need help for this problem.
Thank you.
You should use a packet listener for group chat messages. Run this packet listener in a service so that group chats are update even when the app is not running in foreground. Then check the sender id from packet and update your data base accordingly. Check the code below.
PacketFilter filter = new MessageTypeFilter(Message.Type.groupchat);
yourXmppConnection.addPacketListener(new PacketListener() {
#Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
String received_message=message.getBody();
String from_user=message.getFrom();
// Add incoming message to the list view or similar
}
}
}, filter);
I'm following the documentation and trying to create a room:
//Create a MultiUserChat using a Connection for a room
MultiUserChat muc = new MultiUserChat(conn1, "myroom#mycompany.com");
// Create the room
muc.create("testroom");
// Send an empty room configuration form which indicates that we want
// an instant room
muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT));
When I go into PSI, click service discovery, click multiuserchat, right click browse, click myroom. It pops an error message, that says "There was an error getting agents for myroom#mycompany.com, Reason: Feature not implemented. The feature requested is not implemented by the recipient server and cannot be processed".
Any suggestions, anyone?
Now I can create a new room using PSI. I also tried muc.join instead of muc.create. Same error message.
Without knowing your exact setup, I would guess that your room name (myroom#mycompany.com) is incorrect. You get a Feature not implemented because the XMPP entity mycompany.com does likely not act as MUC service. Those are implemented as separate XMPP component, usually named conference or muc, e.g. conference.mycompany.com.
I'm currently trying to make a Strophe based javascript script to get the list of available users in an OpenFire server (live refreshing needed). I don't care if I have to create a group, room or whatever it's called (anyway, the server will be running for only a small group of users, everyone connected to eachother), but I want to be able to make the server give such a list.
How can I do this? I've read that I need to use muc extension, but I can't seem to find it anywhere...
Problem solved! I had to add the users I was working with to a group and eachtime a user leaves or enters the room OpenFire notifies the other users of the room with a presence stanza wrapped inside a body tag most of the times. This makes Strophe to not identify those presence stanzas very well, so I had to overwrite the xmlInput function from the Strophe connection to get every single xml stanza that I get from the server.
conn.xmlInput = onXmlInput;
function onXmlInput(data) {
Strophe.forEachChild(data, "presence", function(child) {
var from = child.getAttribute('from');
from = from.substring(0, from.indexOf('#'));
//'type' will contain "unavailable" when offline and no attribute 'type' when online
if (!child.hasAttribute('type')) {
addUser(from);
} else {
deleteUser(from);
}
});
}