My current Perl Ironman Challenge status is: My Ironman Badge

Sunday, November 1, 2009

More PCJ refactoring

So I spent the week analyzing in greater depth the patterns I've naturally seen as I started developing POEx modules and how they could be more readily applied to my effort to refactor and generally modernize POE::Component::Jabber. A couple of things came to light.

When I was working on Voltron over the summer to have ready for YAPC10, one of the more base components to the system was the ability to proxy sessions. Hence, POEx::ProxySession was born. And in there, I recognized that I needed that role that defined behavior for sending messages: POEx::ProxySession::MessageSender. And while looking at PCJ, I realized that I needed a more generic MessageSender role that could cover the obvious pattern of accessing a POE::Wheel and call put() with some data. Sending a message via a Wheel is such a common task, that I've decided to break it out into its own role: POEx::Role::MessageSender. And it covers just the behavior described above plus the ability to store context about a particular message.

In a case like PXPS, storing context is important since every message is really a request that is expecting a response. I modeled the protocol very similar to how XMPP functions with tagging a particular message request with an 'id'. And the more I thought about it, in an asynchronous system like POE, you would have to model most of your request/response interactions the same way. So that was built into POEx::Role::MessageSender.

And on a tangent while working this up, I debated how errors should be handled. I glanced at a bunch of different projects I had written in POE over the years and came to the conclusion that exception handling is rather lacking inside POE. POE, by default, executes each handler inside an eval. If for whatever reason that event dies, the $@ is captured and delivered to the owning session via a signal. If you don't have a signal handler installed for the "DIE" signal, then you actually won't get a chance to handle it. And you won't know about it "immediately." I put that in quotes for a reason.

The "DIE" signal is one of the "terminal" signals that POE checks for when dealing with signals. That means it will kill the session and propagate up, eventually stopping the Kernel, unless handled. But that can take a awhile if you have a lot of stuff going on, for some value of "awhile". This means if you are debugging your app while developing, you are going to be very annoyed at the all the stuff you have to wade through to trace the events that lead up to the exception. And this behavior is controllable via a folded constant, but I digress.

POEx::Role::MessageSender actually throws an exception if it is unable to obtain the wheel you want to use to send a message. This requires of course people to make use of the "DIE" signal facilities which a lot of people just don't do. So the second role that I am going to develop is POEx::Role::ExceptionDispatch. This will install a simple dispatch table as a DIE signal handler and expose a hash attribute with the keys being ClassNames and the values being tuples of session id/alias/ref, and event name. The reason I decided on tuples instead of postbacks or coderef, is I wanted to leave the door open for any kind of exception management, whether that be chained (install multiple handlers per exception class with each step given the option of stopping execution), execute-all (multiple handlers per exception class that all execute), or only one handler per exception type. Instead of building all of that logic into the role and trying to fit all of those needs, I opted to use the default moose method modifiers approach. If you want to do any complex exception handling, just advise your handlers with before/around/after. Problem solved. Code kept simple.

Just 'with' the role inside a POEx::Role::SessionInstantiation enabled object, and you have a much better exception handling interface.

POEx::Role::MessageSender is up on github( , but it isn't finished at this point. I still need to flesh out the types and write tests. And soonish, I should have POEx::Role::ExceptionDispatch up there too.

My big picture goal is to develop as many of these kinds of roles as possible during the course of my work on various projects in the hopes that it will encourage further Moose + POE interaction. So any patterns of behavior that I see myself repeating between projects, I will be consolidating and generically developing into their own stand alone roles where needed. And I really hope more developers follow my lead by publishing roles to CPAN. I know many people write roles for their jobs, and with just a tiny bit of effort, for an enormous amount of gain, these generic behaviors can be made available. Because I see the future of CPAN in roles that can be easily composed and utilized

No comments:

Post a Comment