No real new functionality but the all admin pages are styled a bit differently using tabs. And yes, e-mail validation now allows .info TLD’s.

Tabbed admin pages

 

Here’s a screenshot refactored eHour timesheet:
Timesheet panel

As you can see some things changed with respect to the previous version. First of all there are now cumulations for every column and row as well making it easy to see how many hours were booked on a project or on a day.

The other change is that projects are now grouped per customer and there’s a ‘fold’ arrow allowing you to hide all projects from a customer. This setting is persistent over sessions so if you login a few days later the timesheet looks the same as you left it.

Comments field on a weekly basis is still in although I don’t know yet what to do with it as the comments don’t show up in reporting yet. It’s rather useless.

 

There are a couple of ways of integrating Spring with Wicket. If your WebApplications extends SpringWebApplication you already have access to the ApplicationContext for dependency retrieval. See this blog entry on how to proceed from there.

However in my case the WebApplication was extending AuthenticatedWebApplication and Spring beans were injected by adding a SpringComponentInjector to the ComponentInstantiationListener. Spring dependencies are then added by using the @SpringBean annotation.

So how to unit test pages using a @SpringBean annotation? Actually the solution ain’t that complex.
First of all in my main WebApplication I’ve moved registering the SpringComponentInjector to a seperate method:

public void init()
{
	..
	springInjection();
	..
}

protected void springInjection()
{
	addComponentInstantiationListener(new SpringComponentInjector(this));
}

For unit testing purposes I extended the WebApplication so I could provide a different injector and provided a method to retrieve the mock context:

public class TestEhourWebApplication extends EhourWebApplication
{
	private AnnotApplicationContextMock	mockContext;

	@Override
	protected void springInjection()
	{
		mockContext = new AnnotApplicationContextMock();
		mockContext.putBean("EhourConfig", new EhourConfigStub());		

		addComponentInstantiationListener(new SpringComponentInjector(this, mockContext));
	}

	public AnnotApplicationContextMock getMockContext()
	{
		return mockContext;
	}
}

AnnotApplicationContextMock is provided by Wicket and is quite a handy class as it allows you to register beans in the ApplicationContext on the fly.

Now with the TestWebApplication in place which uses a mock Spring context the only thing left is instantiating the WicketTester with this WebApp and fetching the AnnotApplicationContextMock from the WebApp so it’s available for unit tests. This is perfect stuff for a BaseTestCase:

public class BaseUITest extends TestCase
{
	protected WicketTester	tester;
	protected AnnotApplicationContextMock	mockContext;

	protected void setUp() throws Exception
	{
		TestEhourWebApplication webapp =  new TestEhourWebApplication();

		tester = new WicketTester(webapp);
		mockContext = ((TestEhourWebApplication)tester.getApplication()).getMockContext();
	}
}

Any extending TestCases now have access to the Spring’s context and can register any beans needed for the page creation. For instance mocking a ConfigService dependency (using easymock):

public class MainConfigTest extends BaseUITest
{
	public void testMainConfigRender()
	{
		ConfigurationService configService = createMock(ConfigurationService.class);
		mockContext.putBean("configService", configService);

		expect(configService.getConfiguration())
				.andReturn(new EhourConfigStub());

		replay(configService);

		tester.startPage(MainConfig.class);
		tester.assertRenderedPage(MainConfig.class);
		tester.assertNoErrorMessage();

		verify(configService);
	}
}

et voila !

 

Martijn Dashorst from the Wicket team just announced the release of Wicket 1.3.0 b2. Get it here or update your poms with

<dependency>
    <groupId>org.apache.wicket</groupId>
    <artifactId>wicket</artifactId>
    <version>1.3.0-beta2</version>
</dependency>
 

So far I’ve converted a couple of Struts actions & tiles to Wicket panels and I have to say, I’m starting to love Wicket more and more. Okay, on the Java side of things it leads to somewhat of a code explosion but I’d rather have more Java code than keeping JSP’s, Javascript, XML config files, TLD’s, ActionForms and Actions all in sync – manually. And compared to Struts or any other action based framework you can get real independent components without any hassle.
Wicket’s documentation is still lacking but with the help of the examples, the Wiki and the excellent mailinglist this shouldn’t be a long-term problem.

