Appendix B: Project WicketStuff

B.1. What is project WicketStuff

WicketStuff is an umbrella project that gathers different Wicket-related projects developed and maintained by the community. The project is hosted on GitHub at https://github.com/wicketstuff/core. Every module is structured as a parent Maven project containing the actual project that implements the new functionality and an example project that illustrates how to use it in our code. The resulting directory structure of each module is the following:

  1. \<module name>-parent
  2. |
  3. +---<module name>
  4. \---<module name>-examples

In order to enjoy extra components, utilities and/or functionality introduced by WicketStuff modules in our Wicket projects, we can import the respective module dependency in our pom.xml as shown below:

  1. <dependency>
  2. <groupId>org.wicketstuff</groupId>
  3. <artifactId>wicketstuff-<module name></artifactId>
  4. <version><wicketstuff version></version>
  5. </dependency>

where is the version of WicketStuff artifact (e.g. 8.0.0-SNAPSHOT), and corresponds to the name of WicketStuff module we want to use. As an illustration, to have access to Java 8 lambda style ComponentFactory methods for adding Links or AjaxButtons to our pages, the following dependency declaration will suffice:

  1. <dependency>
  2. <groupId>org.wicketstuff</groupId>
  3. <artifactId>wicketstuff-lambda-components</artifactId>
  4. <version>8.0.0-SNAPSHOT</version>
  5. </dependency>

Please refer to Appendix B.7 for more details about Lambda Components.

So far we have introduced only modules Kryo Serializer and JavaEE Inject, but WicketStuff comes with many other modules that can be used in our applications. Some of them come in handy to improve the user experience of our pages with complex components or integrating some popular web services (like Google Maps) and JavaScript libraries (like TinyMCE).

This appendix provides a quick overview of what WicketStuff offers to enhance the usability and the visually-appealing of our pages.

Every WicketStuff module can be downloaded as JAR archive at http://mvnrepository.com . This site provides also the XML fragment needed to include it as a dependency into our pom.xml file.

B.2. Module tinymce

Module tinymce offers integration with the namesake JavaScript library that turns our “humble” text-areas into a full-featured HTML WYSIWYG editor:

tinymce

To “tinyfy” a textarea component we must use behavior TinyMceBehavior:

  1. TextArea textArea = new TextArea("textArea", new Model(""));
  2. textArea.add(new TinyMceBehavior());

By default TinyMceBehavior adds only a basic set of functionalities to our textarea:

tinymce basic

To add more functionalities we must use class TinyMCESettings to register additional TinyMCE plugins and to customize the toolbars buttons. The following code is an excerpt from example page FullFeaturedTinyMCEPage:

  1. TinyMCESettings settings = new TinyMCESettings(
  2. TinyMCESettings.Theme.advanced);
  3. //...
  4. // first toolbar
  5. //...
  6. settings.add(Button.newdocument, TinyMCESettings.Toolbar.first,
  7. TinyMCESettings.Position.before);
  8. settings.add(Button.separator, TinyMCESettings.Toolbar.first,
  9. TinyMCESettings.Position.before);
  10. settings.add(Button.fontselect, TinyMCESettings.Toolbar.first,
  11. TinyMCESettings.Position.after);
  12. //...
  13. // other settings
  14. settings.setToolbarAlign(
  15. TinyMCESettings.Align.left);
  16. settings.setToolbarLocation(
  17. TinyMCESettings.Location.top);
  18. settings.setStatusbarLocation(
  19. TinyMCESettings.Location.bottom);
  20. settings.setResizing(true);
  21. //...
  22. TextArea textArea = new TextArea("ta", new Model(TEXT));
  23. textArea.add(new TinyMceBehavior(settings));

For more configuration examples see pages inside package wicket.contrib.examples.tinymce in the example project of the module.

B.3. Module wicketstuff-gmap3

Module wicketstuff-gmap3 integrates Google Maps service with Wicket providing component org.wicketstuff.gmap.GMap. If we want to embed Google Maps into one of our pages we just need to add component GMap inside the page. The following snippet is taken from example page SimplePage:

