Wednesday, October 12, 2011

Dynamic dispatch with a twist

Had a silly idea for trying out Tapestry 5.3's new ASM wrapper called Plastic: what if we could implement message dispatchers in patterns like Visitor without ruining the forward compatibility of the Visitor SPI?
Let's say we have a generic message interface:
and a message dispatcher (visitor):
When implementing new MyMessage subtypes, we would create additional methods in the interface, but this would also require re-implementing all MyMessageDispatcher subtypes. Additionally, when we have a generic implementation for handling MyMessage and we do not care about the subtypes in a specific scenario, we would not want to implement every possible handle() method out there.
Let's say we have a bunch of message classes:


When creating a message handler for this tree, we might want to handle the messages like this:



One option would be to have a reflection-based check in the handle methods defined by the MyMessageDispatcher interface. Another would be to avoid reflection and add a bunch of instanceof statements in the same method, achieving some form of type-safety. The (twisted) option described here would essentially be the second solution, but with a proxy class created on-the-fly. The main requirements for the proxy are:

  1. The proxy must implement a given dispatcher interface (MyMessageDispatcher)
  2. When handle(MyMessage) is called, the proxy must:
    1. Determine the runtime class of the argument
    2. Find an implementation of the handle() method with the first parameter closest to the runtime class of the message in type hierarchy. Excluding the first argument, the handler must have the same signature and return type as the method specified by the interface
    3. Call the implementation and return the result.


Let's implement a service for creating this kind of a proxy:


The transformer responsible for implementing this proxy is:


Internally, a DispatchTarget class is used:


A dispatcher proxy can be initialized, using the createProxy() method, passing the implementation instance to the method:



I'm actually quite interested in the performance of this bytecode mutant, compared to a reflection-based approach, so there could be some optimization and performance testing waiting ahead.

Monday, September 26, 2011

Integrating YUI3 with Tapestry 5.2

I have become a fan of the YUI's patterns for modular architecture and the idea of executing JavaScript in a restricted "sandbox". Unfortunately, I haven't found many examples for integrating this pattern in Tapestry 5.2. The closest example for integrating YUI at the time was the tapx-yui module, but as the project is more related to YUI 2, I thought to give it a go and try to implement something for YUI 3 myself.

Step 1: importing YUI JavaScript modules and CSS
As far as I've understood, there are two ways for importing YUI modules:
  1. By importing (creating a script tag for) every YUI JavaScript asset that is required on a page.
  2. By importing the yui(-min).js asset and using the auto-loading feature.
As tapx-yui already had a great example on how to go about importing the assets one-by-one, I decided to focus on using the auto-loader approach. In the following example, I define the yui.js asset as a JavaScriptStack, which also contains 4 YUI CSS assets: cssreset, cssbase, cssfonts and cssgrids. No real reason to do so, the CSS assets could as well be auto-loaded.



In this case, all assets from the YUI build are expected to be stored under the src/main/webapp/yui directory.
The CoreYuiStack class needs to be contributed to the JavaScriptStackSource service:



Step 2: Creating a component that defines a YUI module
As soon as YUI libraries can be auto-loaded, we can start defining our own custom modules. Here is an example of a component that wraps the YUI Panel functionality. Not much augmentation here compared to the YUI's Panel, but the idea should be clear enough.


A new JavaScript namespace, appcore is defined, containing a component called Panel. The corresponding Tapestry component class looks like this:



After the component's body is rendered, we add an initialization call for passing configuration data to the new component. There is no special API here, just a simple code snippet rendered into the page body. There is some room for improvement here. Although we would very much like the initialization call to be wrapped in a sandbox, the dependency on a local variable called Y looks a bit iffy design-wise.
The YuiSupport service is responsible for wrapping the initialization calls into a YUI sandbox:



Step 3: Using the new component
The Panel component can now be used like any other Tapestry component:
The output should now contain a panel similar to this:

Welcome

Good news, everyone! There's another blog on the Interwebs that mostly consists of written thoughts of a random dude who is a fan of software design and development, but also computer gaming, music and comedy. This blog.

You might find something interesting, useful or amusing here if You take interest in any of the following: software development and design (architecture), developer productivity, computer and video gaming, computer game development, somewhat outdated music, stand-up comedy or cartoons.

Many thanks for visiting. Your feedback is appreciated.
Tõnis