Anyway enough feathers and flowers, in my Struts app I had a tile with a Calendar and a tile with two tables displaying some data of the selected month. The Calendar tile fired an Ajax request when a previous or next month was clicked and the two tables updated accordingly.
In Wicket terms this means having a Calendar panel (CalendarPanel) and another Panel (TimesheetPanel) displaying the data for that month. To get the basics on how Ajax works with Wicket there are enough Ajax examples to get you started so let’s skip the very very basics.

First of all two links are added to CalendarPanel:

AjaxLink previousMonthLink = new ChangeMonthLink("previousMonthLink", -1);
previousMonthLink.add(new Image("previousMonthImg", new ResourceReference(CalendarPanel.class, "arrow_left.gif")));
add(previousMonthLink);

AjaxLink nextMonthLink = new ChangeMonthLink("nextMonthLink", 1);
nextMonthLink.add(new Image("nextMonthImg", new ResourceReference(CalendarPanel.class, "arrow_right.gif")));
add(nextMonthLink);

And the markup:

<a href="#" wicket:id="previousMonthLink"><img wicket:id="previousMonthImg" src="img/left.gif" border="0" /></a>
<a href="#" wicket:id="nextMonthLink"><img wicket:id="nextMonthImg" src="img/right.gif" border="0" /></a>

To control AjaxLink‘s behaviour there’s an ChangeMonthLink inner class extending AjaxLink:

private class ChangeMonthLink extends AjaxLink
{
	private	int monthChange;

	public ChangeMonthLink(String id, int monthChange)
	{
		super(id);

		this.monthChange = monthChange;
	}

	@Override
	public void onClick(AjaxRequestTarget target)
    {
		EhourWebSession session = (EhourWebSession)this.getSession();
		Calendar month = session.getNavCalendar();
		month.add(Calendar.MONTH, monthChange);
		month.set(Calendar.DAY_OF_MONTH, 1);
		session.setNavCalendar(month);

		((BasePage)getPage()).ajaxRequestReceived(target,
				CommonStaticData.AJAX_CALENDARPANEL_MONTH_CHANGE);
    }

	@Override
	protected IAjaxCallDecorator getAjaxCallDecorator()
	{
		return new LoadingSpinnerDecorator();
	}
}

The onClick method is invoked when the XmlHttpRequest lands back on the server. Since any parameters you passed when constructing are still available there’s no need to parse the request (or ActionForm for that matter).

Within the onClick method of the Calendar panel only some date manipulation is done. Deciding which Panel should be updated in the client’s browser is delegated to the Page containing the Calendar. If you want re-usable components you don’t want to make references from one component to the other when they’re on the same level in the Page hierarchy. Components could re-appear in another context when referenced panels aren’t available.

All my pages extend from BasePage so it’s a safe cast to BasePage. However the extending Page doesn’t know which Panel fired the Ajax request hence the need to supply an event type, AJAX_CALENDARPANEL_MONTH_CHANGE.

With the Calendar panel setup the only code left to do is making the Page refresh the relevant panels:

public class OverviewPage extends BasePage
{
	private CalendarPanel		calendarPanel;
	private	WebMarkupContainer	contentContainer;

	public OverviewPage(PageParameters params)
	{
		..
		calendarPanel = new CalendarPanel("sidePanel", user);
		calendarPanel.setOutputMarkupId(true);
		add(calendarPanel);		

		contentContainer = new TimesheetPanel("contentContainer", user, session.getNavCalendar());
		contentContainer.setOutputMarkupId(true);
	}

	@Override
	protected void ajaxRequestReceived(AjaxRequestTarget target, int eventType)
	{
		CalendarPanel newCalendarPanel = new CalendarPanel("sidePanel", user);
		newCalendarPanel.setOutputMarkupId(true);
		calendarPanel.replaceWith(newCalendarPanel);
		calendarPanel = newCalendarPanel;
		target.addComponent(newCalendarPanel);

		TimesheetPanel	panel = new TimesheetPanel("contentContainer", user, session.getNavCalendar());
		panel.setOutputMarkupId(true);
		contentContainer.replaceWith(panel);
		contentContainer = panel;
		target.addComponent(panel);
	}
}

As you can see replacing a Panel is easy. The calendarPanel was already added in in the constructor. In the ajaxRequestReceived method it’s replaced with a new one. To have it refresh call addComponent on the AjaxRequestTarget with the new panel as an argument and you’re all set! It’s that easy, no need to do anything in the TimesheetPanel as there’s no difference between constructing it from a Page or reconstructing it for an Ajax request.

Just make sure the same ID (sidePanel & contentContainer) is used for the replacement panel as was used in the original construction since you’re replacing a component in the Page’s hierarchy.

