Chapter 2. The architecture of Wicket

published book

In this chapter:

  • Learning how Wicket works
  • Understanding Wicket’s fundamental concepts

Wicket is easy to use, once you grasp the core concepts, and you can be productive without needing to know the inner details of the framework. After you read this chapter, you’ll know where to turn if you run into problems or when you need to customize the framework. Also, by lifting the veil on the magical Wicket box, we hope to make you eager to start using the framework.

First, we’ll introduce the subject of many of the examples you’ll encounter throughout this book. When I (Eelco) lived in Deventer, The Netherlands, I frequented a fantastic cheese store. Like many Dutch, I’m crazy about cheese, and this award-winning store sells an amazing selection. Now that I live in the United States, more specifically Seattle, Washington, (where I moved about the same time we started writing this book), I miss a good and affordable selection of Dutch cheeses. Seattle provides more than enough options to make up for this (like the city’s impressive selection of sushi bars and Thai restaurants), but every once in a while I crave a piece of well-ripened Dutch farmer’s cheese.

Yes, it’s available but you pay sky-high prices and the selection isn’t great. I tried my luck on the internet, but the Deventer store doesn’t sell online; and although I came across some stores that cater to Dutch immigrants and sell Dutch cheese, their selection and pricing weren’t to my liking.

Just when I was making peace with the idea of a drastic cut in cheese intake, I remembered I’m a programmer! I could build my own online store! If only I had a bit more time...(maybe it’s something to pick up after I’m done writing this book). Until then, to keep the idea fresh, it serves as a good subject for a recurring example in this book.

Skip this chapter if you prefer to start coding immediately, rather than reading about the bigger picture. You can always return to the chapter.

We’ll look at Wicket’s architecture from several angles in this chapter. We’ll begin by discussing how requests are processed—what objects Wicket uses and what steps it executes during processing. After that, we’ll get to the meat of what Wicket is all about for end users: components, markup, models, and behaviors.

Let’s start by examining request processing.

2.1. How Wicket handles requests

In this section, we’ll look at how requests are processed. First we’ll examine what objects play a role in request processing, then we’ll discuss the steps Wicket executes during the handling of a request.

2.1.1. Request-handling objects

When you think about the concepts that play a role in an online cheese store—or any web application, for that matter—three immediately come to mind: application, session, and request. The cheese store, which is an application, handles requests from users, who want to do things like browsing through the catalog and placing orders. These requests in turn are part of a session (or conversation): a user browses a catalog, puts items in a shopping basket, and ends the conversation by placing the order for those items.

Figure 2.1 shows that Mary and John are using the cheese-store application. John did a search for leerdammer, browsed the goat cheeses section, and placed an order.

Figure 2.1. One application handles multiple sessions, each of which handles multiple requests over its lifetime.

When you follow an object-oriented design approach, you typically translate concepts to classes. In Wicket, the Application, Session, and Request classes—or rather, their object instances—play a central role in request processing. Figure 2.2 shows these classes together with others that are directly related.

Figure 2.2. Important classes for handling requests. The Application class is responsible for the instantiation of most objects.

Let’s take a closer look at each class.

Application

Conceptually, the Application object is the top-level container that bundles all components, markup and properties files, and configuration. It’s typically named after the main function it performs, which in our example is a cheese store. (We call it Cheesr-Application in our examples, but we could have named it CheeseStoreApplication or something similar.)

Each web application has exactly one Application instance. Practically, the Application object is used for configuring how Wicket behaves in the service of your application, and it’s the main entry point for plugging in customizations. The Application object provides a couple of factory methods to enable you to provide custom sessions and request cycles, and so on. Most of the application-wide parameters are grouped in settings objects—for instance, parameters that tell Wicket whether templates should be monitored for changes or whether to strip wicket:id attributes from the rendered HTML. If you use a framework like Spring to manage your service layer or data access objects (DAOs), you can set up integration with Wicket in the Application object.

Session

A session holds the state of one user during the time the user is active on the site. There is typically one session instance for one user. Sessions either are explicitly terminated (when a user logs off) or timed out when no activity has occurred for a certain time.

A nice feature of Wicket is the ability to use custom sessions—for instance, to keep track of the current user. Listing 2.1 shows a custom session that does this.

Listing 2.1. Custom session that holds a reference to a user

