This document attempts to provide a very high level
overview of the Barracuda Component Model strategy by answering some of the basic
questions. We start with the obvious...
- What is the purpose of this package?
- What is a Component?
- What is a View?
- What is an ElementFactory?
- What are ViewCapabilities?
- What is a ViewContext?
- What is a Model?
- What kinds of data can a Model return?
- How does all this stuff fit together?
- Can I see some UML?
- How does Barracuda compare with Swing?
- What components does Barracuda provide?
- How does the BText work?
- How does the BList work?
- How does the BTable work?
- How does the BTemplate work?
- How does the BAction work?
- How does the BLink work?
- How does the BSelect work?
- How does the BInput work?
- How does the BToggleButton work?
- How do components support multiple markup
languages?
- Where should I look for the gory details?
- Can I create custom components?
- Can I create non-visual components?
- How about some working examples?
Q: What is the purpose of
this package?
A: The purpose of the Component Model package is to make it easier to
manipulate the DOM structures (generated by tools like XMLC)
that back HTML, WML, and even XML pages.
We accomplish this by using components with strongly typed Model interfaces; rather
than directly modifying data through the low-level DOM interfaces, you create a component,
bind it to a portion of the DOM, and then render the component. The component is smart
enough to figure out how to take data out of the model and insert it into the DOM. This
makes it significantly easier to manipulate complex HTML structures (like lists and
tables) programatically.
Q: What is a Component?
A: A component is really just an object that has three basic
characteristics:
First, a component must support the notion of a View. A
view is simply the object a component should render itself in. In the case of the
Barracuda component model, there can actually be more than one view per component.
Second, it must implement the BContainer interface,
which defines the methods necessary for a component to contain child components; after a
component renders itself, it should also ask all of its children to render as well.
Third, it must extend from the AbstractBComponent
class, which defines a render() method (similar to paint() in GUI component models); when
the render method is invoked the component should "render" its data in all of
its views.
Some components may support additional characteristics as well. For
instance, they may require the user to manipulate data through a Model interface (like in
Swing). Or, the components may provide support for server side event handlers that are
notified when an action occurs on the client (like traditional client-server apps).
This very simple arrangement makes it possible to assemble hierarchies of components
that are bound to various points of a DOM page. Render the root component and all of the
components will update their portions of the page as well.
Q: What is a View?
A: A View has a 1:1 relationship with a node in a DOM. The view is
used to "bind" a component to a particular point in the DOM. When the component
renders, it is responsible for mapping data from its model into the part of the DOM the
view is bound to.
Most components just use a DefaultView. There are, however, several
special kinds of views: a TableView is used to identify table structures;
a TemplateView, is used to indicate that a portion of the DOM will
processed by the BTemplate component.
Q: What is an ElementFactory?
A: Whenever a view is bound to a node, the view creates a structure
called an ElementFactory. An element factory basically catalogues all the
children beneath a given node, allowing them to be retrieved later (by the model) to be
used as templates for new nodes. The default implementation of ElementFactory
catalogs elements based on their class name, interface name, and id attributes. As an
example, consider a section of HTML that looks like this:
<ul>
<li id="CreditRow">...</li>
<li id="DebitRow">...</li>
</ul>
would allow you to retrieve template rows from the element factory like this:
Element creditRowTemplate =
elementFactory.getElement("CreditRow");
Q: What are ViewCapabilities?
A: The ViewCapabilities object is used to describe
the client view capabilities. It specifies the client type (what kind of browser), the
format type (HTML, WML, etc), and the scripting type (None, Javascript, etc). This
information is available for the component to consider when rendering. It also returns the
target client Locale, which is very useful for the developer who wishes to load a
localized DOM template.
Q: What is a ViewContext?
A: A ViewContext structure acts as a simple container
for the ElementFactory and ViewCapability objects; it is passed into each component during
the render process. The model implementation also receives a copy of this structure.
Q: What is a Model?
A: Now is a good time to discuss the role the Model plays. Every
component has a model-- in the case of a BText, the model is simply the underlying text
String; in the case of other components, the model is an actual interface (ie. ListModel,
TableModel, TemplateModel). That this is very comparable to Swing.
When the component needs to render itself in a View, it takes the data from the model
and loads it into the DOM structure. List and table components will usually clear any
prexistant data from the DOM structure; in these cases the model is really driving the
structure and content of the list. In the case of the BTemplate component however, the DOM
template itself drives the structure and content -- the component parses the template
looking for keys and as they are found it queries the model to replace the data based on
key name. This demonstrates the different methods components can use to actually update
the DOM.
As in Swing, Model implementations will generally be implemented as inner classes.
Q: What kinds of data can a Model return?
A: In Swing, model implementations can return data of type Object as
opposed to just String. The Swing component examines the returning data type; if it
implements JComponent, then the new component will be added directly to the parent
component. Otherwise, the data is converted to a String and displayed. This is very
powerful because it allows the nesting of components: a JList can contain simple String
data, or it can contain complex components.
This is actually very similar to HTML and other *ML languages, where a list or table
structure can contain other complex blocks of markup.
Barracuda follows Swing's example here. In the Barracuda component model, models can
generally return one of three types of data:
Nodes - if the returning data implements Node, it will
be added directly into the parent structure. This is useful where you would like to return
HTML fragments, like headers, footers, etc.
BComponents - if the returning data type implements
BComponent, it is automatically added in to the parent component as a temporary child
(causing it to get rendered too) and then removed once the rendering proces is complete.
This is very powerful because it allows models to return complex components. For instance,
you might want to return a BLink component to represent a URL link. Or, you might want to
return a BList (to create lists within lists!). There are many similar examples.
Objects (Strings) - if the data returned does not
implement Node or BComponent, it is converted to a String and added to the underlying DOM
structure by delegating to a BText.
One specific component (BSelect) can also support models that return ItemMap
objects. This interface is basically a convenience mechanism to pass back multiple data
elements like a key and a value in one wrapper object--useful in rendering Select lists.
All of this is highly transparent to the developer, just like it is in
Swing. All you have to know is that you can return these types of data and the component
model will handle the rest.
Q: How does all this stuff fit together?
A: Let's see if we can construct a cohesive picture of how this all
fits together. Generally the process of rendering a component hierarchy will go something
like this:
- load DOM object
- construct component hierarchy, binding individual components to specific spots in the
DOM via Views
- render the component hierarchy
- render the DOM object
Of course, we could apply caching patterns to steps 1 and 2 here to improve performance
by reusing the component tree. The key point to note, however, is that unlike stated GUI
components, here we really have a 2-phased render: first we render the component hierarchy
(which updates the DOM), then we render the DOM (which actually generates the client
view).
This distinction is important because it highlights a key difference between a server
side component model and more traditional client-server components. In the traditional
model, we can get away with a one-pass render because all the information needed for
rendering (both layout and data) is kept in one place: a single component hierarchy.
Here in the web-app paradigm, however, the DOM "template" structure is really
defining the layout and structure of the document. Now, we still need the component
hierarchy in order to ensure that all components render, but the actual layout specifics
are contained in the DOM template the components are bound to. Consequently, we need a 2
phase rendering process because the information needed to complete the render is stored in
two distinct places: the component hierarchy (data) and the DOM template (layout).
The following diagram illustrates the concept:

