The following steps need to be performed to get yourself a project ready to play around with. We will go here with the classic approach of creating a war packaged maven project that is configured via a web.xml and gets tested via the tomcat7-maven-plugin. Even though we explain this here step by step, if you want to just get something to start with, just copy and modify the invesdwin-nowicket-examples-war project. As an alternative invesdwin-nowicket-examples-guide is also worth a look since it contains the code for this documentation website and is using spring-boot. Next we are going through how invesdwin-nowicket-examples-war was created. All in all you will notice that this is not much different from setting up a pure Wicket project.
The image below shows what the final directory layout looks like so you can compare while setting everything up.
Final Directory Layout
4.1) Maven Setup
First install a current version of Maven and create a pom.xml to get the required dependencies and to be able to package and run the war project. Read the comments that are included for explanations.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.invesdwin</groupId>
<artifactId>invesdwin-nowicket-examples-war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<version.compiler>1.7</version.compiler>
<version.maven-compiler-plugin>3.1</version.maven-compiler-plugin>
<version.maven-processor-plugin>2.2.4</version.maven-processor-plugin>
<version.invesdwin-nowicket>1.0.0</version.invesdwin-nowicket>
<version.javax.servlet>3.0.0.v201112011016</version.javax.servlet>
</properties>
<build>
<pluginManagement>
<plugins>
<!-- run this sample with "mvn tomcat7:run" and access the website at
http://localhost:8080 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- our typical compiler configuration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version.maven-compiler-plugin}</version>
<configuration>
<source>${version.compiler}</source>
<target>${version.compiler}</target>
<complianceLevel>${version.compiler}</complianceLevel>
<encoding>${file.encoding}</encoding>
<compilerArgument>-proc:none</compilerArgument>
<Xlint>ignore</Xlint>
<verbose>false</verbose>
</configuration>
</plugin>
<!-- enable annotation processing and thus allow the invesdwin-norva constants
generator to create classes for our models (either annotate model with @BeanPathRoot
or extend AValueObject -->
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>${version.maven-processor-plugin}</version>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${project.build.directory}/generated-sources/apt</outputDirectory>
<failOnError>false</failOnError>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<!-- make development a bit easier by allowing html files and other web
resources to reside next to our class files so we do not have to maintain
two identical package trees -->
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/java</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
</testResource>
<testResource>
<filtering>false</filtering>
<directory>${basedir}/src/test/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
</build>
<dependencies>
<!-- add a dependency to invesdwin-NoWicket -->
<dependency>
<groupId>de.invesdwin</groupId>
<artifactId>invesdwin-nowicket</artifactId>
<version>${version.invesdwin-nowicket}</version>
<exclusions>
<exclusion>
<!-- exclude servlet API to add special handling below -->
<artifactId>javax.servlet</artifactId>
<groupId>org.eclipse.jetty.orbit</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- for compilation we need the servlet API, but not for deployment since
tomcat has its own; also make sure to only have one of these jars in your
classpath -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet</artifactId>
<version>${version.javax.servlet}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- add invesdwin-oss maven repo so that NoWicket artifacts can be resolved -->
<repositories>
<repository>
<id>invesdwin-oss</id>
<url>https://invesdwin.de/repo/invesdwin-oss-remote</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>invesdwin-oss</id>
<url>https://invesdwin.de/repo/invesdwin-oss-remote</url>
</pluginRepository>
</pluginRepositories>
</project>
4.2) Servlet Setup
Next create a web.xml in src/main/webapp/WEB-INF/
to register your implementation of the NoWicket servlet filter and map the error pages to nice looking ones provided by the framework (which you can customize or replace later).
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>invesdwin-nowicket-examples-war</display-name>
<filter>
<filter-name>wicket</filter-name>
<filter-class>de.invesdwin.nowicket.examples.war.ExampleWicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>de.invesdwin.nowicket.examples.war.ExampleWebApplication
</param-value>
</init-param>
<init-param>
<param-name>filterMappingUrlPattern</param-name>
<param-value>/*</param-value>
</init-param>
<init-param>
<!-- enable production error-pages -->
<param-name>configuration</param-name>
<param-value>deployment</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<!-- Missing login -->
<error-code>401</error-code>
<location>/accessdenied</location>
</error-page>
<error-page>
<!-- Forbidden directory listing -->
<error-code>403</error-code>
<location>/accessdenied</location>
</error-page>
<error-page>
<!-- Missing resource -->
<error-code>404</error-code>
<location>/pagenotfound</location>
</error-page>
<error-page>
<!-- Missing resource -->
<error-code>410</error-code>
<location>/pageexpired</location>
</error-page>
<error-page>
<!-- Uncaught exception -->
<location>/internalerror</location>
</error-page>
</web-app>
The NoWicket servlet filter implementation is very simple. It just needs a reference to your NoWicket application config for providing the ignorePaths feature that is needed when integrating tools like Guacamole which do not play well behind a Wicket filter.
package de.invesdwin.nowicket.examples.war;
import de.invesdwin.nowicket.application.IWebApplicationConfig;
import de.invesdwin.nowicket.application.filter.AWicketFilter;
public class ExampleWicketFilter extends AWicketFilter {
@Override
protected IWebApplicationConfig newConfig() {
return new ExampleWebApplicationConfig();
}
}
The NoWicket web application implementation ia similarly simple. It also only needs a reference to the NoWicket application config and a set of base packages for which the @MountPath annotation should be scanned for.
package de.invesdwin.nowicket.examples.war;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import de.invesdwin.nowicket.application.IWebApplicationConfig;
import de.invesdwin.nowicket.application.filter.AWebApplication;
public class ExampleWebApplication extends AWebApplication {
// also referenced in ExampleMarkupGenerator
public static final Set<String> BASE_PACKAGE = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList("de.invesdwin.nowicket.examples.war")));
@Override
protected IWebApplicationConfig newConfig() {
return new ExampleWebApplicationConfig();
}
@Override
public Set<String> getClasspathBasePackages() {
return BASE_PACKAGE;
}
}
The NoWicket application config implementation only has to specify the home page. You can optionally override more methods to do all the customizations discussed in the previous chapters.
package de.invesdwin.nowicket.examples.war;
import org.apache.wicket.markup.html.WebPage;
import de.invesdwin.nowicket.application.WebApplicationConfigSupport;
import de.invesdwin.nowicket.examples.war.page.HomePage;
public class ExampleWebApplicationConfig extends WebApplicationConfigSupport {
@Override
public Class<? extends WebPage> getHomePage() {
return HomePage.class;
}
}
4.3) Markup Generator Setup
The NoWicket markup generator implementation just needs a reference to the base packages to know where to scan for the @GeneratedMarkup annotation. You could also override more methods to change the input/output folders.
package de.invesdwin.nowicket.examples.war;
import java.util.Set;
import de.invesdwin.nowicket.generated.markup.AAnnotatedGeneratedMarkup;
public class ExampleMarkupGenerator extends AAnnotatedGeneratedMarkup {
public static void main(final String[] args) {
new ExampleMarkupGenerator().generate();
}
@Override
protected Set<String> getClasspathBasePackages() {
return ExampleWebApplication.BASE_PACKAGE;
}
}
4.4) Home Page Setup
The home page will be demonstrated as a hello world example. For this you will need a model implementation for which the markup should be generated. Here we implement Serializable since it is the minimum required for Wicket, otherwise it is recommended to extend AValueObject.
package de.invesdwin.nowicket.examples.war.page;
import java.io.Serializable;
import de.invesdwin.nowicket.generated.markup.annotation.GeneratedMarkup;
@GeneratedMarkup
public class Home implements Serializable {
public String getHello() {
return "NoWicket!";
}
}
Next we tell the markup generator to generate markup for a page implementation by writing a class for it. Here we provide our standard two constructors. We could also customize the navbar and other things that were discussed in the previous chapters, but this should better be done in an application specific base page that extends AWebPage. To keep this example as minimal as possible, we omit that here.
package de.invesdwin.nowicket.examples.war.page;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.wicketstuff.annotation.mount.MountPath;
import de.invesdwin.nowicket.application.AWebPage;
import de.invesdwin.nowicket.generated.binding.GeneratedBinding;
@MountPath("home")
public class HomePage extends AWebPage {
// constructor for bookmarkable link
public HomePage() {
this(Model.of(new Home()));
}
// constructor for model redirects
public HomePage(final IModel<Home> model) {
super(model);
new GeneratedBinding(this).bind();
}
}
4.5) Running It
Now you can run your ExampleMarkupGenerator. It will produce a HomePage.html file like this:
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head></head>
<body>
<wicket:extend>
<form wicket:id="form">
<!-- <div wicket:id='feedback'></div> -->
<div wicket:id="modal"></div>
<div class="mb-3 row">
<label class="col-form-label col-sm-2" wicket:for="hello"> <wicket:label>
Hello
</wicket:label></label>
<div wicket:id="hello_gridColumn" class="col-sm-4">
<input type="text" class="form-control" wicket:id="hello"></input>
</div>
</div>
</form>
</wicket:extend>
</body>
</html>
And it will also generate a HomePage.properties file like this:
hello=Hello
With everything in place now, you could run your project via Maven with mvn tomcat7:run
and access the website at http://localhost:8080.
4.6) Apache Server Reverse Proxy Configuration
If you want to embed/deploy your Wicket application behind an Apache Server as a Reverse Proxy (under the same context path between the java web server and apache2), you can use the following server configuration:
# enable required modules with: "a2enmod proxy proxy_html proxy_http proxy_connect substitute headers rewrite"
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPreserveHost On
RewriteEngine On
<Location /app/>
Order allow,deny
Allow from all
ProxyPass http://example.com:8080/app/ flushpackets=on
ProxyPassReverse http://example.com:8080/app/
RewriteCond %{REQUEST_URI} ^/app/wicket/websocket [NC,OR]
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://example.com:8080%{REQUEST_URI} [P,QSA,L]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule .* http://example.com:8080%{REQUEST_URI} [P,QSA,L]
SetOutputFilter proxy-html
ProxyHTMLDoctype "<!DOCTYPE HTML>"
ProxyHTMLURLMap http://example.com:8080/app/ /app/
AddOutputFilterByType SUBSTITUTE text/html
AddOutputFilterByType SUBSTITUTE text/css
AddOutputFilterByType SUBSTITUTE text/js
Substitute "s|http://example.com:8080/app/|https://example.com/app/|ni"
RequestHeader unset Accept-Encoding
</Location>
#also put this into the default site for port 80:
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
4.7) Nginx Server Reverse Proxy Configuration
If you want to embed/deploy your Wicket application behind an Nginx as a Reverse Proxy (under the same context path between the java web server and apache2), you can use the following server configuration:
location /app/ {
proxy_pass http://example.com:8080/app/;
proxy_redirect http://example.com:8080/app/ https://example.com/app/;
sub_filter "http://example.com:8080/app/" "https://example.com/app/";
sub_filter_once off;
sub_filter_types text/html;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $host;
}
Replace example.com
with your domain, 8080
with the port of your Wicket application is running on and app
with the path you want to make the application available under. With this you can make the application available in a web presence at port 80
which is more firewall friendly. Also you can reuse the SSL configuration of the Apache Server without having to configure it in Java (if not, just replace https above with http). The application can then be accessed at http://example.com/app/ (or what you changed it to).