HTML:

  1. ...
  2. <body>
  3. <div wicket:id="map">Map</div>
  4. </body>
  5. ...

Java code:

  1. public class SimplePage extends WicketExamplePage
  2. {
  3. public SimplePage()
  4. {
  5. GMap map = new GMap("map");
  6. map.setStreetViewControlEnabled(false);
  7. map.setScaleControlEnabled(true);
  8. map.setScrollWheelZoomEnabled(true);
  9. map.setCenter(new GLatLng(52.47649, 13.228573));
  10. add(map);
  11. }
  12. }

The component defines a number of setters to customize its behavior and appearance. More info can be found on wiki page https://github.com/wicketstuff/core/wiki/Gmap3 .

B.4. Module wicketstuff-googlecharts

To integrate the Google Chart tool into our pages we can use module wicketstuff-googlecharts. To display a chart we must combine the following entities: component Chart, interface IChartData and class ChartProvider, all inside package org.wicketstuff.googlecharts. The following snippet is taken from example page Home:

HTML:

  1. ...
  2. <h2>Hello World</h2>
  3. <img wicket:id="helloWorld"/>
  4. ...

Java code:

  1. IChartData data = new AbstractChartData(){
  2. public double[][] getData(){
  3. return new double[][] { { 34, 22 } };
  4. }
  5. };
  6. ChartProvider provider = new ChartProvider(new Dimension(250, 100), ChartType.PIE_3D, data);
  7. provider.setPieLabels(new String[] { "Hello", "World" });
  8. add(new Chart("helloWorld", provider));

Displayed chart:

googlechart

As we can see in the snippet above, component Chart must be used with Appendix B: Project WicketStuff - 图4 tag while the input data returned by IChartData must be a two-dimensional array of double values.

B.5. Module wicketstuff-inmethod-grid

Module wicketstuff-inmethod-grid implements a sophisticated grid-component with class com. inmethod.grid.datagrid.DataGrid.

Just like pageable repeaters (seen in paragraph 13.4) DataGrid provides data pagination and uses interface IDataProvider as data source. In addition the component is completely ajaxified:

inmethod grid1

DataGrid supports also editable cells and row selection:

inmethod grid2

The following snippet illustrate how to use DataGrid and is taken from wiki page https://github.com/wicketstuff/core/wiki/InMethodGrid :

HTML:

  1. ...
  2. <div wicket:id="grid">Grid</div>
  3. ...

Java code:

  1. final List<Person> personList = //load a list of Persons
  2. final ListDataProvider listDataProvider = new ListDataProvider(personList);
  3. //define grid's columns
  4. List<IGridColumn> cols = (List) Arrays.asList(
  5. new PropertyColumn(new Model("First Name"), "firstName"),
  6. new PropertyColumn(new Model("Last Name"), "lastName"));
  7. DataGrid grid = new DefaultDataGrid("grid", new DataProviderAdapter(listDataProvider), cols);
  8. add(grid);

In the code above we have used convenience class DefaultDataGrid that is a subclass of DataGrid and it already comes with a navigation toolbar.

The example pages are under package com.inmethod.grid.examples.pages in the example project which is hosted at http://www.wicket-library.com/inmethod-grid/data-grid/simple .

B.6. Module wicketstuff-rest-annotations

REST-based API are becoming more and more popular around the web and the number of services based on this architecture is constantly increasing.

Wicket is well-known for its capability of transparently handling the state of web applications on server side, but it can be also easily adopted to create RESTful services. WicketStuff module for REST provides a special resource class and a set of annotations to implement REST APIs/services in much the same way as we do it with Spring MVC or with the standard JAX-RS.

The module provides class AbstractRestResource as generic abstract class to implement a Wicket resource that handles the request and the response using a particular data format (XML, JSON, etc…​). Subclassing AbstractRestResource we can create custom resources and map their pubblic methods to a given subpath with annotation MethodMapping. The following snippet is taken from resource PersonsRestResource inside module ‘restannotations-examples’:

  1. @MethodMapping("/persons")
  2. public List<PersonPojo> getAllPersons() {
  3. //method mapped at subpath "/persons" and HTTP method GET
  4. }
  5. @MethodMapping(value = "/persons/{personIndex}", httpMethod = HttpMethod.DELETE)
  6. public void deletePerson(int personIndex) {
  7. //method mapped at subpath "/persons/{personIndex}" and HTTP method DELETE.
  8. //Segment {personIndex} will contain an integer value as index.
  9. }
  10. @MethodMapping(value = "/persons", httpMethod = HttpMethod.POST)
  11. public void createPerson(@RequestBody PersonPojo personPojo) {
  12. //creates a new instance of PersonPojo reading it from request body
  13. }

