Skip to end of metadata
Go to start of metadata

Bookmarkable link

Icon
  • Wicket-spring project is in the subversion alongside wicket.
  • Please read on for some Maven tips.
  • Also look at wicket-examples org.apache.wicket.spring packages for examples.
  • Some knowledge of spring is required.
  • In wicket the Page class is a subclass of Component, so from here on the word component can represent either a component or a page.

The Issues

Most problems with injecting dependencies from an IOC container come from the following

  1. wicket is an unmanaged framework
  2. wicket components and models are often serialized

Wicket is an unmanaged framework

Wicket does not manage the lifecycle of its components. This means that a page or a component can be created anywhere in the code by simply using the new operator. This makes it difficult to inject dependencies because it is difficult to intercept the creation of the component. A possible solution can be to use a singleton factory to create the components and subsequently inject them with dependencies. However, this approach is not very flexible because it is often more convenient to have specific constructors in components rather than the default empty constructor. ie

is far more convenient than

Wicket components and models are often serialized

Wicket keeps its tree of components in the session. In a clustered environment, session data needs to be replicated across the cluster. This is done by serializing objects in a cluster-node's session and deserializing them on another cluster-node's session. This presents a problem for dependency injection because it is not desirable to serialize the dependency. Dependencies often have references to other dependencies in the container, and so if one is serialized it will probably serialize a few others and can possibly cascade to serializing the entire container. To say the least, this is undesirable. Even if the cascading is not a problem and the dependency is serialized, when it deserializes it will no longer be part of the conainer - it will be a stand alone clone. This is also undesirable.

Solutions

Application Object Approach

Wicket applications have a global application object which is a subclass of Application. This global application object is only created once per application and is never serialized (since it contains no user-specific data and thus remains the same across all nodes in the cluster). These qualities make it a good candidate to act as a service locator for the rest of the application. Wicket allows you to provide a custom factory for creating this object, the wicket-contrib-spring project provides such a factory (SpringWebApplicationFactory) that, instead of creating an instance, pulls it out of the spring application context. Wicket keeps the instance of the application object in a threadlocal variable and provides various helper methods in components to get to it, so it is easy to retrieve dependencies in wicket components.

Example:

web.xml:

applicationContext.xml:

code:

Pros:
*Simple
*Avoids serialization problem by never storing dependencies in components
Cons:
*Application class might get cluttered if the application has a lot of dependencies
*It is far too easy to accidentally keep a reference of the dependency and thus run into the serialization problem

Proxy-based Approach

It is also possible to create a dynamic proxy for the dependency that can be serialized and deserialized safely. The proxy would need to contain just enough information to lookup the dependency when needed, yet be small enough to not have a high impact on session size. This proxy will be known as a LazyInitProxy for the rest of the article. This solves the serialization problem, but not the injection problem. Later we will see how we can work around the injection problem. wicket-contrib-spring provides tools to create the proxies we need.

LazyInitProxy is very simple as far as proxies go. The gist of its invocation handler looks like this:

The only piece of information this proxy needs to have is the implementation of lookupTarget() method. For this the wicket-contrib-spring project provides the IProxyTargetLocator. This interface is very simple:

LazyInitProxyFactory is the factory class that can generate a lazy init proxy given a proxy target locator and the proxy target's class. If the class is an interface the factory will generate a dynamic jdk proxy, otherwise a cglib proxy will be created.
Example:

Here our application class extends the SpringWebApplication class provided by wicket-contrib-spring. This subclass of WebApplication implements the spring ApplicationContextAware interface to provide our application with an easy way to get to spring context.

The problem with the above is its verbose nature. For every dependency you have to create the proxy and an object locator. To ease the pain wicket-contrib-spring provides the Injector class. This class can automatically inject dependencies into our application objects using an implementation of IFieldValueFactory interface. Injector.inject(Object object, IFieldValueFactory fieldValueLocator) will go through all of the fields of the object argument and assign to it the value it gets from the IFieldValueFactory.getFieldValue(Field field, Object fieldOwner) call. Using this class we can implement IFieldValueFactory that can create the proxies for us using some kind of a metadata associated with a field. The provided AnnotProxyFieldValueFactory class in the wicket-contrib-spring-jdk5 project does just that using SpringBean jdk5 annotation. The @SpringBean annotation can be found in wicket-spring-annot.
Example:

The above create factory method creates the page and injects it with dependencies. This has most of the advantages of ioc without actually being ioc.

Annotation-based Approach

Icon

Please note that you cannot use constructor argument based injection with Wicket, only setter-based injection!

It's possible to have your annotated dependencies automatically injected on construction. For this you have to install a SpringComponentInjector in your application.

Example:

With Wicket >1.5 it is:

Here the page (or indeed anything derived from a Wicket Component) will have its dependencies injected when created. [Constructor/superclass chaining down to the Component(final String id, final IModel model) constructor, where there's a call to getApplication().notifyComponentInstantiationListeners(this);]

When doing this it is important to remember not to initialize dependencies, to null or any other value, e.g.private ContactDao dao=null;. Don't do this because the injector will run before the subclass initializes its fields, and so the dao=null will override the created proxy with null.

Using annotation-based approach, you should not worry about serialization/deserialization of the injected dependencies as this is handled automatically, the dependencies are represented by serializable proxies. Also, you should not mark your dependency properties transient because if you do so, they won't be re-initialized upon deserialization.

wicket-spring-annot project provides the SpringComponentInjector class for you. All you have to do to get transparent injection working is to install the injector in your application as shown above. SpringComponentInjector also supports automatic injection of wicket portlet apps.

Icon

Using the annotations approach requires that the servlet security manager you are using allows Reflection API calls, so you might have to change your security manager policy for the application.

Unit Testing the Proxy Approach

Even when using automatic injection, unit testing is easy. Following is a sample unit test that tests a fictional DeleteContactPage class.

Part 1 is the standard setup of the dependencies required to run the test. This particular test uses EasyMock library to make working with mock objects easier.

Part 2 is where we setup a mock spring environment. We setup a mock application context using the ApplicationContextMock object and add all beans necessary for the test. We also create the injector we will use to inject objects in this test.

Part 3 is the setup of WicketTester and the SpringComponentInjector, which will inject our dao into classes which have the @SpringBean annotation

Part 4 is the test itself. We go through a setup of the page and a submission of its form.

If you are using the annotations package to inject your pages, the testcase above can be further simplified by the use of AnnotApplicationContextMock that performs all the necessary wiring that is required to install the AnnotSpringInjector into the environment.

Example

If you are using a AuthenticatedWebApplication along with a SpringInjector, you can configure the WicketTester this way:

As you can see, the use of the AnnotApplicationContextMock removes some noise from the test case.

Unit Testing Proxy Approach with Custom Session

When unit testing a Wicket application with a custom session and with Spring bean dependencies, it best to create an mock implementation of a WebApplication, overriding the newSession method, and passing this to the WicketTester. Here is an exampleL

To expand on the WicketTester/Spring integration, many web applications are developed on J2EE compliant servers and use the server based datasources and connection pooling accessed through a JNDI call. The datasource/connection pool is then centralized in a Spring configuration file as a spring bean. The implication on testing is that this bean is invalid since there is no JNDI service; hence a local datasource/connection pool bean is substituted. Once the ant script or manual method is defined to substitute the datasource bean the application context can be integrated as follows:

Beyond Spring

This technology can also be used to inject non-spring dependencies like JNDI or EJB3 beans. All it takes is a simple implementation of IFieldValueFactory and IProxyTargetLocator.

Using @SpringBean beyond Wicket

See SpringBean outside Wicket.

Getting wicket-spring with Maven 2

If you include wicket-spring as a dependency you will also get the full spring 2.0 jar. This is undesirable if you already have the smaller Spring jars on your dependency list.

As far as I known, wicket-spring only needs spring-core.jar. Here is an example that includes spring-core.jar (2.5.3) and the wicket-spring jars (1.3.3):

Labels