Two things are left. You have to call setOutputMarkupId(true) on components you want to have ajaxified. By setting this to true Wicket generates a unique ID to locate the Panel in the HTML DOM.

The other thing is the AjaxCallDecorator as seen in the AjaxLink code fragment. With an AjaxCallDecorator you can decorate the Javascript onClick event before and after the request was send. I use it to display a loading message while the request is being processed:

public class LoadingSpinnerDecorator implements IAjaxCallDecorator
{
	public CharSequence decorateOnFailureScript(CharSequence script)
	{
		return "document.getElementById('LoadingSpinner').style.visibility = 'hidden';" + script;
	}

	public CharSequence decorateOnSuccessScript(CharSequence script)
	{
		return "document.getElementById('LoadingSpinner').style.visibility = 'hidden';" + script;
	}

	public CharSequence decorateScript(CharSequence script)
	{
		return "document.getElementById('LoadingSpinner').style.visibility = 'visible';" + script;
	}
}

That’s it! If I compare this to my Javascript/Action/ActionForm/Tiles/JSP/JSTL/XML combo you might understand why my love for Wicket is growing :)

 

Now you would say, why blog about something as simple as doing a mouseover with Wicket and hiding/showing a row? Well because during this minor development some interesting things came up such as how to reference rows generated by Wicket’s ListView and how to get relative image paths in Javascript.

In the Struts app I’m converting I had a table with a couple of rows (AggregateRow), each row being clickable. When clicked an image in front of the AggregateRow should flip to an up or down arrow and a second row right underneath the AggregateRow – the SummaryRow – should display/hide. To see what I mean go to the eHour demo, login with user thies and password test. On the first page you see a table with Aggregated hours per project, that’s the one I’m converting right now.

Okay, the ingredients are a Panel (net.rrm.ehour.ui.panel.overview.projectoverview.ProjectOverviewPanel); some small Javascript source located in net.rrm.ehour.ui.panel.overview.projectoverview.js and the images for the mouseover. As the images are common for the whole application they’re in the /img root dir of the webapp.

The first thing to do is to get the image URL’s in the javascript file. With JSTL a <c:url /> tag would be fine but not in this environment.
For stylesheets Wicket automatically fixes the image paths but not for Javascript sources (which makes sense, how to tell the difference between a URL and just a literal ?).

The trick is to replace the image locations in the JS with variables and replace those variables with a PackagedTextTemplate.
As you can see in the javascript below the image locations are replaced with ${iconUpOn}, ${iconUpOff}, etc. variables:

function init()
{
	imgUpOn = new Image();
	imgUpOn.src = '${iconUpOn}';
	imgUpOff = new Image();
	imgUpOff.src = '${iconUpOff}';
	imgDownOn = new Image();
	imgDownOn.src = '${iconDownOn}';
	imgDownOff = new Image();
	imgDownOff.src = '${iconDownOff}';
}

During the Panel setup the location of the javascript is passed to a PackagedTextTemplate and the variable names along with their values are put in a Map. The TextTemplateHeaderContributer takes care of replacing the variables in the Javascript with the values from Map and inserting the result in the header of the HTML:

CharSequence iconUpOn = getRequest().getRelativePathPrefixToContextRoot() + "img/icon_up_on.gif";
CharSequence iconUpOff = getRequest().getRelativePathPrefixToContextRoot() + "img/icon_up_off.gif";
CharSequence iconDownOn = getRequest().getRelativePathPrefixToContextRoot() + "img/icon_down_on.gif";
CharSequence iconDownOff = getRequest().getRelativePathPrefixToContextRoot() + "img/icon_down_off.gif";

PackagedTextTemplate js = new PackagedTextTemplate(ProjectOverviewPanel.class, "js/aggregate.js");

Map<String, CharSequence> map = new HashMap<String, CharSequence>();
map.put("iconUpOn", iconUpOn);
map.put("iconUpOff", iconUpOff);
map.put("iconDownOn", iconDownOn);
map.put("iconDownOff", iconDownOff);

add(TextTemplateHeaderContributor.forJavaScript(js, new Model((Serializable)map)));

As the Javascript initialization is done in a init() method a <body onLoad=”init()” > is needed to execute it after the page is rendered. Although this onload event is in the Panel markup it’s copied to the ‘main’ body tag when rendered. Just make sure you have a <wicket:head> section in your Panel markup otherwise it won’t be copied.

