This chapter explains how the previously explained concept is applied to Wicket. It expects the reader to be familiar with Wicket core concepts and shows how they are extended and improved by this framework.
2.1) How does it influence the daily routine?
For starters, let us answer this important question. The following activity diagram shows how you go around to get your work done:
NoWicket Development Workflow
You might remember reading something similar to this in the introduction page, where we highlighted the benefits of the framework in your daily live. So instead of repeating ourselves, we will just extend that information and show how you go about it from a practical perspective.
Implement Intelligent Model
After you have defined what your view should look like and what behavior it is supposed to have, translate that concept into one or more
model classes that represent your ideas. The following tools are available here:
- Use property and action methods to define your user interactions. Everything public will be picked up by the generator, though you can use the @Hidden annotation to hide even public things. Missing setters for properties or null values will automatically result in disabled view components.
- The GuiService class allows you to display messages, show modals, do extended internationalization and do page redirects from anywhere without making your model code aware of any Wicket specific details.
- Add BeanValidation (JSR-303 and JSR-349) annotations to your property elements to keep your models clean from invalid user inputs. For more complex scenarios, write custom validators that intercept new values coming in from the UI and validate against other existing values from your model.
- Add utility methods for dynamic hide, disable, tooltip and titles to enhance your view logic. You can also access the Wicket RequestCycle, WebApplication and WebSession from anywhere. The Roles class allows you to check user permissions from anywhere.
- Throw checked exceptions with messages from your action elements to show the user default error dialogs. You can also secure actions via the @PreAuthorize annotation to allow them only for specific user roles and show also an error dialog on violation.
- Return other models from your action elements to cause a redirect there, or return null to prevent that.
- The default interaction scenario on the web is lazy form submission in order to save bandwidth and server load. To change this and make better use of AJAX, you can add the @Eager annotation where desired to give the user immediate feedback after he changes properties. The framework will automatically register onchange listeners for you and handle the page submit.
- It is useful to extend AValueObject in your models, since it offers a DirtyTracker with which it is easy to check for model changes and warn the user about data loss when redirecting without saving.
- Implement hashCode and equals methods for your model to control when the framework should reuse page instances after redirects or direct browser access.
- You can also write headless unit tests on those models using a mocked GuiService and the WicketTester (only for providing the Wicket context, not for actual rendering) to validate your model behavior.
Implement Wicket Page/Panel Class with GeneratedBinding Call
For each of your model classes, create a corresponding Wicket page or panel class that calls the
GeneratedBinding inside its constructor. This will process the HTML file using the
Visitor Pattern, find all Wicket IDs, match them against the model elements and instantiate components for them while creating a dynamic component tree according to the markup hierarchy. Despite this automated binding, you normally don't have to do much more on the Wicket side of things. But if you need to after all, you can extend this by the following means:
- Register a BindingInterceptor with which you can substitute default components by specific Wicket IDs. When you have added more Wicket IDs to the HTML than represented in the model class, you can bind them using this mechanism too. Otherwise Wicket would complain about missing components at runtime. All component classes follow the convention used by Wicket to be designed for extension and thus allow you to change everything about them.
- You can even register default BindingInterceptors for your whole web application in the GeneratedBindingDefaults class. There you can also change the error handling for action elements which normally shows a message box on checked exceptions.
- Make use of the various registries for HTML components, model elements and so on to get your metadata and generic binding for the model, or just use the model object directly and hard code the binding. You can access the dynamic model state (enabled, hidden, tooltips, titles) from the elements those registries contain and call getters/setter in a nullsafe fashion.
- Do anything you are used to from Wicket, like inheriting from a base page, handling page parameters, doing crazy component hierarchy manipulation, adding behaviors, dynamically modify HTML tags or CSS classes.
- Use I18nModel to reference internationalization properties dynamically, or do the normal Wicket internationalization things.
- Use the @MountPath annotation to beautify the bookmarked URL or use @AuthorizeInstantiation to secure the page for specific user roles.
- Register new pages in your website navigation menu inside the base page you created, which should extend AWebPage. You can also change the page format from container to container-fluid there, add tracking code to your pages, provide a locale switcher, modify the themes or change the AJAX busy indicator (AjaxIndicatorAppender) instance used.
- Change page processing settings in the web application using a WebApplicationInitializer override in your IWebApplicationConfig. You can also customize the session, the favicon, home page, authentication manager, error pages and sign in/sign out pages in the config class. Override the BootstrapExtensionsHeaderContributor to remove features like autofocus, enter keyboard binding, allowing copy/paste on disabled inputs, disabling components during AJAX calls and retrying AJAX calls on connection loss with Offline.js (useful for mobile connections).
- We separated the IWebApplicationConfig from the WebApplication itself to allow reuse of web.xml configurations, which need to have a fixed path to your WebApplication class. This eases configuration in a multi module project structure and allows you to replace the config class dynamically using a dependency injection context if desired.
Run NoWicket Generator
Run the main or test class you have setup for the generator. It processes your model classes using the
Visitor Pattern and generates the HTML and properties files for them. If those files already exist, the generator will merge new things into those files at the end without touching the things you edited. The generator will never remove things from the output files, if you refactor your model, you might have to refactor your HTML Wicket IDs as well or remove old things to regenerate them. You can use the following variations of the generator:
- SpecifiedGeneratedMarkup: Use this class to define manually what model classes to process from where and provide the destination folder for the generated files.
- SpecifiedGeneratedMarkupMain: A command line wrapper around the SpecifiedGeneratedMarkup so you can integrate it with your build tools.
- AAnnotatedGeneratedMarkup: Extend this one for the most convenient way to generate markup. The generator will scan your given base packages and process all model classes annotated with @GeneratedMarkup. This generator will delegate the work to SpecifiedGeneratedMarkup after collecting all information.
Modify HTML and Properties
Edit the generated files:
- Adjust the order of components and put everything where it belongs in the bootstrap grid layout. You can also use WYSIWYG tools, just add a reference to the bootstrap CSS in the file header if desired. Or remove all bootstrap tags and go back to the table layout if desired.
- Modify tags to change the components that should appear in the website. The GeneratedBinding will adjust itself automatically as needed and as logically possible.
- Add the btn-primary CSS class to the button you want to get a keyboard binding on the enter button.
- Put the autofocus attribute to an input to change the default autofocus behavior. Add it to a hidden input to disable autofocus.
- Remove unneeded tags or add your own Wicket IDs that you want to bind manually using a BindingInterceptor.
- Internationalize the markup by adding wicket:message tags and enhance the properties file as you need. Duplicate the properties file for other locales. You could also add more property elements to your model class for internationalization purposes.
- Uncomment the feedback tag to get one place for validation messages. Remove the gridcolumn tags around your inputs to prevent validation messages from appearing inline.
Start Web Server and View Page
To verify that your page renders properly and that it behaves as expected, there is no way around manually checking it in the browser. Start an
embedded web server with Jetty or Tomcat, integrate them via their
maven plugins or deploy your application to a test server.
- You could also use JRebel or at least use the hot code replacement in the debug mode of your IDE to reduce the amount of restarts you need while working on the web application. Also use the development config of Wicket to reduce the amount of caching done. If the page cache hinders you, reset your session cookie to get a new session without restarting the application. This can be done either by deleting cookies in your browser, or by signing out and signing in again in the application.
- With a Wicket deployment config the website will automatically minify the HTML, CSS and JavaScript resources and do lots of other performance optimizations.
- The application also handles web crawlers Search Engine Optimization (SEO) friendly by reverting to one pass rendering for agents that are identified as bots to prevent redirect confusion in them.
Page Looks and Works as Desired?
Now it is time to try out the website and get a feel for it.
- Do your usability checks. Verify that buttons, modals, validations, behaviors, AJAX and status messages work as expected. Check if you are missing some of the requirements or did not implement them correctly.
- Test different browsers to see that bootstrap and modernizr do a good job at abstracting away inconsistencies. Verify that the layout is responsive on smaller screens.
- Try to disable JavaScript in your browser to see if the common Wicket AJAX fallback functionality works. Where possible, the framework automatically binds AJAX components that have fallbacks, though not everything works this way. Things that won't work are CSS based icons, dropdown buttons and modals since they are dependant on JavaScript. But things like button clicks and basic navigation will still work, which should be enough for most bots to index your website properly.
- If you find something odd, just go back a few steps to modify/extend/refine the appropriate files, rerun the generator if needed and check again. Sometimes this is just a matter of trial and error in a feedback loop. As soon as you are satisfied, go grab a beer with some friends and tell them how satisfying working with Wicket can be.
2.2) Does it change Wickets internal processing?
Not at all! Wicket still behaves as usual and all artifacts you are used to are still present in your project. Wickets internal processing is demonstrated by the following graphic:
Wickets Internal Processing
We will explain next at which steps the NoWicket framework is active during Wickets internal processing:
- Request:
As usual the web browser asks the Wicket application to retrieve a page. The Wicket application was setup using the NoWicket AWebApplication base class and is thus configured according to your IWebApplicationConfig. If no session is present yet, it creates a new one for the client.
- Lookup:
Then Wicket checks its page store, if it finds a page, it returns it directly. The addition here is that AWebApplication adds its own model aware session page cache on top of the page store. So if a bookmarkable URL was requested for which a new model was created, but that created model matches in equality to a model from a previous page in the page store, then that previous page instance is served instead of returning a new page. The user will still see his UI state as it previously was. So for example sorting information in a data table will be the same as previously, data paging will still be on the old page and validation messages will still be there. This transforms dumb links (ones without the ?{ID} parameter) into page store aware links.
- Locate:
If the page store was empty, Wicket locates the appropriate page class to be served.
- Parse:
Then it parses the requested HTML template and sees what Wicket IDs need to be provided as components by the page class.
- Instantiate:
And instantiates the page class to combine both. If the page was requested from a bookmarkable link, then the page constructor without a model argument will be used, where you will be responsible to create a new model instance. Otherwise if this was an internal redirect based on a model, then the page will be instantiated with a model constructor where you will be responsible to use it since it might be specially configured from the redirecting code. Remember from above that action elements in models and code using the GuiService can do model based redirects.
- Bind:
The page instance will then build its component hierarchy corresponding to the HTML template and bind values from the model into the HTML placeholders. Here the GeneratedBinding takes place if you put it into the constructor (otherwise you do it the classic Wicket way). The binding might add other panels into the hierarchy which also have their corresponding markup and initializations with a separate GeneratedBinding taking place. In each case separate BindingInterceptors and component modifications might be used.
- Store:
When everything between the HTML and the model is wired up, Wicket will put the new page instance into the page store for future lookups.
- Render:
Then it will render the HTML document and put in the values from the model that were wired.
- Respond:
And finally it answers the browser with the content requested.