Unlike the key-value maps people typically employ when they use the Servlet API’s HttpSession object, this code takes advantage of static typing. It’s immediately clear what information can be stored in the session at any given time.

Note in this example, the methods are synchronized, because sessions aren’t thread-safe (more on that later this chapter). setUser calls dirty so that any clustering is properly performed, and the static get method uses Java’s covariance feature so users can get the current session instance without casting (you can do WiaSession s = WiaSession.get() instead of WiaSession s = (WiaSession)WiaSession.get()). When using Wicket, you typically never need to deal with the raw HttpServlet-Request or Response objects; this holds true even when you’re dealing with custom sessions.

SessionStore

The session store is responsible for where, when, and how long session data is kept. A typical implementation stores the current page in the HttpSession object (from the javax.servlet API) and stores older pages to a temporary store (by default a temporary directory) for Back button support. Each application has one store.

In addition to the user Session objects, the session store is also responsible for keeping track of the browsing history of clients in the application. Keeping track of this history supports calls to the current page and also supports the Back button.

As figure 2.3 shows, the request history is stored as pages in page maps, which in turn are linked to sessions.

Figure 2.3. The conceptual relationship between session stores, sessions, page maps, and pages

Page instances are grouped in page maps. Typically, a single page map per session is sufficient to store the history of pages the user has accessed. But you may need multiple page maps to support the use of multiple browser windows (including popups and browser tabs) for the same logged-in user.

Request

A Request object encapsulates the notion of a user request and contains things like the request’s URL and the request parameters. A unique instance is used for every request.

Response

Responses encapsulate the write operations needed to generate answers for client requests, such as setting the content type and length, encoding, and writing to the output stream. A unique instance is used for every request.

RequestCycle

The request cycle is in charge of processing a request, for which it uses the Request and Response instances. Its primary responsibilities are delegating the appropriate steps in the processing to the RequestCycleProcessor and keeping a reference to the Request-Target that is to be executed. Each request is handled by a unique request cycle.

When you get to be a more advanced Wicket user, you’ll probably use the request cycle a lot. It provides functionality for generating Wicket URLs, and it can expose some of the bare metal—like the HttpServletRequest—if you need that.

RequestCycleProcessor

RequestCycleProcessor is a delegate class that implements the steps that make up the processing of a request. In particular, it implements how a request target is determined, how events are passed through the appropriate event handlers, and how the response is delegated. An instance is shared by all requests.

RequestTarget

A request target encapsulates the kind of processing Wicket should execute. For instance, a request can be to a bookmarkable page (a public accessible page that is constructed when the request is executed), or the target can be a page that was previously rendered. It can be to a shared resource, or it may represent an AjaxRequest. The request target ultimately decides how a response is created. Multiple request targets may be created in a request; but in the end, only one is used to handle the response to the client.

Listing 2.2 shows a simple implementation of a request target.

Listing 2.2. Simple request target that redirects to the provided URL

When Wicket handles the request target, it calls the respond method, which in turn issues a redirect. Behind the scenes, the Wicket Response object delegates to the Servlet API to perform the redirect.

In this section, we looked at what objects play a role in request processing. You saw that the Application object holds settings and acts like an object factory. The session represents a user and helps you relate multiple requests. The request cycle is in charge of processing separate requests. In the next section, we’ll look at the steps Wicket follows during processing.

2.1.2. The processing steps involved in request handling

When Wicket determines that it should handle a request, it delegates the processing to a request-cycle object. The processing is done in four consecutive steps, shown in figure 2.4.

Figure 2.4. Request processing is performed in four steps: decode request, determine request target, process events, and respond.

Wicket’s URL handling is flexible, and sometimes the same kind of request can be encoded in multiple ways. For instance, depending on your settings, the URL fragments foo=bar, foo/bar, and x773s=09is7 can mean the same thing. In the first step of the request handling, Wicket decodes (unpacks the values encoded in) the URL of the request so that no matter what the URL looks like, it’s interpreted just one way. The decoding result is stored in a RequestParameters object.

If you look at the decoded values in the RequestParameters object in figure 2.4, you can guess what this request will do. The component path 2:actionLink refers to the component with path actionLink, found on the page that Wicket knows by identifier 2. Wicket assigns version numbers when structural changes occur to page instances (for instance, when you replace, hide, or unhide components). In this case, the page version derived after decoding the URL is 0, which means we’re after the first (unchanged) instance of the page.