MethodMapping requires to specify the subpath we want to map the method to. In addition we can specify also the HTTP method that must be used to invoke the method via REST (GET, POST, DELETE, PATCH, etc…​). This value can be specified with enum class HttpMethod and is GET by default. In the code above we can see annotation RequestBody which is used to extract the value of a method parameter from the request body (method createPerson). To write/read objects to response/from request, AbstractRestResource uses an implementation of interface IWebSerialDeserial which defines the following methods:

  1. public interface IWebSerialDeserial {
  2. public void objectToResponse(Object targetObject, WebResponse response, String mimeType) throws Exception;
  3. public <T> T requestToObject(WebRequest request, Class<T> argClass, String mimeType) throws Exception;
  4. public boolean isMimeTypeSupported(String mimeType);
  5. }

To convert segments value (which are strings) to parameters type, AbstractRestResource uses the standard Wicket mechanism based on the application converter locator:

  1. //return the converter for type clazz
  2. IConverter converter = Application.get().getConverterLocator().getConverter(clazz);
  3. //convert string to object
  4. return converter.convertToObject(value, Session.get().getLocale());

In order to promote the principle of convention over configuration, we don’t need to use any annotation to map method parameters to path parameters if they are declared in the same order. If we need to manually bind method parameters to path parameters we can use annotation PathParam.

  1. @MethodMapping(value = "/variable/{p1}/order/{p2}", produces = RestMimeTypes.PLAIN_TEXT)
  2. public String testParamOutOfOrder(@PathParam("p2") String textParam, @PathParam("p1") int intParam) {
  3. //method parameter textParam is taken from path param 'p2', while intParam uses 'p1'
  4. }

As JSON is de-facto standard format for REST API, the project comes also with a ready-to-use resource (GsonRestResource) and a serial/deserial (GsonSerialDeserial) that work with JSON format (both inside module ‘restannotations-json’). These classes use Gson as JSON library.

AbstractRestResource supports role-based authorizations for mapped method with annotation AuthorizeInvocation:

  1. @MethodMapping(value = "/admin", httpMethod = HttpMethod.GET)
  2. @AuthorizeInvocation("ROLE_ADMIN")
  3. public void testMethodAdminAuth() {
  4. }

To use annotation AuthorizeInvocation we must specify in the resource construcor an instance of Wicket interface IRoleCheckingStrategy.

To read the complete documentation of the module and to discover more advanced feature please refer to the project homepage

B.7. Module wicketstuff-lambda-components

This module comes with class org.wicketstuff.lambda.components.ComponentFactory which exposes a number of factory method to build components using Lambda expressions as event handler. This can be useful to create components with simple behavior. For example:

  1. //create a standard link component
  2. add(ComponentFactory.link("id", (link) -> {/*do stuff*/});
  3. //create an AJAX link component
  4. add(ComponentFactory.ajaxLink("id", (ajaxLink, ajaxTarget) -> {/*do stuff*/});

The factory uses library jdk-serializable-functional to convert lambda expressions into a serializable version of java.util.function.* interfaces.

AjaxButton and AjaxSubmitLink are also supported:

  1. //create a submit link
  2. add(ComponentFactory.ajaxSubmitLink("id", (ajaxLink, ajaxTarget) -> {/*do submit stuff*/});
  3. //create a submit link with error handler
  4. add(ComponentFactory.ajaxSubmitLink("id", (ajaxLink, ajaxTarget) -> {/*do submit stuff*/},
  5. (ajaxLink, ajaxTarget) -> {/*do error stuff*/});

See ComponentFactory JavaDoc for a full list of factory methods.