- We load the DOM template (via an XMLC object or some other DOM generating mechanism)
- We create our component hierarchy and bind it to the DOM template. The root component is
not bound to any node; each of the child components is bound to a specific node in the
DOM.
- We invoke render on the component hierarchy; the root component does not have any views,
so it simply invokes render on all the child components.
- When the BText component renders, it takes the component's text value, finds the first
Text child beneath in its view and sets the text value in it.
- When the first BList renders, it starts by removing all DOM items beneath the List1
node. Then it queries the model to determine how many items it contains, and adds them
back in as new items to the list.
- The second BList does the same thing, updating the portion of the DOM for which it is
responsible.
- Finally, we render our updated DOM template to generate the client view and return it in
response to the original HTTP Request.
As you can see, the actual process is really very straightforward. It may be
worthwhile, however, to investigate the actual mechanics of how components handle the data
returned by the model. As we mentioned above, a model can return 3 distinct data types:
Nodes, BComponents, or Strings.
In the case of List1 above, if the model returns a Node, that value is just added in
directly as a child of the <ol>
element.
If the item is a BComponent, however, we are dealing with a complex component that is
already bound (or should be) to a node in a template. In this case the list component
locates that node that backs the list item and adds it to the actual DOM list. Now, the
list component adds the list item as a temporary child to itself. This last step is
critical: when the list component finishes rendering, it will invoke the render method for
all its children as well, including those BComponents which were returned from the model.
This will ensure that each BComponent list item also renders itself into the node to which
it was bound.
If the list model returns a String, the component actually wraps that value in a BText
and adds that to the list (which results in the same processed as described above).
If these details sound complex, don't worry: as a developer all you really
to know is that a model can return objects of type Node, BComponent, and
String. The components should take care of the rest.
Q: Can I see some UML?
A: Sure, here's the complete Component Model Class
Diagram.
Q: How does Barracuda compare with Swing?
A: If you are familiar with Swing you will undoubtedly see a great
deal of similarity. Both Swing and the Barracuda Component Model define strongly typed
model interfaces that make it very easy for programmer to provide the component with data.
In addition, Barracuda also keeps track of component validity; if you invoke render on the
component hierarchy again, only components which have been invalidated will actually be
re-rendered. This makes sense because as long as the data has not changed in the models,
then there is no need for the component to update the DOM again. Again this is very
similar to Swing.
There are some differences however. Most notably, Barracuda also defines View
interfaces, whereas Swing does not. We primarily do this because some components (like
BTable and BTemplate) require additional information in order to render themselves in the
view, and we wish to ensure that the component is bound to a View that it will be capable
of rendering in.
In addition, Barracuda defines far fewer components than does Swing. This is because
the template approach makes each component much more flexible. For instance, you can
bind a BList component to just about any DOM node and it will do its best to render the
data as a list structure suitable to the parent node. As an example, if you bind the list
to a <ul>
node, list data will be wrapped in <li>
items. If you bind the same list to an <select> node, the list data will be wrapped
in <option>
elements. Furthermore, the components are smart enough to handle multiple output formats
(HTML, WML, etc).
As a final note, it's important to observe that Barracuda does not attempt to match
Swing components on a 1:1 basis. Sure, you'll see some that look familiar (BList, BTable,
BText), but you'll see many that are modeled after the underlying markup structures
(BAction, BLink, BInput, BToggleButton, BSelect). And some are actually modeled
after higher level presentation concepts (BTemplate).
Where there was natural commonality, we tried to keep things as close to Swing as
possible. We didn't want to lose sight of the fact that we're dealing with the web-app
paradigm, however, and so we also tried to simplify whenever possible.
Q: What components does Barracuda
provide?
A: Barracuda currently provides the following components:
- BText - used for binding text into a node within the DOM
- BList - used for generating a list structure in the DOM
- BTable - used for generating a table structure in the DOM
- BTemplate - used to treat the DOM structure as a simple template
engine, querying the model based on key values found in the template.
- BAction - used to bind server side event handlers to DOM elements that
can generate events on the client side (buttons, links, input elements, etc.)
- BLink - extends BAction, and will also set text in the DOM (ie the
display name for a link)
- BSelect - used to manipulate a HTML/WML select element within the DOM
- BInput - used to manipulate a HTML/WML input element within the DOM
- BToggleButton - used to manipulate a HTML radio and checkbox input
elements within the DOM
Q: How does the BText work?
A: Unlike most of the other components, BText does not have a specific
model interface. Instead, the component's "model" is really just the internal
variable that stores the text value. When the component renders, it retrieves that value,
finds the first Text child beneath the Node to which it is bound, and sets the value
therein. Its really quite simple.
We should note that this makes the BText component very flexible: you can bind it to
just about any node and it will do it's best to set the text in the appropriate place. In
all likelihood, developers will probably not actually use this class a lot, although it is
used extensively by the other components.
Q: How does the BList work?
A: The list is actually fairly straightforward and easy to use. In
order to use the list component, you must bind it to a node and then provide it with a
model that is capable of returning the data for the actual list.
When the list component renders, it starts by removing all children beneath the node to
which it is bound. Then it simply iterates through the list model, retrieving items from
the model and adding them to the list based on the mechanics described above in How does all this stuff fit together? It should be noted
that when adding items to the DOM template, the list does use some simple intelligence to
try and ensure that the data you are adding is actually valid markup. For instance, the
HTML DTD specifies that <ul>
and <ol>
tags can only contain <li>
elements. If you try adding a <table>
element to one of these parents, the component will automatically wrap it in an <li> element first.
Q: How does the BTable work?
A: The generic table component operates on the same principal as the
list component: remove all data from the DOM element and then repopulate it based on the
data in the model. There are a couple of noteworthy differences, however.
First, the table component supports both kinds of table structures defined in the HTML
4.0 spec (those with <thead>,
<tbody>,
and <tfoot>
elements, as well as those that just contain <tr>
elements). When you create a TableView, it preparses the template in an attempt to locate
the header, body and footer elements. If it cannot find them, it just assumes that header,
body, and footer elements will all be placed directly under the <table> node.
Second, the table component allows for three distinct table models: one for the header,
one for the footer, and one for the body. These are invoked in turn and the data is added
into the table accordingly. Of course, you don't have to implement either the header or
the footer models if you don't wish to; this approach just adds flexibility and makes it
easy to create complex, multined headers and footers.
Q: How does the BTemplate work?
A: The BTemplate component takes a different approach than the other
Barracuda components. Instead of clearing the template and repopulating based on the
contents of the model (which mirrors the Swing approach), the template component parses
the template looking for tagged keys and then queries the model for data based on key
name. This approach is similar to that taken by template engines, although because
BTemplate is a component, it can be freely intermixed with the other Barracuda components
(so it's a lot more flexible than most template engines).
BTemplate is probably the easiest of all the components to use; let's take a closer
look at how it works.
First of all, the easiest way to use the template component is to embed simple commands
or "directives" inside the actual markup template. In HTML, directives can be
specified in an elements' class attribute. In XML, directives can be embedded by using
processing instructions. Now some developers want to avoid adding any kind of programming
logic to *ML; directives can also be specified programatically via a "directive id
map" which allows the template component to lookup directives based on ID. Using this
latter approach directives could be stored anywhere (in code, properties file, XML, etc).
The examples below will demonstrate directives stored in a properties file.
The key here is that the directives provide a flag to instruct the component:
"Hey, go query the model for the piece of data that goes here". The component
interprets the directive to determine which model is capable of serving the request, and
passes in the directive key. At this point, the model acts just like those for the other
Barracuda components: the developer can pass back a Node, BComponent, or a String and the
component will ensure it gets added into the template in the proper position.
Let's take a look at a simple template (ie. sample.html):

The ids here match values in a corresponding directives file (ie. sample.directives) that might look something like this:

As the component parses this template, it finds directives based on id values in the
template. It will then query the model based on directive keys. Notice that there are two
models referenced here: ReportData and UserData.
The ReportData model might be implemented like this:
class ReportModel extends
DefaultPropertiesModel {
public ReportModel() {
super("ReportModel");
}
}
That was it? Wow! Yep. In this case, the model is extending DefaultPropertiesModel,
which simply looks for the keyname in an underlying properties file. Pretty easy.
The UserData model is a little more complicated, but not much. It could be implemented
like this:
class UserModel extends
AbstractTemplateModel implements IterativeModel {
//data structures
UsersList users = UsersList.getData();
UserData ud = null;
Iterator it = null;
public String getName() {return "UserData";}
public Object getItem(String key) {
if (key.equals("First")) return
ud.get(UserData.FIRST);
else if (key.equals("Last")) return
ud.get(UserData.LAST);
else if (key.equals("Descr")) return
ud.get(UserData.DESCR);
else if (key.equals("Age")) return
ud.get(UserData.AGE);
else if (key.equals("Gender")) return
ud.get(UserData.GENDER);
}
public void preIterate() {
it = users.iterator();
}
public boolean hasNext() {
return (it!=null && it.hasNext());
}
public void loadNext() {
ud = (UserData) it.next();
}
public void postIterate() {
it = null;
}
}
Note that the UserData model implements IterativeModel. This allows the template
component to loop through the data set when it enounters the iterate directive. During
this process, the model will faithfully return key values for the current record, and then
report when it's out of data so the template component can continue.
The net result of all this is that its very easy to use: the designer tells the
programmer "this is what data I need"; the developer responds by saying
"here are the models and keys you can use to retrieve the data". At this point,
the developer can focus on creating the models that provide the data and the designer can
focus on making the page look good. Both can work independantly of one another.
This clean separation makes it possible for the designer to drastically alter pages
without requiring the developer to make further changes to the server. For instance, let's
say the designer decides that the list format just doesn't look right, and instead the
data should be presented in a table format. The template could be altered to look like
this (sample2.html):

If we are using XMLC to generate the DOM structures, all the designer has to do is
recompile the XMLC pages and the new layout will take effect! The developer is only needed
when the designer requires additional information that is not present in the current
models.
For many developers, BTemplate will be the easiest way to leverage the power of
Barracuda components. For those who would rather not use the directive approach, the more
traditional components are still at your disposal. You should also keep in mind that
many times the template model will actually want to return instances of these
more specific components, so even if you love templates, you'll still want to read about
the rest...
Q: How does the BAction work?
A: In a nutshell, the BAction component allows you to manipulate
client-side markup that is capable of server-side events. Specifically, a BAction
component can be bound to <a>,
<form> <input>, <button>, and
<select>
elements. You can use a BAction to specify what kind of "action", or
"event" should be generated when the element is activated.
For instance, when you bind a BAction to an <a>
element, you are saying "this is what I want the link to be...". BAction allows
you to specify the action as a URL or as an Barracuda Event. In the latter case, when the
link is pressed it would cause the specified event (which defaults to ActionEvent) to be
generated (and handled) on the server. The same thing applies if you bind the component to
a <button>;
you are saying, "when this button is pressed, generate this particular event".
BAction also makes it easy to catch the events generated by client side components by
providing a method called addEventListener(). This method allows you to
specify an event handler factory (defined in the Barracuda Event Model) that should be
notified when the event occurs.
In short, BAction makes it easy to control a) what events get generated by client-side
markup and b) what server-side interests handle those events. In the future, we intend to
add support for client-side event handlers (via Javascript) as well.
Q: How does the BLink work?
A: BLink provides a simple extension of BAction that allows you to
specify the text that accompanies an action. The most obvious usage would be for working
with anchor elements <a>,
where the action corresponds to the href
attribute, and the text corresponds to the text that the link contains. BLink can also be
bound to <button>
and <input>
elements (although you'd usually use BInput for this); the text value will be used to
render the text associated with those elements.
One of the really convenient features about BLink is that it doesn't actually have to
be bound to a node. You can return it as data for a Template model and it will
automatically create an anchor element if needed and bind the link to it. This makes it
very easy to work with links in a web-application.
Q: How does the BSelect work?
A: BSelect provides an extension of BList that is specifically
tailored to manipulate <select>
elements. In a nutshell, the BSelect component will render the list data as <option>
elements in the select list. As its name suggest, BSelect also supports the concept of
"selection" via a ListSelectionModel very similar to that found in Swing--you
can select one or more elements in the list simply by setting the appropriate range in the
selection model.
When working with HTML select lists, many times you will want to set both key and value attributes
for each option. Since the ListModel interface only allows you to return one item for a
given list element, we created a special ItemMap interface that acts a simple wrapper
around key/value pairs. The BSelect component is smart enough to recognize ItemMap objects
and set the key/value attributes accordingly.
We should also note that BSelect provides a convenience mechanism to add event
listeners directly to the component, just like with BAction. This all combines to make it
much easier to manipulate select elements from within your Java code.
Q: How does the BInput work?
A: BInput is another specialized component, in this case aimed at
making it easier to manipulate <input>
elements. BInput allows you to specify the element type (TEXT, PASSWORD, SUBMIT, RESET,
FILE, HIDDEN, IMAGE, BUTTON, RADIO and CHECKBOX) and set the value associated with that
element. This is very useful for manipulating textfields, buttons, etc. As with BSelect,
BInput also allows you to add event listeners directly to the component.
Q: How does the BToggleButton work?
A: BToggleButton extends BInput to provide additional functionality in
dealing with Radio and Checkbox buttons. In a nutshell, it allows you to mark them as
selected/unselected. Again, you can add event listeners if you so desire.
(A brief note here: you may be wondering "Why would I want to add an
event listener to a Radio button?" Well, in most cases you wont: usually the button
would be part of a form and you'd want to defer processing the button until the form is
submitted. In a case like this, you'd never need to add an event listener to the specific
Radio button component.
There may however, be occasions where you'd like to be notified immediately as soon as
a button is pressed, without waiting for the user to press a separate submit button. In
cases like these, adding an event listener makes perfect sense. When the component
renders, it sees that it has event listeners and so will use Javascript to ensure that
when the component value is clicked/changed, the server side gets notified.
Ultimately, the developer will need to decide whether or not its appropriate; the
Barracuda components just aim to make it easy to do when you need to)
Q: How do components support multiple
markup languages?
A: The Barracuda components use custom renderers to handle different
kinds of markup. In a nutshell, every component generally has a static initializer that
registers specific renderers for various classes of documents. For instance, the BInput
component says "whenever you need to render an HTMLElement or HTMLDocument, use the
HTMLRendererFactory". Other classes like BTemplate already have support for both HTML
and XML documents. When the component renders, it looks at the type of node it is bound to
(starting with the more specific interfaces, like org.w3c.dom.html) and looks for a
renderer that can handle the component/document combination. If none is available, it will
work up the parent interfaces until it finds a suitable match.
The net effect of all this is that if you create a custom component (ie. MyComp.java)
you can also create a custom renderer and install it dynamically so that MyComp.java will
be rendered by your renderer rather than the default renderer.
While not really that difficult to follow, the details are a little complex.
Fortunately, the developer is generally insulated from these details (unless you decide to
start writing custom components). The good thing is, if you need it, its there!
Q: Where should I look for the gory details?
A: If you really want to understand components in all their gory
details, the place to start is actually in the org.enhydra.barracuda.core.comp.renderer
package. This package contains all of the markup specific renderers used by each
components. Most of these renderers are very simple and straightforward. By studying these
classes, you will be able to quickly understand exactly how components manipulate the
markup.
After that, you should probably study the components themselves, especially
BAbstractComponent and BComponent, as these classes are responsible for most of the
general component behaviour.
Q: Can I create custom components?
A: Sure! All you have to do is extend BComponent (or one of the other
components) and override the render() method. Or, if you prefer, you can override three
methods which are called by the default implementation of render(ViewContext vc):
- preRender(ViewContext vc) - called prior to any actual rendering
- renderView(View view, ViewContext vc) - called once for every view
associated with the component; this is the method that should generally do the actual work
of rendering the component into the DOM
- postRender(ViewContext vc) - called after all rendering is complete for
the component and the components children
It should be noted, however, that in most circumstances you'll probably never need to
extend the base components. As with Swing, you can often control look and feel of the data
simply through your model implementation.
If you do decide to create a custom component, there are really only two key
requirements: first, you component should handle multiple output formats as specified by
the ViewContext structure; secondly, if your component uses a Model interface, it should
handle the standard data types that models can return: Nodes, BComponents, and Strings.
Following these guidelines will help you create well behaved components that are useful to
the Barracuda community.
Q: Can I create non-visual components?
A: Yes again. All you would have to do is override the render() or
renderView() methods as specified above. There is no requirement for a component to
actually have any views, or to actually render anything into those views, so it would be
entirely permissable to have components that do not actually manipulate the DOM structure.
It should also be noted that components are free to traverse the component hierarchy,
so it would certainly be possible to create a component which interacts with other
components above or below it in the hierarchy. Given the generic nature of the component
hierarchy, there are really not many limitations to what components can do.
Q: How about some working examples?
A: Sure thing. If you're new to components, start by taking a look at
the Component Model Tutorial. Once you're familiar with the
basics, check out the BConfig Application for a
good example of how it all fits together. |