In the next step, Wicket determines the request target. Wicket can handle many different kinds of requests—for instance, to bookmarkable pages, shared resources, and Ajax requests. In figure 2.4, the request target is an instance of class Listener-InterfaceRequestTarget, and it encapsulates the call to a link (ILinkListener interface) on a previously rendered page. In this case, the previously rendered page is retrieved using identifier 2 and version 0, as you’ve already seen.

The third step, event processing, is optional. It’s used for things like calling links or Ajax behaviors, but not for processing requests for bookmarkable pages or shared resources. During event processing, the request target may change. For example, this happens when you call setResponsePage in a form’s onSubmit method, in which case a PageRequestTarget instance is used for the remainder of the request processing. Calling setResponsePage is how you can easily navigate from one page to another when handling events such as onClick or onSubmit.

The final step is responding to the client. As mentioned earlier, the processing of this step is delegated to the request target, because that target has the best knowledge of how the response should be created.

Note

When runtime exceptions occur, a special variant of the response step is executed.

A page-request target takes care of rendering a page and sending it to the client, a resource-request target locates a resource (an image, for instance) and streams it to the client, and an Ajax request target renders individual components and generates an XML response that the client Ajax library understands.

2.1.3. Thread-safety

Much in Wicket centers around providing a natural programming model. Having to worry about thread-safety can be a pain, so Wicket tries to provide a single-threaded programming model wherever possible.

Pages and components are synchronized on the page map they’re in. Every page is a member of only one page map; in effect pages can never be used by multiple threads concurrently.

You never have to worry about thread-safety as long as you keep two rules in mind:

  • Never share component object instances, models, and behaviors between pages that are in several page maps. Although the chance that a user will trigger two pages in different page maps at the same time is slight, it’s possible, especially with pages that take a while to render.
  • Application objects, session objects, and session stores aren’t thread-safe.

So far in this chapter, we’ve looked at Wicket from the perspective of request processing. It’s good to understand what goes on in the background, but you’re unlikely to deal with this often. Starting in the next section, we’ll be more practical and look at classes you will use on a daily basis. Components, models, markup, and behaviors are all important concepts; take a break, drink some coffee, and get ready for components!

2.2. Introducing Wicket components

There are a million ways to build a house, but most people wouldn’t consider building toilets, bathtubs, and glass windows from scratch. Why build a toilet yourself when you can buy one for less money than it would cost you to construct it, and when it’s unlikely you’ll produce a better one than you can get in a shop?

In the same fashion, most software engineers try to reuse software modules. “Make or buy” decisions encompass more than whether a module is available; generally, reusing software modules is cheaper and leads to more robust systems. Reusing software also means you don’t have to code the same functionality over and over again.

Components, like objects, are reusable software modules. The distinction between components and objects is blurry, and as yet there is no general consensus on how to tell the two apart.

A workable explanation is that in addition to data, components encapsulate processes and can be thought of as end-user functionality; objects are primarily data-oriented and typically finer grained than components. Components are like prefab modules that merely require configuration and assembly to start doing their job; objects are building blocks that don’t do much by themselves. Along this line of thought, examples of components are a weather forecast reporting service and a credit-card validation module, and examples of objects are a user and bank account.

One special class of components is specialized to function in UIs. Such components are often called widgets; we’ll use the terms components and widgets interchangeably in this book. Technically, Wicket is concerned with markup manipulation, but because that markup is mostly used for rendering UIs, we can call Wicket a widget framework.

Here are a few key observations about Wicket’s widgets/components:

  • They’re self-contained and don’t leak scope. When you want to use a certain component, it’s enough to place it in a container (like a Page); other components don’t have to know about it.
  • They’re reusable. If you develop a cheese browser once, and you need it in another page or another application, you can use it there without having to rewrite half of it.
  • You build them using plain Java. Java is an expressive language that is statically typed and has excellent tool support (for things like refactoring and debugging). You don’t have to learn another domain-specific language (DSL) to work with Wicket.
  • You use them through plain Java programming. If the cheese browser component has an option for setting the number of categories it displays on a page, you can find that option by using your IDE or by looking up the Javadoc. When you use that setting, and the API changes (for instance, if it’s deprecated), the compiler and IDE will tell you.