Now that the Javascript stuff is added the only thing left is to hide/display the second row when the AggregateRow is clicked. To have the AggregateRow, SummaryRow and the image find eachother in the client-side DOM an id attribute needs to be added to each element.

Adding an id is easily done with an AttributeModifier. As the name says you can modify attributes of a tag with this class.

For instance, adding an id to the image:

ContextImage img = new ContextImage("foldImg", new Model("img/icon_down_off.gif"));
img.add(new AttributeModifier("id", true, new AbstractReadOnlyModel()
{
	public Object getObject()
	{
		return "foldImg_" + item.getIndex();
	}
}));

And the corresponding markup:

<img src="[replaced]" onmouseover="onMouseOver(this)" onmouseout="onMouseOut(this)" wicket:id="foldImg" />

By the way, ContextImage is handy; it replaces the src attribute of an image tag with the relative path for an image.

And doing the same with the SummaryRow:

WebMarkupContainer summaryRow = new WebMarkupContainer("summaryRow");
summaryRow.add(new AttributeModifier("id", true, new AbstractReadOnlyModel()
{
	public Object getObject()
	{
		return "summaryRow_" + item.getIndex();
	}
}));
summaryRow.add(new SimpleAttributeModifier("style", "display: none"));

And the markup:

<div class="SummaryRow" wicket:id="summaryRow">
	[additional information row]
</div>

Take note that a WebMarkupContainer is used merely as a placeholder for the div. Also a second AttributeModifier is added to set the style to display:none as I want the SummaryRows to be initially hidden. Although each Wicket Component has a ‘visible’ property this is different than the Javascript’s visible property. If you set visible to false for a Wicket componenet it’s not rendered at all.

Now that the div’s and image have proper id attributes and images have relative URL’s in the javascript writing the final Javascript is easy.
For full source check out revision 438 and 439.

 

On archive.org I found a screenshot of the first site I ever made, RAW Online anno 1996 :) Funny to see how things evolved since then.

Even found my first applets made in ’97

 

In the ongoing Struts/JSTL conversion it was time for converting the <fmt:formatNumber JSTL tags I used for formatting currencies and floats.

For currency and float formatting I first needed access to the eHour config. Preferred Locale and currency are configurable in my app and exposed through a Spring managed Config class. Rather than injecting a new instance of the Config per request I wanted to have it in the Session. Although the config can be changed on runtime I don’t expect it to change that frequent.

Within Wicket you don’t have straight access to the HttpSession. Instead you provide your own implementation of WebSession and register it in your EhourWebApplication. The good thing is that you have a clear view of what’s in your session rather than messing around with static lookups and it’s type safe, you don’t have to cast around:

public class EhourWebSession extends WebSession
{
	@SpringBean
	private EhourConfig	ehourConfig;

	public EhourWebSession(WebApplication app, Request req)
	{
		super(app, req);

		InjectorHolder.getInjector().inject(this);
	}

	public EhourConfig getEhourConfig()
	{
		return ehourConfig;
	}
}

One thing of interest is the InjectHolder. Only classes that extend WebPage (could be Panels as well, am not sure) got automatic IoC. If you want to have injection in your POJO’s call the InjectHolder from your constructor.

To register the session, in your WebApplication class override getSessionFactory:

public ISessionFactory getSessionFactory()
{
	return new ISessionFactory()
	{
		public Session newSession(Request req, Response res)
		{
			return new EhourWebSession(EhourWebApplication.this, req);
		}
	};
}

From a Page or Panel you can get a reference to the session with EhourWebSession session = (EhourWebSession)getSession();

Ok, back to what I was trying to do in the first place; format currencies and floats properly. The idea is to use a standard Label and feed it a model which takes care of converting the value to a String. When I compare it to JSTL the good thing is that the conversion is done in pure Java so any NumberFormatter instance can be used.

For source, check here

 

Finally it’s time to get some data into a panel. In my Struts app I have an overview page which consists of two tiles. One tile is displaying a list with all hours booked per project in a month and the other tile shows an overview on a per-day basis of that month.

My Wicket plan is to create two panels, one for the list and the other for the month overview. The reason I’m creating separate panels – and not adding everything from the Page class – is that I want the flexibility to swap in different panels for different versions of the application.

