Wicket's Event-Driven Model and PageMap
In keeping with the use case, it's time to move on to FileUploadPage. Override the default onSubmit method in Form:
protected void onSubmit()
FileUploadPage fileUploadPage = new FileUploadPage(getModel());
Wicket's event-driven, unmanaged nature is clear here; whatever page you add a FileInfoForm to, submitting that instance of FileInfoForm executes the event handler and sends the user to a newly constructed FileUploadPage. The model object is already populated with validated values at this point, and you pass it to the next page's constructor. This is a departure from other frameworks, which often use the session to marshal objects from one page to the next.
Wicket maintains a server-side cache of the object graphs that represent each page rendered to the user. This makes your application a true state machine, and effectively solves the classic back button problem. For example, after advancing to the FileUploadPage, the user can hit the back button and Wicket will deserialize and render a copy of the object that represents the FileInfoPage the user was just on. The FileInfoForm's model is part of the object graph of the page, and so Wicket deserializes it and binds it to the form with all the values last submitted by the user intact. This is a much more effective way to manage moving back and forth in multipage workflows than juggling session variables. (If this raises any red flags for scalability, read Sidebar 3.)
Wicket caches the object graph that backs each page rendered in a PageMap attached to the user's session (see Figure 3). These previously visited pages are eventually garbage collected, but in the meantime the user can access them with the back button, or by utilizing the workflow code to repeat previous steps.
|Figure 3. The Wicket PageMap:|
Wicket caches the object graph that backs each page rendered in a PageMap attached to the user's session.
File Upload with AJAX Progress Feedback
The "shouldAcceptInfoAndAdvance" test now passes, and users can get as far as the FileUploadPage. The last step is for them to be able to upload their files, which you specify with the following test:
public void shouldAcceptFileUpload()
FormTester formTester = wicketTester.newFormTester("fileUploadForm");
formTester.setFile("fileInput", TEST_UPLOAD_FILE, "image/jpeg");
// for simplicity we store the previously collected meta information in
// the file name.
String uploadedFilePath = TEST_UPLOAD_FOLDER.getAbsolutePath()
.concat( File.separator )
.concat("guest-Alpine Lakes Trail-hike+forest+alpine lakes.jpg");
java.io.File uploadedFile = new java.io.File(uploadedFilePath);
("File not deposited in upload folder or incorrectly named.",
Now you implement your second Form component: FileUploadForm. You can read the onSubmit event handler later, but for now take a closer look at the constructor and Wicket's AJAX support:
public class FileUploadPage extends BasePage
private class FileUploadForm extends Form
private FileUploadField fileUploadField;
public FileUploadForm(String id)
add(fileUploadField = new FileUploadField("fileInput"));
add(new UploadProgressBar("progress", this));
|Figure 4. AJAX the Wicket Way:|
Here is a rough overview of how Wicket implements AJAX.
The UploadProgressBar component in this example works in a somewhat involved manner, but it's still a good example of how Wicket can be extended to accommodate complex AJAX functionality without breaking encapsulation. Simply add the progress bar component to your form and configure the application to use UploadWebRequests in ExampleWicketApplication with this override:
protected WebRequest newWebRequest(HttpServletRequest servletRequest)
return new UploadWebRequest(servletRequest);