When we zoom in on Wicket components, we can see that they consist of three parts that closely work together. We’ll call this the component triad.

2.2.1. The component triad

Making up the component triad are the (Java) component, the model, and the markup. Each has a distinct responsibility. For plain vanilla web pages, the markup defines the static parts of the pages. The Java components fill in the dynamic parts of that markup, and models are used by components to get the data for those dynamic parts.

In figure 2.5, you can see the following:

Figure 2.5. The component triad: Java components, models, and markup
  • The markup (which formally is metadata that describes text, but in our example is HTML) that contains the bulk of what is displayed to a user. Wicket matches wicket:id attributes and attaches Java components to the tags in which these attributes are defined. Here, the span tag contains a wicket:id attribute.
  • Java components Page and Label. Pages are special top-level components that we’ll talk about a bit later, and labels are components that replace their bodies with dynamic content. The label in figure 2.5 has message as its identifier, which matches with the wicket:id attribute of the span tag in the markup.
  • The Label component, which uses a model that produces the string ”Hello”. The label replaces the body of the HTML tag it’s attached to with the model value, so the browser receives <span>Hello!</span> as part of the page.

As you saw in chapter 1, markup files in Wicket never contain real processing logic. You’ll never find loops, conditionals, and the like in Wicket templates; they’re only concerned with presentation. The UI logic, such as when to display a button and what to do when it’s clicked, is encoded in the Java components. The Java components also function as state holders—for instance, to remember what page of a pageable list you’re on.

Models are optional and are an indirection for how to get the data that drives the Java components. Models hide what data to get and from where to get it, and Java components hide when and how that data is displayed. We’ll look at models later in this chapter.

Next, we’ll look at Java components, markup, and models separately, starting with the Java components.

2.2.2. Wicket’s Java components

Every Wicket Java component must extend from the Component base class somewhere down the line. The Component class encapsulates the minimal behavior and characteristics of Wicket widgets, such as how they’re rendered, how models are managed, how authorization is enforced, and whether a component should be displayed for a given context. Figure 2.6 shows the hierarchy of a few commonly used components.

Figure 2.6. A sample of Wicket’s component hierarchy

There are many kinds of components, ranging from generic to specific. Most non-abstract components are specialized for a certain task; for example, TextFields receive and display textual user input, and Labels replace their tag bodies.

We’ll get into the details of many specific components later. At this point, we’ll examine one special component: Page.

2.2.3. Page: one component to rule them all

Pages are special components that function as the root for your component trees. When you’re using Ajax or a testing framework, individual components can be rendered independently; but as a rule, components are ultimately embedded in a tree structure with a Page as the root in order for users to see them.

Think of a Page as the equivalent of a browser window. Common names for the same concept in other widget frameworks are Window, Frame, and ViewRoot.

Figure 2.7 shows a component tree with a page that has a panel and a form as its direct children. The panel nests a label and a link; the form nests a text field and a button. A page and its nested components render recursively. When Wicket asks the page to render itself, the page asks its children to render, and they in turn ask any children to render.

Figure 2.7. A page with nested components

Component paths reflect the components’ position in the component tree. Examples of component paths based on the tree in figure 2.7 are user:userName and searchForm:find, where the colon (:) functions as a separator between path elements.

The page isn’t part of the path, because only one is rendered at any given time (you can’t nest pages); that one is also always the root. That’s why pages don’t need to have a wicket:id like other components.

If you look again at the example from section 2.1.2, a request to the link in figure 2.7 could look like this

/?wicket:interface=:1:user:toProfile::ILinkListener::

where 1 is the page’s identifier.

Pages have special responsibilities that are related to rendering and the way page maps are managed. They hold versioning information, and they have special abilities that make serializing the component trees as efficient as possible.

Pages also have associated markup files. A page’s associated markup file functions as the starting point where that tree’s markup is read. Markup is parsed into a tree structure in Wicket: you’ve probably guessed how Wicket matches the Java component tree and the associated markup tree, taking into account the hierarchy of parents/ children and the way Wicket identifiers are used to match siblings. In the next section, we’ll look at how components and their associated markup work together.

2.2.4. Components and markup

