SecureSocial is a plugin for Play! Framework which provides support for authentication and authorization (through both classic user/pass and OAuth providers).
It requires presence of CacheAPI implementation plugin but don’t worry – Play comes with default implementation based on Ehcache, which works very well.
But we want to try also alternative solution – Memcached, a distributed memory object caching system.
Maciej add play2-memcached plugin to project dependencies and configure it. I also have to install memcached daemon on my local machines.
Application starts without any warnings and everything goes as expected until I fire secured action.
This was a point where the problems begins 😦
Key is empty
When I tried to fire some simple SecuredAction I get an following exception:
java.lang.IllegalArgumentException: Key must contain at least one character.
I look closer to the stacktrace and start my investigation. Key was empty as exception message says, so my first direction was to find how the keys are built.
I quickly discovered that play2-memcached can prepend so called namespace to the given key to allow sharing the same storage between many application instances without key collisions.
In that moment little, yellow light bulb shines above my head 😉 I can provide workaround by setting
memcached.namespace value in
application.conf. I was awared that this is only workaround and the problem still lies somwhere in between chair and memcached plugin.
Key is too long
That works perfectly – when I wasn’t authenticated SecureSocial redirects me to a appropriate login page.
When I was going to register a new user everything goes right until I commit second registration step (filling the user details form after receive e-mail with activation link).
I got IllegalArgumentException one more time, but with different cause:
java.lang.IllegalArgumentException: Key is too long (maxlen = 250)
At this moment I think something like “if this would be a human being then it will be a very disgruntled woman” 😉
As before I started my investigation from a top of stacktrace:
Unfortunately I can’t find cause in any of these method calls.
At the same moment I remind myself that few days ago I was looking for cause of logging off when Play! is reloaded after some code changes in dev mode and I found answer on stackoverflow.
Jorge explained there that AuthenticatorStore’s default implementation persists user information using Play!’s cache abstraction.
I study its source code, especially DefaultAuthenticatorStore and I discovered two important things:
- An authenticator id is used as a cache key. Id is generated by IdGenerator trait implementation,
- Method find never checks that given key is empty, it always forwards them to the cache plugin. That was a cause of my first problem
I also found that DefaultIdGenerator produces 256 characters long identifiers – it is too long for Memcached.
Update: Solution described below is outdated. I succeed with providing a more straightforward solution, which I described in “SecureSocial & Memcached – Never that easy”.
I create my custom implementations of IdGenerator and AuthenticatorStore which restricts the invariants:
I replace problematic implementations in
play.plugins with these new ones and now everything works as expected 🙂
Also memcached namespace isn’t needed any more to avoid empty key problem.
There is a lot of joy in such investigations (I fixed the problem, oh yeah!).
I decide to make a pull request to SecureSocial repository which will provides memcache support “out of the box”.
What do you think – which element should be responsible for checking if given key is empty when covered implementation doesn’t support such keys?
Does client (secure social) have to do that or maybe the middleware (memcached play plugin) in between framework abstraction and concrete 3rd party solution?
Don’t hesitate yourself to discuss in comments 🙂