To get the data I need two parameters, the user id and the month to view. The user id should be the authenticated user but as I haven’t added authentication yet I’m just adding it as a request parameter. In Struts you would fetch request parameters from the HttpServletRequest, in Wicket you add them to the constructor.
Now the constructor seems like a weird place but Wicket creates an instance per User session. For more info check the first chapter of Pro Wicket which goes into the life cycles of a Page.

public OverviewPage(PageParameters params)
{
	// ..
	int userId;

	userId = params.getInt("userID");

        // ..
}

Take note, as with Struts’ ActionForms, using a Form is way better but as this is just temporarily a simple request param would do.

Now on to displaying the data. For displaying the list with projects I’ve created a separate ProjectOverviewPanel. The constructoraccepts an id it should register on and the collection to list. All it does right now is adding a ListView component to the object graph:

public ProjectOverviewPanel(String id, Collection<UserProjectStatus> projectStatusSet)
{
	super(id);

	ListView view = new ListView("projectStatus", new ArrayList<UserProjectStatus>(projectStatusSet))
	{
		public void populateItem(ListItem item)
		{
			UserProjectStatus projectStatus = (UserProjectStatus) item.getModelObject();
			item.add(new Label("projectName", projectStatus.getProjectAssignment().getProject().getName()));
			item.add(new Label("projectCode", projectStatus.getProjectAssignment().getProject().getProjectCode()));
		}
	};

	add(view);
}

It’s rather straightforward, create a ListView component and feed the id and List it should act on. I’m explicitly creating an ArrayList object since ListView can only operate on Lists (duh..) and not on Sets.
On every iteration populateItem is called with a ListItem. ListItem is a wrapper around the object contained in the original list. Every property is then added as a Label to the item.
The relevant markup of the panel:

<span wicket:id="projectStatus">
	<tr>
		<td><span wicket:id="projectName">projectName</span></td>
		<td><span wicket:id="projectCode">projectCode</span></td>
		<td><span wicket:id="customerName">customerName</span></td>
	</tr>
</span>

As you can see the outer span’s id reflects the id with which the ListView was added while the inner id’s reflect are the ones used in the Item objects.
For now I’ve only added String labels as I haven’t looked into displaying currencies and floats. Another thing I have to look into is session management. As you can see from the projectStatus.getProjectAssignment().getProject().getName() the List I’m displaying is a couple of levels object graph. Wicket caches data in the session and I definitely don’t want one-off graphs like this in the user’s session.

Code changes can be found here.

 

After a painful server crash last week (and a successful recovery) it’s finally time to get back to coding.

I left off with including images and stylesheets in my MainNavPanel. Right now I’ve got a SidePanel class and markup which provides a rounded border and a CalendarPanel extending the SidePanel providing the content.

The next step is adding some dynamic data to the pages. Question was where to get the data from… I’m writing enough mock stuff at work so the first thing was getting real data from my Spring service project. Take note, the app I’m converting was already split up in a web module and a service module.

At the Wicket wiki a couple of solutions are listed on how to integrate Spring. Since I don’t want to create proxy objects for every dependency or do Servicelocator alike lookups in my application class I went for the annotation approach.

Actually the whole thing is quite easy and consisted of not more than:

1. Adding wicket:wicket-spring-annot dependency to my Maven 2 pom

<dependency>
    <groupId>wicket</groupId>
    <artifactId>wicket-spring-annot</artifactId>
    <version>1.2.6</version>
</dependency>

2. Add Spring’s ContextLoaderListener plus service applicationContext.xml’s to the web.xml. Same stuff as I did in my Struts app

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:applicationContext-dao.xml
		classpath:applicationContext-datasource.xml
		classpath:applicationContext-service.xml
		classpath:applicationContext-mail.xml
		classpath:applicationContext-wicket.xml
	</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

3. Create a seperate applicationContext for wicket with (the applicationContext-wicket.xml)

<beans>
         <bean id="wicketApplication" class="net.rrm.ehour.ui.EhourWebApplication"></bean>
</beans>

4. Make EhourWebApplication Spring aware by adding the injector to the init()

addComponentInstantiationListener(new SpringComponentInjector(this));

With this in place injecting a dependency was easy, for example in my OverviewPage class it’s just a matter of annotating a property as a SpringBean et voila, I could call methods on my service.

@SpringBean
private	UserService	userService;

It’s so simple there should be some caveats to it but I’ll guess I’ll found that out tomorrow (I suspect it has something to do with OpenSessionInViewFilter :) )

Code changes so far can be seen here.

© 2011 Thies' blog Suffusion theme by Sayontan Sinha