In chapter 1, we introduced Wicket as a framework that bridges the impedance mismatch between the stateless nature of the web and Java code on the server-side. Wicket makes this possible by what we like to call component-oriented programmatic manipulation of markup. Components may do things like adding, removing, and changing HTML tag attributes, replacing the body of their associated tags, and in some cases generating more components and markup on the fly.

To illustrate how components and markup fit together, let’s look at another Hello World! example. Listing 2.3 shows the Java part of the page.

Listing 2.3. Java code for the Hello web page (Hello.java)

Listing 2.4 shows the page’s markup.

Listing 2.4. HTML code for the Hello web page (Hello.html)

The Hello class, as defined in Hello.java, is the Wicket Page component. The Hello.html file holds the markup for the Hello page. As you’ve seen before, Wicket automatically matches the Java page instance and the markup file if they have the same name (minus the extension) and reside in the same Java package. The Hello page instance has Hello.html as its associated markup.

The Label component doesn’t have its own associated markup file. Only a few component classes—mainly pages, panels, and borders—work with associated markup files. Components that aren’t associated with markup files are assigned a markup fragment of one of their parents. Wicket locates the markup fragment by matching the hierarchy of the Java component tree with the markup tree. Here, the label’s associated markup is the <span> fragment that has the wicket:id attribute with the value ”message”. That attribute is part of the markup as defined in Hello.html, which as we saw is the markup file associated with the Hello page—the label’s direct parent.

In listing 2.5, a label is added to a link, which in turn is added to a page.

Listing 2.5. HelloSun Java code

Listing 2.6 shows the markup for the page.

Listing 2.6. HelloSun HTML code

The HelloSun page is linked to the HelloSun.html markup file, the external link encompasses the <a> tag and the tags nested in that, and the label is attached to the span tag. To illustrate further how the matching works, look at listing 2.7: the nesting doesn’t match.

Listing 2.7. HelloSun HTML with incorrect nesting

Wicket would complain loudly about this page. The component tree doesn’t match the wicket:id markings in the markup tree. In the Java code, the label is nested in the link; but in the markup, it isn’t. If you want Wicket to do its job, the hierarchies have to match.

2.2.5. Separation of presentation and logic: a good thing?

Being required to synchronize the component tree with your markup has a disadvantage: you can’t shuffle tags around and expect everything to work. Most other frameworks let you do this, which enables you to work quickly when you’re in prototype mode. In such frameworks, you can get a lot done without writing any Java code, which may speed up your development even more.

But as is often the case, what is nice in the short term can be a pain in the long term. The fact that you can code logic in your templates means that more often than not, you’ll code logic in your templates. Or if you don’t, one of your colleagues will. Mixing logic code with presentation code should be avoided, because it poses these problems:

  • UI logic is scattered over multiple locations, making it harder to determine how an application will behave at runtime.
  • Any logic you put in your templates is plain text until it’s executed. You don’t have any static typing. And without static typing, simple typos can go undetected until you run the code. Changes can then be easily overlooked when you’re refactoring.
  • A problem that typically surfaces when projects become larger stems from the fact that frameworks that support scripting typically support only a limited subset of what you can do with a language like Java. Any DSL covers a subset of general-purpose languages. JSPs, for instance, have many different tag libraries with their own scope handling (meaning you can’t easily mix them) and their own way of expressing things.

If you limit your templates to contain just the presentation code, which is something that Wicket enforces, it’s a lot easier to keep your designs and prototypes synchronized. The designs are focused on presentation, and so are Wicket’s templates. You can hire web designers to mock up the pages and panels, for which they can use their favorite HTML editors; you’ll never have to explain to them how JSP tags or Velocity directives work. In the worst case, they may break the hierarchy, and you’ll have to fix it. But they will never introduce bugs related to business logic (which can be hard to track) because they’re completely isolated from all that.

Let’s look at what we believe is good about Wicket’s insistence on separating presentation from logic.

Easy-to-Find Logic

Wicket’s strict separation of concerns means it’s always straightforward to find the logic (in the Java code). And you have a good overview of what your pages and panels will look like when they’re rendered—you can even preview them in your web browser or HTML editor.

Convention Over Configuration

The way Wicket matches component trees and markup trees is an example of convention over configuration. You don’t need explicit configuration to get things accomplished; instead, you adhere to a few simple rules. The convention of the Java file having the same name as the associated markup file is a good example where Wicket uses the well-known Don’t Repeat Yourself (DRY)principle.

