Waffle Registrar

Now you should have an understanding of how Controllers and ActionMethods work in Waffle and it has been mentioned that no custom XML configuration files are needed. So how does everything get wired up? With a simple java class called a Registrar.

In order to register Controllers (and other components Controllers may have dependencies on) we are required to extend the org.codehaus.waffle.registrar.AbstractRegistrar class. A Registrar class is used to register Waffle Actions and dependant components at various scopes.

Notice the method "session" method name? This marks the component as 'session level' within the application.

The registrar class name (CustomRegistrar in this case) needs to be referenced in the web.xml (yes we can't completely get away from XML if we want to play in the J2EE world).

Let's dig deeper into how the Registrar works and what functionality it provides. Assume we have the following three Classes in our application.

Store.java

ShoppingCart.java

CheckoutController.java

With these classes one could imagine the Registrar looking similar to:

NOTE:
AbstractRegistrar also exposes to concrete registrar implementations a method to retrieve Waffle's ComponentRegistry. This can be useful in usecases in which a custom component (registered via the web.xml) needs to be accessed to configure the user business logic.

Components registered to the Application context level are shared across all users. Session level components are shared for a user across requests. And request level components only live as long as the request - they are garbage collected after the request.

NOTE:
As with any web application care must be taken to ensure data is stored to the correct context level.

When an application starts up and the ServletContext is intialized your CustomRegistrar.application() method will be invoked. When a session is created for a user all the CustomRegistrar.session() will be invoked (that is per user and per session). When a request is initialized the CustomeRegistrar.request() method will be invoked.

Application level components only have access to other components registered in the application() method. Session level components can access their sibling components and those components registered under application(). Request level components can access their siblings component and their parent components (session()) and grandparent components (application()). In other words dependency resolution can traverse up the tree hierarchy but NOT down. The table below describes each of the available registration method provided by Waffle's Registrar.

Method Description
application will be invoked upon ServletContext initialization. This should be used for those components that need to live for the entire length of the application.
session will be invoked upon HttpSession creation. This should be used for those components that need to live for the entire length of a users session.
request will be invoked upon ServletRequest initialization. This should be used for those components that need to live for the entire length of a request

The diagram below shows a high level view of how the levels are seperated. As you might expect an application will have several concurrent sessions, and a session will more than likely handle several request (maybe not simultaneously). Of course a session from one user cannot be accessed by a session from another user, same hold true for requests.

Context level hierarchy seperation

NOTE: To prevent exceptions registration of components should follow these rules:

By default the "register" methods cache instances created within the framework. That is, only a single managed instance is created. Again that is a "single managed instance" rather than a singleton. If your application requires a new instance of a particular component for each use you can do one of two things: first simply register the component to the REQUEST context; or utilize the registerNonCaching(...) method with Registrar. The registerNonCaching method, as their name describes, will not cache and share a component but rather a new instance of that component each time.

Registrar currently has the following registration methods:

Methods Description
Registrar useInjection(Injection injection) Allows to change the type of Injection used, which defaults to Injection.CONSTRUCTOR. Injection.SETTER is also supported
boolean isRegistered(Object typeOrInstance) Determines if a component is already registered for a given type or instance
Object getRegistered(Object typeOrInstance) Returns a registered component for a given type or instance. Note that is throws a RegistrarException if the component is not registered or if more than one is registered for the same type.
void register(Class componentClass, Object... parameters) Registers a component (useful for components other than Actions)
void register(Object key, Class componentClass, Object... parameters) Register a component under a specific key (keys are used to locate Controllers from request)
void registerNonCaching(Class componentClass, Object... parameters) Registers a component in non-caching mode, ie with new instance created for each class with the defined dependency
void registerNonCaching(Object key, Class componentClass, Object... parameters) Registers a component in non-caching mode, ie with new instance created for each class with the defined dependency
void registerInstance(Object instance) Registers a component instance directly
void registerInstance(Object key, Object instance) Registers a component instance directly under a specific key

One last thing to note - we talk of CustomRegistrar here and in some our examples, we illustrate one called MyRegistrar. It can be called whatever you like. UserRegistrar, AdminRegistrar, AdminActionSetup, AdminSetup are all good names if the they make sense to you.