APE User Handling – reduce the WTF (and why there is no getUserByName)

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:

  1. ape user objects have a pubid
  2. 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).
  3. Your client cannot send a message to a nickname, but to an ape user object (if it knows the pubid).
  4. 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 ;-)

5 thoughts on “APE User Handling – reduce the WTF (and why there is no getUserByName)

  1. wow. how did you figure that out? did you have to examine the c source code or you just trial and error? thanks!

  2. Hi Peter,

    Thanks a lot for taking the time to post this very helpful example, it is exactly what many people will be looking for. Add it to the wiki!

    “Good news: You have the total control of what you want to have
    Bad news: You have to implement it.”

    Yeah I think this is the problem with APE, no one is really interested in learning/implementing when it means you have to work with serverside js. :)

  3. Hi. Thanks for posting this. It helps, but all I really want to do is be able to send a message to an individual user or the entire group. I cannot figure out how to do this. If you post something like this, it would be helpful. Thank you!

  4. Hi Xosofox,

    Ich hab da n kleines Problem… bzw eins das mir langsam meinen Kopf zermardert.
    Habe ebenfalls den APE-Server in Verbindung mit symfony 1.4 am laufen.

    Ansich funktioniert das Ganze ja, aber:
    Ich würde gerne meinem ape user Object die ID meines sfGuardUsers übergeben. Jedoch scheint dies nicht so zu funktionieren wie ich mir das dachte. Kleines Codebeispiel:

    var client = new APE.Client();
    client.load();

    client.addEvent(‘load’, function() {
    client.core.start({“name”:”getGuardUser()->getId()?>”});
    console.log(client.core.user.properties.name);

    });

    Rein theorethisch müsste ich nun ja unter client.core.user.properties.name den Namen des Benutzers (also in diesem Fall die sfGuardUser ID) bekommen.

    Das einzige was ich jedoch bekomme ist: client.core.user is undefined
    In dem Fall existiert ja nichtmal das User Objekt im Core und ich versteh einfach nicht warum, das so ist. Kannst du mir da vllt einen Ratschlag oder etwas Hilfe beisteuern? Sitze nun schon den 2. Tag an diesem Problem.
    Kannst gerne auch Kontakt per E-Mail zu mir aufnehmen ;) die Adresse hast du ja jetzt ;)

    Grüßle aus dem Schwabenland.
    Matze

  5. Hi,

    All you explained here I figured out myself but you don’t resolve the matter of custom sessions, the actual file to read and modify to understand sessions it’s actually on the APE_JSF/Source/Core/Sessions.js , there you can see how the APE_Cookie is created and which values it takes and why.

    I suppose you can modify this file to use your own session parameters and thus connect the session with your ‘web users’ to identify them correctly.

    I didn’t test it yet, but I will. Did you find any more discoveries about this topic?

Leave a Reply