Component Nesting

Component hierarchies are trees. It is easy to traverse the tree structure, navigating from parent to child and vice versa and collecting whatever information you wish— using, for example, the visitor pattern. You can even perform updates across a tree of components in this manner and reuse this kind of code across pages. Models can rely on the models of siblings, children, and parents of the components they’re attached to, which can be a great help when you’re creating composite components. And the order of processing (like rendering and model updating) is always predictable and natural.

Plain Old Java Objects

The acronym POJO, which stands for Plain Old Java Object, was for a while part of the battle cry in the struggle against inflexible, heavyweight, XML-centric programming models promoted by the industry as part of the first few releases of the Java Enterprise Edition (JEE). Hibernate, Spring, and a few other frameworks (and their loyal bands of passionate users) turned the tide, and now lightweight or agile approaches are increasingly being favored.

Lately, it’s starting to fall out of fashion to talk about POJO programming. It no longer sounds fresh, and some argue that the fight is over and we should abandon the acronym.

But we believe the battle isn’t over, and Wicket is at the front for the web tier. Even though JSF is probably an improvement over Struts (still regarded by many as the de facto standard) and other Model 2 frameworks, the web tier of JEE still has remarkably little to do with POJO. One of Wicket’s main goals is providing a POJO programming model, and matching Java component and markup hierarchies is a key part of Wicket’s strategy to achieve this.

In this chapter so far, you’ve seen that Wicket components consist of three parts: the Java class, the associated markup, and models. It’s time to discuss this last part of the component triad.

2.2.6. The component’s data brokers: models

Models provide components with an interface to data. What components do with that data is up to them. Labels use models to replace their tag bodies, list views to get the rows to render, text fields to render their value attribute and write user input to, and so forth.

The concept of models comes from the Model View Controller (MVC) pattern, first described by Steve Burbeck in the context of a user-interface framework of Smalltalk. Since then, it’s been applied in many variations. Although the implementation of the pattern differs widely across those variations, the one thing they all have in common is that they talk about the MVC triad. There is much discussion about the pattern’s degree of purity as it’s applied, but for the purpose of this book we’ll examine how MVC is implemented in Wicket. Figure 2.8 is a diagram that shows how the tree elements interact.

Figure 2.8. The Model View Controller pattern as implemented in Wicket

This diagram shows that every element of the MVC triad has its own responsibility. The elements represent different parts of a whole:

  • The model represents the domain model and the interaction with it. A domain model includes objects like users, orders, cheeses, and spaceships. The domain model contains the abstractions of the outside world for which the system is built.
  • The view renders UI elements. It takes care of how a component is displayed, and it queries the model for any dynamic parts.
  • The controller receives user input. This can range from the value of a text field or a check-box selection to the user clicking a link or a button. The controller uses the user input to update the model, and it typically handles things like page navigation and sending events to other components.

In desktop application frameworks, the controller is typically responsible for sending messages to the view when it either detects model changes or receives input. But as in web applications, the view is rendered on a user request rather than when the component thinks it needs repainting; you don’t need to let the controller notify the view. It’s enough to update any model data that is used by the view so that the next time a component is rendered, the view will use the up-to-date data.

Figure 2.8 shows a box labeled Component drawn around the controller and view parts. This illustrates that those two elements are combined in one class in Wicket. Much as in frameworks like Swing, components in Wicket are responsible for both their rendering and the handling of input.

The IModel model interface is fairly simple. It consists of two methods, getObject and setObject—or three, if you count the detach method that IModel inherits from the IDetachable interface. Figure 2.9 shows the IModel interface with some of its related hierarchy.

Figure 2.9. The IModel interface

Components hold a reference to a model. It’s possible to let a component use more than one model, but typically it uses one model or none (models are optional).

The term model can be confusing, because many people understand the model to be the data the component is interested in. But the concept of a model in Wicket is more like an indirection to the data than the data itself. We could have called models model proxies or model locators. Models provide only a means of locating the actual Model object. Figure 2.10 illustrates.

Figure 2.10. The model locates the model object (cheese).

Figure 2.11 shows the same concepts drawn another way.

Figure 2.11. The model contains the logic for looking up the data you’re interested in.

In this example, the model holds a reference to the actual data you’re interested in. How models locate their data is implementation specific. In this case, we used the simplest model that ships with Wicket: org.apache.wicket.model.Model, which wraps the model value.

