Having been struggeling with APE’s user management, sessions and related errors like Error 007 “NICK_USED”, I finally had to take the time to understand more in details, what is going on internally inside the ape-server regarding user management.
Because it is literally: Nothing.
WHAT? Yes, well, kind of…
Documentation again is not very good regarding this topic, so I tried to put up some stuff on my own – hope it helps
First of all, we need to understand what “Users” are.
What are APE users?
Questions like “Why is there no Ape.getUserByName?” are a natural question that I asked myself as well – but it only showed to me in the end that I did not fully understand the concept.
So the simple reason in a nutshell:
Web User != Ape User
In a setup with a client, a web server and an ape server, there is one most important thing:
The user (in terms of person using the web application) is not the user an ape server knows.
Web User: If your web appliction is referring to a “user”, it mostly is ONE person, having a nickname, a password, a name, an email… (called “web user” from now on)
Ape User: On an APE server, a user is “an instance of an object with a pubid and pipe that established a connection to the ape server and keeps polling”. (called “ape user [object]” now)
And with this said, the problem becomes obvious: APE does NOT AT ALL know or care about your web app’s user database and cannot relate to it.
Sure, in a default installation and configuration the APE server has some handling of the username (you will have experienced that when you tried to get your first ape project running). And you will have wondered, what those errors “006 BAD_NICK” or “007 NICK_USED” mean and where they come from.
But actually, it is not “APE” that is doing that, because the server does not care about nicknames or usernames. In fact, this comes from a file/module on the server called “nickname.js” – and it is in the “example” directory. And actually, this is what it’s meant to be: an example only!!!
Because in some cases, this can become a nightmare (especially, if the guy in front of your browser hits F5/Rrefresh) and might/does not fit your needs.
So, what then?
Good news: You have the total control of what you want to have
Bad news: You have to implement it.
Defining what you want, need and can do, requires some understanding of the interaction between the APE_JSF client framework and the APE server.
First of all and most important: consider the APE server to be DUMB! It’s just a stupid machine that waits for incoming connections and if told so, passes it on. It does not know about usernames of the web application and has NO CLUE AT ALL how two web users can exchange messages.
However, if there is a message incoming from an ape user’s pipe and it includes the info “pass this on to the pipe with pubid ‘0123456789abcdef'” it will happily do so – maybe resulting in a message to another web user, if your app works like this (basic chat).
So, what this means is:
- ape user objects have a pubid
- Your CLIENT (APE_JSF, JS on web browsers side) knows it’s own pubid. And in a public channel where there is an event when another ape user objects joins a channel, your client can TRACK other ape user objects (with pubid).
- Your client cannot send a message to a nickname, but to an ape user object (if it knows the pubid).
- How can your client know, which ape user object relates to which web user?
Enter: “nickname.js”!
The nickname.js default example requires the CLIENT to pass a “name” when establishing the connection to the ape server. And this “name” is stored on the server side in a hash (key-value-pair). Additionally, it is added to the ape user object as a property.
By this, it is possible on the client side to track the web-user <-> ape-user relation. If you listen to the userJoin event, you get the ape user object AND a property, telling you the web user name.
Now, on the server side, all the ape server does in this nice (and at the same time dreadful) “nickname.js”, is creating a hash with the web user nicknames.
So, whenever a new user (ape user) tries to register with a nickname (web user nickname), it checks the name against this hash. And if the nickname already is present, throws a well known error.
The main issue with this is, that whenever your client hits refresh, he will reconnect to the ape server and try to initialize a new ape user object and forget about the old object. This means, that the previous ape user object will remain on the server for the next 45 seconds (default time out) and you cannot connect a new users with the same nickname for this timeframe.
And all of this is handeled by nickname.js, allowing only “one web user at a time”.
Enter: “apeCoreSession.js”
or apeCore.js vs apeCoreSession.js – which one to use?
In your configuration you have to define which of the two files your client should use. This also implies some effects on you user handling.
Because with apeCoreSession, APE_JSF tries to re-attach to the previous session on every reload. This means, that when you hit refresh and the APE JS Framework initializes, it will try to “recover” your previous session in terms of the ape users pubid as well as joined channels.
Even when you are having multiple browser windows open, all sessions will be reachable under one common ape user object = pubib and if you have a name property set, you will have a chance to find a pubid for a give username.
But what if the user opens another BROWSER (not tab) or logs in from another machine/location? Baam, problem again…
Now, depending on the purpose of your apllication, you can decide what you need to do – like not allowing additional users with the same name or handle it.
For test purpose, I rewrote the nickname.js to test, understand and show, what is going on internally.
The first idea was to create a “flat” hash with username (key) and pubid (value). However, this means that whenever a web user connects again, the old pubid is forgotten and only the new one saved – it could do the job in combination with apeCoreSession.
//global userlist - a hash of key (=username) and values (= hash of pubids)
var userlist = new $H;
Ape.registerHookCmd("connect", function(params, cmd) {
if (!$defined(params.name)) return 0;
cmd.user.setProperty('name', params.name);
return 1;
});
function listUserNames()
{
Ape.log("--------Current Userlist-------------------------");
userlist.each(function(v,k){
Ape.log(k + ": ");
v.each(function(pubid){
Ape.log(" "+ pubid);
});
});
Ape.log("--------Current Userlist End---------------------");
}
Ape.addEvent('adduser', function(user) {
var name=user.getProperty('name');
var pubid=user.getProperty('pubid');
//Ape.log("NEW: " + name + " as " + pubid);
var uis=userlist.get(name);
//if no user instance hash, create one
if (!$defined(uis))
{
uis= new $H;
userlist.set(name, uis);
}
uis.set(pubid,pubid);
listUserNames();
});
Ape.addEvent('deluser', function(user) {
var name=user.getProperty('name');
var pubid=user.getProperty('pubid');
Ape.log("Timout: " + name + " " + pubid);
var uis=userlist.get(name);
uis.erase(pubid);
//if last instance left, erase hash
if (uis.getLength()==0) userlist.erase(name);
listUserNames();
});
This example however creates a hash PER USER, tracking ALL the current pubids. So, in theory, you could send a message from the server to all pubids (= user instances) of a given username.
So, just give it a try in a test setup, replacing the original nickname.js and while watching the ape log file and hitting F5 in several different browsers, you will get a better understanding of web users, ape users and pubids.
Feedback or Flattr clicks much appreciated 😉