You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

Bookmarkable link

Table of contents

The Life-Cycle of a Wicket Application

Loading the Application

A Wicket application runs in any J2EE compliant application server by
defining a Java servlet in the application's web.xml file:

	<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE web-app
		  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
		  "http://java.sun.com/dtd/web-app_2_3.dtd">

	<web-app>

		<display-name>My Wicket Application</display-name>

		<servlet>
			<servlet-name>HelloWorldApplication</servlet-name>
			<servlet-class>wicket.protocol.http.WicketServlet</servlet-class>
			<init-param>
			  <param-name>applicationClassName</param-name>
			  <param-value>wicket.examples.helloworld.HelloWorldApplication</param-value>
			</init-param>
			<load-on-startup>1</load-on-startup>
		</servlet>

		<servlet-mapping>
			<servlet-name>HelloWorldApplication</servlet-name>
			<url-pattern>/helloworld/*</url-pattern>
		</servlet-mapping>

		<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		</welcome-file-list>

	</web-app>

The servlet-class specified will always be "wicket.protocol.http.WicketServlet".
The "applicationClassName" init-param provided must be the name of a WebApplication
subclass. In the case above, it is "wicket.examples.helloworld.HelloWorldApplication".

When WicketServlet is loaded, it will use this information to instantiate a single
instance of your application class.

Servicing a Request

The following three steps occur each time that WicketServlet handles a
request:

1. Wicket asks the Application class to create a Session for the
servlet request. If no session exists for the incoming request, a
Session object is created using the application's session factory.

2. The Session returned by the Application is asked to create a
RequestCycle object using the Session's request cycle factory.

3. The RequestCycle's request() method is called to handle the request.

How RequestCycle Handles a Request

For each request, a RequestCycle object does the following 3 things:

1. Calls the overridable method onBeginRequest() to allow RequestCycle
subclasses to do things at the beginning of each request, such as opening
a Hibernate session.

2. Parses the request and potentially invokes user request handling
code. If request handling sets the responsePage property of the
RequestCycle, the request() method is called on the response page.

3. Calls the overridable method onEndRequest() to allow RequestCycle
subclasses to do things at the end of each request, such as closing a
Hibernate session.

Step 2 may involve some pretty sophisticated logic. When a request
URL is parsed, it may include information such as a Component listener
to invoke. This Component listener may create a whole new Page that
is used

How Page Handles a Request

Page.request(), called in step 2 above, performs the following 3
steps to handle a request:

1. Calls onBeginRequest() for every Component on the Page, giving each
Component a chance to alter itself before rendering begins.

2. Renders the Page by calling Page.render().

3. If the Application for the Page has component checking enabled in
its ApplicationSettings, checks that each component rendered.

4. Calls onEndRequest() for every Component on the Page.

Once step 2 begins (the render phase for the Page), the Page becomes
immutable and it is no longer valid to alter either the Page's
component hierarchy or the values of any of its models. Attempting to
change the Page during rendering will generally result in a runtime
exception.

Clustering

When a Page is replicated from one machine in a cluster to another,
the onSessionAttach() method will be called for every component on the
Page.

Model Changes

When models or model values are changed in Wicket by a call to
setModel() or setModelObject(), the change results in a call to
onModelChanging() before the change actually occurs and then
onModelChanged() after the change occurs.

Because Wicket cannot always know if a model has changed, you can help
it to do smart things (such as versioning your Page's model changes)
by calling modelChanging() before altering a Component's model object
and modelChanged() afterwards. For example, if your component has a
Model that wraps a List object, there is no way for Wicket to know if
you set the 3rd element to some new value. By calling modelChanging()
and modelChanged(), you can ensure that Wicket knows about your change.

Rendering

A Page renders itself by rendering its associated markup (the html
file that sits next to the Page). As MarkupContainer (the superclass
for Page) iterates through the markup stream for the associated
markup, it looks up components attached to the tags in the markup by
id. Since the MarkupContainer (in this case a Page) is already
constructed and initialized by onBeginRequest(), the child for each
tag should be available in the container. Once the Component is
retrieved, it's render() method is called.

If no component can be retrieved for a given id in the markup stream,
a chain of IComponentResolver objects in the Application is consulted.
If one of the resolvers can resolve the id, it may actually add a
component to the component hierarchy during rendering. However, such
an action is special purpose and internal to Wicket and
IComponentResolver is not intended for use beyond the existing
AutoComponentResolver resolver that handles <wicket:component> tags
and the AutoLinkResolver which creates automatic page links. The
component hierarchy changes that these resolvers make are handled
using special internal methods and should not be used as a general
extension point for modifying Pages during rendering, which is
illegal to do outside of the Wicket core.

Component.render() follows these steps to render a component:

1. Determine component visibility. If the component is not visible,
the RequestCycle's Response is changed to NullResponse.getInstance(),
which is a Response implementation that simply discards its output.

2. Component.onRender() is called to allow the Component's rendering
implementation to actually render the Component.

3. Any Component models are detached to reduce the size of the
Component hierarchy in case it is replicated across a cluster.

The implementation of onRender() can really be anything that writes to
the Response, but it typically is not overridden. The default
implementation of onRender() in MarkupContainer simply calls:

	protected void onRender()
	{
		renderAll(findMarkupStream());
	}

which renders the markup in the container. The implementation of
onRender() in WebComponent and WebMarkupContainer calls a different
method that is tuned to rendering reusable components rather than
containers full of arbitrary markup:

	protected void onRender()
	{
		renderComponent(findMarkupStream());
	}

The renderComponent method gets a mutable copy of the next Tag in the
markup stream for the component and calls onComponentTag(Tag) to allow
the subclass to modify the tag. Once the subclass has changed the
tag, it is written out to the Response with renderComponentTag(Tag)
and the markup stream is advanced to the next tag. Next
onComponentTagBody() is called, passing in the MarkupStream and the
ComponentTag that was written out as the opening tag. This allows the
component to do whatever it needs to do to produce a body for the
component tag. One operation the subclass can call in
onComponentTagBody() is Component.replaceComponentTagBody(), which
replaces the markup inside the component's body with an arbtirary
String. Finally, the framework writes out any appropriate closing tag
for the component's open tag.

Rendering to a string

For rendering a complete page to a string, see this article.

For rendering just a Panel to a string, see this entry.

  • No labels