When the label from this example renders, it calls getModelObject on itself, which calls getObject on the model. This is illustrated in figure 2.12.

Figure 2.12. Calls to get the model object when rendering a component

This diagram is simplified—in reality, an extra processing step occurs for type conversion when components render. But basically, this is what happens.

We haven’t been entirely fair to one class in Wicket when we’ve talked about the component triad. There is a conceptually simple utility for extending components that is so powerful, it deserves a place in this chapter: behaviors.

2.2.7. Extending components with behaviors

The intuitive way to customize components—beyond instantiating and configuring them, if that satisfies your use case—is to extend them using inheritance. This isn’t always the most flexible approach, though—certainly not when you take into account that Java is limited to single inheritance. Behaviors are a way around this inflexibility. They provide the means to extend components using composition, which is more flexible than extending them using inheritance.

Typically, components are meant for one purpose. Labels render text. Text fields handle text input. Repeaters repeat elements, and so forth. But in many cases, you want to use a certain component but add functionality that isn’t related to its core function. For instance, when you provide a link that lets the user remove an item from a list, you may want to pop up a confirmation dialog. You could write a specialized link for this purpose, but by using behaviors you can add such a dialog without writing a special link class. As another example, wouldn’t it be nice to attach a date picker to a text field without having to create a special class?

Behaviors must be attached to components to do something useful, and each component can have several behaviors attached. Some components use behaviors for their internal workings, but you can also add behaviors to components from the outside by calling Component’s add(IBehavior) method.

Figure 2.13 shows the behavior interface.

Figure 2.13. The base interface for behaviors

All the methods in figure 2.13 except isTemporary share a common feature: they have a Component argument. This way, behaviors can be stateless (they don’t have to keep the reference to the components they’re attached to), and they can be shared among components.

Behaviors are mainly used for—but aren’t limited to—these two cases:

  • Modifying attributes of HTML tags
  • Responding to events or calls to the components they’re bound to (their host components)

For the first case, two classes are available: AttributeModifier and SimpleAttributeModifier. An example is the quickest way to show what they do. Take this code

TextField myText = new TextField("myText", new Model("foo"));
myText.add(new SimpleAttributeModifier("class", "error");

with the following markup fragment:

<input type="text" wicket:id="myText" />

This is rendered as:

<input type="text" wicket:id="myText" name="myText"
    class="error" value="foo" />

The class attribute is added to the tag the component is bound to. The text field first handles its own tag, where it sets the name and value attributes to the appropriate values. Then, it iterates through the bound behaviors and executes them. The relevant part of SimpleAttributeModifier is as follows:

@Override
public void onComponentTag(Component component, ComponentTag tag) {
    if (isEnabled(component)) {
        tag.getAttributes().put(attribute, value);
    }
}

This sets the attribute to the passed-in value when the bound component calls onComponentTag. As you’d expect, attribute modifiers are commonly used to dynamically change the look and feel of rendered components by tweaking the HTML style attribute or CSS class.

Behaviors that want to receive calls through their host components must implement an extra interface (IBehaviorListener). Behaviors that do this can receive the same kind of call that, for instance, links can receive, but they’re always passed through their host components. Typically, such behaviors modify certain attributes, such as onclick, to trigger the call to themselves. Behavior listeners are mainly used to implement Ajax behaviors, which will be explained in chapter 10.

You’ll have plenty of opportunities to see behaviors in action throughout this book.

2.3. Summary

This chapter provided an architectural overview of Wicket. We started by looking at the classes that play a role in request processing and the steps Wicket executes when it handles a request. You saw that the application object holds settings and function as object factories, that sessions represent users and can connect multiple requests, and that request cycles handle separate requests.

After that, we discussed components. The component triad consists of the Java component, associated markup, and the (optional) model. Components are nested in tree structures, and special-purpose components called Pages serve as root containers. Wicket uses convention over configuration to match component trees with markup trees. Models are used as an indirection to locate the data for components.

You also learned that behaviors form a special class that helps you configure and extend components using composition rather than inheritance. The two common forms of behaviors are attribute modifiers and Ajax behaviors.

Now that you understand the core concepts of Wicket, it’s time to become active! In the next chapter we’ll build the cheese store we just introduced.

sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage