You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by ad...@apache.org on 2015/06/15 10:21:28 UTC

[1/8] wicket git commit: Revert Jetty version to 8.1.16 because there is no 8.1.17 of jetty-maven-plugin. This breaks 'mvn jetty:run'

Repository: wicket
Updated Branches:
  refs/heads/WICKET-5906-7.x 70d09656f -> 410b0ae3c


Revert Jetty version to 8.1.16 because there is no 8.1.17 of jetty-maven-plugin. This breaks 'mvn jetty:run'


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/410b0ae3
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/410b0ae3
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/410b0ae3

Branch: refs/heads/WICKET-5906-7.x
Commit: 410b0ae3c4722ff651c767a36401de474d303225
Parents: f041bf0
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Sun Jun 14 14:20:43 2015 +0300
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/410b0ae3/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 643d8d3..25ab75e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,7 +127,7 @@
 
 		<!-- Project Versions -->
 		<jacoco.version>0.7.4.201502262128</jacoco.version>
-		<jetty.version>8.1.17.v20150415</jetty.version>
+		<jetty.version>8.1.16.v20140903</jetty.version>
 		<jetty9.version>9.0.7.v20131107</jetty9.version>
 		<joda-time.version>2.7</joda-time.version>
 		<junit.version>4.12</junit.version>


[6/8] wicket git commit: Cleanup of Maven's compiler settings

Posted by ad...@apache.org.
Cleanup of Maven's compiler settings


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/b9718f9c
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/b9718f9c
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/b9718f9c

Branch: refs/heads/WICKET-5906-7.x
Commit: b9718f9c7e537ffc08fbbb6fb9c793b6bca8fadb
Parents: 5e73409
Author: Martijn Dashorst <da...@apache.org>
Authored: Fri Jun 12 13:51:29 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 pom.xml | 22 +++++++++-------------
 1 file changed, 9 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/b9718f9c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index d28c9ce..6b11c5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
 	<parent>
 		<groupId>org.apache</groupId>
 		<artifactId>apache</artifactId>
-		<version>14</version>
+		<version>17</version>
 	</parent>
 	<groupId>org.apache.wicket</groupId>
 	<artifactId>wicket-parent</artifactId>
@@ -118,9 +118,14 @@
 		<!-- Encoding -->
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-		
+
+		<maven.compiler.optimize>true</maven.compiler.optimize>
+		<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
+		<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
+		<maven.compiler.source>1.7</maven.compiler.source>
+		<maven.compiler.target>1.7</maven.compiler.target>
+
 		<!-- Project Versions -->
-		<mvn.build.java.version>1.7</mvn.build.java.version>
 		<jacoco.version>0.7.4.201502262128</jacoco.version>
 		<jetty.version>8.1.17.v20150415</jetty.version>
 		<jetty9.version>9.0.7.v20131107</jetty9.version>
@@ -677,19 +682,10 @@
 					<version>2.3</version>
 				</plugin>
 				<plugin>
-					<inherited>true</inherited>
 					<groupId>org.apache.maven.plugins</groupId>
 					<artifactId>maven-compiler-plugin</artifactId>
 					<version>3.3</version>
-					<configuration>
-						<source>${mvn.build.java.version}</source>
-						<target>${mvn.build.java.version}</target>
-						<compilerVersion>${mvn.build.java.version}</compilerVersion>
-						<encoding>${project.build.sourceEncoding}</encoding>
-						<showWarnings>true</showWarnings>
-						<optimize>true</optimize>
-						<debug>true</debug>
-					</configuration>
+					<inherited>true</inherited>
 				</plugin>
 				<plugin>
 					<groupId>org.apache.maven.plugins</groupId>


[4/8] wicket git commit: WICKET-5921 Provide a default implementation of IModelComparator that always returns false

Posted by ad...@apache.org.
WICKET-5921 Provide a default implementation of IModelComparator that always returns false

(cherry picked from commit 0dab604341abc7e48fd34f4c4c2bc29c188d4054)


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/5e734096
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/5e734096
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/5e734096

Branch: refs/heads/WICKET-5906-7.x
Commit: 5e734096ca06a4551289bad4456ae8f539237c45
Parents: dc2f636
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Tue Jun 9 14:38:30 2015 +0300
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 .../java/org/apache/wicket/model/IModelComparator.java  | 12 ++++++++++++
 1 file changed, 12 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/5e734096/wicket-core/src/main/java/org/apache/wicket/model/IModelComparator.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/model/IModelComparator.java b/wicket-core/src/main/java/org/apache/wicket/model/IModelComparator.java
index 686469d..476252b 100644
--- a/wicket-core/src/main/java/org/apache/wicket/model/IModelComparator.java
+++ b/wicket-core/src/main/java/org/apache/wicket/model/IModelComparator.java
@@ -34,6 +34,18 @@ import org.apache.wicket.util.io.IClusterable;
 public interface IModelComparator extends IClusterable
 {
 	/**
+	 * A model comparator that always returns false
+	 */
+	IModelComparator ALWAYS_FALSE = new IModelComparator()
+	{
+		@Override
+		public boolean compare(Component component, Object newObject)
+		{
+			return false;
+		}
+	};
+
+	/**
 	 * @param component
 	 *            The component which received the new object
 	 * @param newObject


[2/8] wicket git commit: Automated adding releasenotes to changelog

Posted by ad...@apache.org.
Automated adding releasenotes to changelog


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/81570440
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/81570440
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/81570440

Branch: refs/heads/WICKET-5906-7.x
Commit: 81570440aad93dab08d610fe1bf22effa69d9f9f
Parents: ae41fa1
Author: Martijn Dashorst <da...@apache.org>
Authored: Fri Jun 12 17:13:45 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 build-changelog.sh | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 147 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/81570440/build-changelog.sh
----------------------------------------------------------------------
diff --git a/build-changelog.sh b/build-changelog.sh
new file mode 100755
index 0000000..2c9c3d9
--- /dev/null
+++ b/build-changelog.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+function fail {
+	echo "$1"
+	exit 1
+}
+
+function getProjectVersionFromPom {
+	cat << EOF | xmllint --noent --shell pom.xml | grep content | cut -f2 -d=
+setns pom=http://maven.apache.org/POM/4.0.0
+xpath /pom:project/pom:version/text()
+EOF
+}
+
+if [ "$1" = "--help" ] ; then
+	echo "
+Usage: $0 [--help] [version]
+
+Retrieves the release notes for the next release of Apache Wicket,
+and merges this into the CHANGELOG file.
+
+  version
+      optional version number to retrieve the release notes for
+
+  --help
+      shows this help
+
+"
+	exit 0
+fi
+
+if [ ! -z "$1" ] ; then
+	current_version="$1"
+	major_version=$(expr $current_version : '\(.*\)\..*\..*\-.*')
+	minor_version=$(expr $current_version : '.*\.\(.*\)\..*\-.*')
+	bugfix_version=$(expr $current_version : '.*\..*\.\(.*\)-.*')
+	milestone_version=$(expr $current_version : '.*\..*-\(.*\)')
+	version="$major_version.$minor_version.0-$milestone_version"
+	previous_version="$major_version.$(expr $minor_version - 1).0"
+else
+	current_version=$(getProjectVersionFromPom)
+	major_version=$(expr $current_version : '\(.*\)\..*\..*\-.*')
+	minor_version=$(expr $current_version : '.*\.\(.*\)\..*\-.*')
+	bugfix_version=$(expr $current_version : '.*\..*\.\(.*\)-.*')
+	version="$major_version.$minor_version.0"
+	previous_version="$major_version.$(expr $minor_version - 1).0"
+fi
+
+echo "
+Apache Wicket release notes generator
+=====================================
+This tool retrieves the release notes for the upcoming release from JIRA
+and merges this with the existing CHANGELOG in a text format.
+
+Version to retrieve the release notes from: $version
+
+Press <enter> to continue \c"
+
+read
+
+git status --porcelain CHANGELOG-$major_version.x | grep -q "CHANGELOG-$major_version.x"
+if [ $? -eq 0 ] ; then
+	fail "You already have changes in the CHANGELOG-$major_version.x
+"
+fi
+
+grep -q "$version\$" CHANGELOG-$major_version.x
+if [ $? -eq 0 ] ; then
+	fail "You already have added release notes for this version to the changelog.
+"
+fi
+
+
+echo "
+Extracting JIRA Release notes -- making web requests"
+echo "  - determining JIRA version id for $version: \c"
+jira_project_id=12310561
+jira_version_id=$( \
+	curl -s https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=$jira_project_id \
+	| xmllint --noout --noblanks --html --xpath "string(//select[@id=\"version_select\"]/option[translate(normalize-space(text()), ' ', '')=\"$version\"]/@value)" - 2>/dev/null \
+	)
+
+re='^[0-9]+$'
+if ! [[ $jira_version_id =~ $re ]] ; then
+	echo "ERROR"
+	echo "
+Unable to retrieve the version ID from JIRA: received '$jira_version_id'" >&2
+	exit 1
+fi
+
+echo "$jira_version_id"
+
+echo "  - retrieving text release notes: \c"
+curl -s "https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=$jira_project_id&version=$jira_version_id&styleName=Text&Create=Create" \
+	|  xmllint --noout --noblanks --html --xpath "string(//textarea)" - 2>/dev/null | cat -s | awk '{ if ($0 ~ /^\*\* / ) {
+    printf( "%s\n\n", $0);
+} else {
+    printf( "%s\n", $0 );
+}
+}' > /tmp/release-notes-$version.txt
+
+echo "done"
+
+echo "  - merging release notes into changelog: \c"
+
+echo "This file contains all changes done in releases for Apache Wicket 7.x.
+
+=======================================================================
+$(cat /tmp/release-notes-$version.txt)
+
+=======================================================================
+$(tail -n +4 CHANGELOG-$major_version.x)
+" > /tmp/changelog-$version.txt
+cp /tmp/changelog-$version.txt CHANGELOG-$major_version.x
+
+echo "done"
+
+echo "
+The CHANGELOG-$major_version.x file has been updated. Please check the contents
+and commit the changes.
+
+To see the status:
+
+    git status
+    git diff
+
+To add and commit the CHANGELOG:
+
+    git add CHANGELOG-$major_version.x
+    git commit -m \"Added CHANGELOG for release $version
+
+Have fun!
+"


[7/8] wicket git commit: Upgraded release scripts to automate more

Posted by ad...@apache.org.
Upgraded release scripts to automate more

Added automatic release notes generation, merged milestone
release creation into the main release script.


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/f041bf0b
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/f041bf0b
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/f041bf0b

Branch: refs/heads/WICKET-5906-7.x
Commit: f041bf0b8fd87b6234bc91941e47371498adbbd1
Parents: 8157044
Author: Martijn Dashorst <da...@apache.org>
Authored: Fri Jun 12 18:11:05 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 build-versions.py    |  57 +++++++
 release-dashorst.sh  | 424 +++++++++++++++++++++++++++++++---------------
 release-milestone.py |  57 -------
 release-milestone.sh |  47 +----
 4 files changed, 349 insertions(+), 236 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/f041bf0b/build-versions.py
----------------------------------------------------------------------
diff --git a/build-versions.py b/build-versions.py
new file mode 100755
index 0000000..5dbf488
--- /dev/null
+++ b/build-versions.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+#
+# prints a release.properties file for instructing the Maven Release Plugin
+# to generate the proper release artefacts without having to manually version
+# everything.
+#
+# Usage:
+#
+#     build-versions.py <release-version> <dev-version>
+#
+# This will generate a release.properties file that will release the 
+# release-version, and will continue development on dev-version.
+#
+# Example:
+#
+#    build-milestone.py 7.0.0-M1 7.0.0-SNAPSHOT
+#
+
+import sys
+from xml.dom.minidom import parse
+
+groupId = "org.apache.wicket"
+
+if len(sys.argv) != 3:
+    print "Usage: %s <release-version> <dev-version>" % sys.argv[0]
+    sys.exit(1)
+
+relVersion = sys.argv[1]
+devVersion = sys.argv[2]
+
+relVersions = []
+devVersions = []
+
+def addVersions(groupId, module):
+    relVersions.append("project.rel." + groupId + "\\:" + module + "=" + relVersion)
+    devVersions.append("project.dev." + groupId + "\\:" + module + "=" + devVersion)
+    
+def getModulesFromParent(parentPomFile):
+    pom = parse(parentPomFile)
+
+    for moduleTag in pom.getElementsByTagName('module'):
+        module = moduleTag.childNodes[0].nodeValue.replace("testing/", "").replace("archetypes/quickstart", "wicket-archetype-quickstart")
+        addVersions(groupId, module)
+
+addVersions(groupId, "wicket-parent")
+addVersions("org.apache.wicket.experimental.wicket7", "wicket-experimental")
+
+getModulesFromParent("pom.xml")
+getModulesFromParent("wicket-native-websocket/pom.xml")
+
+for version in relVersions:
+    print version
+
+print
+
+for version in devVersions:
+    print version

http://git-wip-us.apache.org/repos/asf/wicket/blob/f041bf0b/release-dashorst.sh
----------------------------------------------------------------------
diff --git a/release-dashorst.sh b/release-dashorst.sh
index 42d1174..a6d9a1e 100755
--- a/release-dashorst.sh
+++ b/release-dashorst.sh
@@ -14,105 +14,141 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-log=/tmp/wicketrelease.out
-
 function fail {
 	echo "$1"
-	if [ -f $log ] ; then
-		echo ""
-		cat $log
-	fi
-	exit
+	exit 1
 }
 
-function setup_gpg {
-	gpg --armor --detach-sign --use-agent --sign pom.xml >& $log
-	if [ $? -ne 0 ] ; then
-		fail "ERROR: Unable to run gpg properly"
-	fi
-
-	gpg --verify pom.xml.asc >& $log
-	if [ $? -ne 0 ]; then
-		rm pom.xml.asc
-	    fail "It appears that you fat-fingered your GPG passphrase"
-	fi
-	rm pom.xml.asc
+function getJavaVersionFromPom {
+	cat << EOF | xmllint --noent --shell pom.xml | grep content | cut -f2 -d=
+setns pom=http://maven.apache.org/POM/4.0.0
+xpath /pom:project/pom:properties/pom:maven.compiler.source/text()
+EOF
 }
 
-function getVersion {
+function getProjectVersionFromPom {
 	cat << EOF | xmllint --noent --shell pom.xml | grep content | cut -f2 -d=
 setns pom=http://maven.apache.org/POM/4.0.0
 xpath /pom:project/pom:version/text()
 EOF
 }
 
+function getJdkToolchain {
+	xmllint ~/.m2/toolchains.xml --xpath "/toolchains/toolchain[provides/version/text() = '$JAVA_VERSION']/configuration/jdkHome/text()"
+}
+
 # set -e
 
-echo "Apache Wicket Release script"
-echo "----------------------------"
-echo "Building a release for Apache Wicket."
-echo ""
-echo "This script assumes you are running on OS X, it hasn't been tested on any other"
-echo "operating systems, and you can bet it won't work on Windows..."
-echo ""
-echo "REQUIREMENTS:"
-echo ""
-echo " - a pure JDK 6 environment, JDK 7 or newer won't cut it"
-echo " - Maven 3.0.4 (older releases are b0rked, just don't bother)"
-echo " - gpg, gpg-agent and pinentry for signing"
-echo ""
+# the branch on which the code base lives for this version (master is
+# always current development version)
+GIT_BRANCH=master
 
-agentcount=`ps aux|grep gpg-agent|wc -l`
+JAVA_VERSION=$(getJavaVersionFromPom)
 
-current_version=$(getVersion)
-major_version=$(expr $current_version : '\(.*\)\..*\..*\-SNAPSHOT')
-minor_version=$(expr $current_version : '.*\.\(.*\)\..*\-SNAPSHOT')
-bugfix_version=$(expr $current_version : '.*\..*\.\(.*\)-SNAPSHOT')
-version="$major_version.$minor_version.0"
-echo "This script will release version: Apache Wicket $version"
-echo ""
-echo "Press enter to continue or CTRL-C to abort \c"
-read 
+echo "
+Apache Wicket Release script
+----------------------------
+Building a release for Apache Wicket.
 
-branch="build/wicket-$version"
-tag="wicket-$version"
+This script assumes you are running on OS X, it hasn't been tested on any other
+operating systems, and you can bet it won't work on Windows...
 
-if [ "$agentcount" -ne 1 ]; then
-	echo "Found gpg-agent running, killing all agents"
-	killall gpg-agent
+REQUIREMENTS:
+
+ - A Java version $JAVA_VERSION configured through the Maven toolchain
+ - Maven 3.3.0 (older releases are b0rked, just don't bother)
+ - gpg, gpg-agent and pinentry for signing
+"
+
+if [ ! -f ~/.m2/toolchains.xml ] ; then
+	fail "
+Maven will load the Java $JAVA_VERSION environment from the toolchain specified in 
+~/.m2/toolchains.xml
+
+You don't have a toolchains.xml file in your .m2 folder. Please specify your
+JDK's in the toolchains.xml file.
+"
 fi
 
-echo ""
-echo "You are asked twice for your passphrase, one for scripting purposes, and one "
-echo "for gpg-agent using pinentry such that gpg and git are able to sign things."
-echo ""
-echo "Enter your GPG passphrase (input will be hidden) \c"
-stty_orig=`stty -g` 
-stty -echo 
-read passphrase
-stty $stty_orig
-
-# test the GPGP passphrase to fail-fast:
-echo "$passphrase" | gpg --passphrase-fd 0 --armor --output pom.xml.asc --detach-sig pom.xml
-gpg --verify pom.xml.asc
-if [ $? -ne 0 ]; then
-        echo "It appears that you fat-fingered your GPG passphrase"
-		rm pom.xml.asc
-        exit $?
+grep -q "<version>$JAVA_VERSION</version>" ~/.m2/toolchains.xml
+
+if [ $? -ne 0 ] ; then
+	fail "
+Your ~/.m2/toolchains.xml file doesn't provide a Java $JAVA_VERSION toolchain.
+"
 fi
-rm pom.xml.asc
 
-echo "Starting new gpg-agent"
-eval $(gpg-agent --daemon --pinentry-program $(which pinentry))
+echo "Java version for running Maven is: $(java -version 2>&1 | tail -n 2 | head -n 1)
+Java used to compile (from toolchain) is: $(getJdkToolchain)
+"
+
+agentcount=`ps aux|grep gpg-agent|wc -l`
+
+if [ ! -z "$1" ] ; then
+	current_version="$1"
+	major_version=$(expr $current_version : '\(.*\)\..*\..*\-.*')
+	minor_version=$(expr $current_version : '.*\.\(.*\)\..*\-.*')
+	bugfix_version=$(expr $current_version : '.*\..*\.\(.*\)-.*')
+	milestone_version=$(expr $current_version : '.*\..*-M\(.*\)')
+	version="$major_version.$minor_version.0-M$milestone_version"
+	next_version="$major_version.0.0-SNAPSHOT"
+	previous_version="$major_version.0.0-SNAPSHOT"
+else
+	current_version=$(getProjectVersionFromPom)
+	major_version=$(expr $current_version : '\(.*\)\..*\..*\-.*')
+	minor_version=$(expr $current_version : '.*\.\(.*\)\..*\-.*')
+	bugfix_version=$(expr $current_version : '.*\..*\.\(.*\)-.*')
+	version="$major_version.$minor_version.0"
+	next_version="$major_version.$(expr $minor_version + 1).0-SNAPSHOT"
+	previous_minor_version=$(expr $minor_version - 1)
+	if [ $previous_minor_version -lt 0 ] ; then
+		previous_version="$major_version.0.0-SNAPSHOT"
+	else
+		previous_version="$major_version.$(expr $minor_version - 1).0"
+	fi
+fi
+
+# Check if the changelog has the issues this release
+
+grep -q "$version\$" CHANGELOG-$major_version.x
 if [ $? -ne 0 ] ; then
-	fail "ERROR: Unable to start gpg-agent"
+	fail "You have forgotten to add the closed tickets for Wicket $version to the CHANGELOG-$major_version.x file
+
+Use build-changelog.sh to add the release notes to the changelog.
+"
 fi
 
-setup_gpg
+log=$(pwd)/release.out
 
-echo "Ensuring we are starting from master"
+if [ -f $log ] ; then
+    rm $log
+fi
+
+branch="build/wicket-$version"
+tag="wicket-$version"
+
+echo "# Release configuration for Wicket-$version
+scm.tag=${tag}
+" > release.properties
+
+./build-versions.py $version $next_version >> release.properties
+
+echo "Contents of the release properties generated for Maven:
+-------------------------------------------------------------------------------
+$(cat ./release.properties)
+-------------------------------------------------------------------------------
+
+Writing detailed log to $log
+
+This script will release version: Apache Wicket $version and continue
+development with $next_version
+
+Press enter to continue or CTRL-C to abort \c"
+read 
+
+echo "Ensuring we are starting from wicket-$major_version.x"
 # otherwise we can't remove a previous release branch that failed
-git checkout master
+git checkout $GIT_BRANCH
 
 echo "Cleaning up any release artifacts that might linger"
 mvn -q release:clean
@@ -131,20 +167,20 @@ git checkout -b $branch
 echo "Creating notice file."
 
 NOTICE=NOTICE
-> $NOTICE 
-echo "Apache Wicket" >> $NOTICE
-echo "Copyright 2006-$(date +%Y) The Apache Software Foundation" >> $NOTICE
-echo "" >> $NOTICE
-echo "This product includes software developed at" >> $NOTICE
-echo "The Apache Software Foundation (http://www.apache.org/)." >> $NOTICE
-echo "" >> $NOTICE
-echo "This is an aggregated NOTICE file for the Apache Wicket projects included" >> $NOTICE
-echo "in this distribution." >> $NOTICE
-echo "" >> $NOTICE
-echo "NB: DO NOT ADD LICENSES/NOTICES/ATTRIBUTIONS TO THIS FILE, BUT IN THE" >> $NOTICE
-echo "    NOTICE FILE OF THE CORRESPONDING PROJECT. THE RELEASE PROCEDURE WILL" >> $NOTICE
-echo "    AUTOMATICALLY INCLUDE THE NOTICE IN THIS FILE." >> $NOTICE
-echo "" >> $NOTICE
+
+echo "Apache Wicket
+Copyright 2006-$(date +%Y) The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This is an aggregated NOTICE file for the Apache Wicket projects included
+in this distribution.
+
+NB: DO NOT ADD LICENSES/NOTICES/ATTRIBUTIONS TO THIS FILE, BUT IN THE
+    NOTICE FILE OF THE CORRESPONDING PROJECT. THE RELEASE PROCEDURE WILL
+    AUTOMATICALLY INCLUDE THE NOTICE IN THIS FILE.
+" > $NOTICE
 
 # next concatenate all NOTICE files from sub projects to the root file
 for i in `find . -name "NOTICE" -not -regex ".*/target/.*" -not -regex "./NOTICE"`
@@ -168,7 +204,7 @@ mvn -q clean -Pall
 
 # package and assemble the release
 echo "Prepare the release"
-mvn --batch-mode release:prepare -DpreparationGoals="clean" -Dtag=$tag
+mvn --batch-mode release:prepare -l $log -DpreparationGoals="clean" -Dtag=$tag -Papache-release,release
 if [ $? -ne 0 ] ; then
 	fail "ERROR: mvn release:prepare was not successful"
 fi
@@ -183,11 +219,17 @@ fi
 #git tag --sign --force --message "Signed release tag for Apache Wicket $version" $tag >> $log
 
 echo "Performing the release using Maven"
-mvn -Dgpg.passphrase="$passphrase" -ff -l $log release:perform -DlocalCheckout=true -Dtag=$tag
+mvn -Dgpg.passphrase="$passphrase" -ff -l $log release:perform -DlocalCheckout=true -Dtag=$tag -Papache-release,release
 if [ $? -ne 0 ] ; then
 	fail "ERROR: mvn release:perform was not successful"
 fi
 
+# Determine the staging repository and close it after deploying the release to the staging area
+stagingrepoid=$(mvn org.sonatype.plugins:nexus-staging-maven-plugin:LATEST:rc-list -DnexusUrl=https://repository.apache.org -DserverId=apache.releases.https | grep -v "CLOSED" | grep -Eo "(orgapachewicket-\d+)";)
+
+echo "Closing staging repository with id $stagingrepoid"
+mvn org.sonatype.plugins:nexus-staging-maven-plugin:LATEST:rc-close -DstagingRepositoryId=$stagingrepoid -DnexusUrl=https://repository.apache.org -DserverId=apache.releases.https -Ddescription="Release has been built, awaiting vote"
+
 echo "Create and sign the source tarballs"
 
 mkdir -p target/dist/binaries
@@ -224,7 +266,7 @@ gpg --print-md SHA1 dist/binaries/apache-wicket-$version-bin.zip > dist/binaries
 gpg --print-md MD5  dist/binaries/apache-wicket-$version-bin.zip > dist/binaries/apache-wicket-$version-bin.zip.md5
 popd
 
-echo "Uploading release"
+echo "Uploading release to dist.apache.org"
 pushd target/dist
 svn mkdir https://dist.apache.org/repos/dist/dev/wicket/$version -m "Create $version release staging area"
 svn co --force --depth=empty https://dist.apache.org/repos/dist/dev/wicket/$version .
@@ -233,49 +275,165 @@ svn add *
 svn commit -m "Upload wicket-$version to staging area"
 popd
 
-echo ""
-echo "The release has been created. It is up to you to check if the release is up"
-echo "to par, and perform the following commands yourself when you start the vote"
-echo "to enable future development during the vote and after."
-echo ""
-echo "You can find the distribution in target/dist"
-echo ""
-echo "    cd target/dist"
-echo ""
-echo "To verify all signatures:"
-echo ""
-echo "    find . -name \"*.asc\" -exec gpg --verify {} \; "
-echo ""
-echo "To push the release branch to ASF git servers"
-echo ""
-echo "    git push origin $branch:refs/heads/$branch"
-echo ""
-
-echo "To move the release from staging to the mirrors:"
-echo ""
-echo "    svn mv https://dist.apache.org/repos/dist/dev/wicket/$version https://dist.apache.org/repos/dist/release/wicket -m \"Upload release to the mirrors\""
-echo ""
-
-echo "To sign the release tag issue the following three commands: "
-echo ""
-echo "    git checkout $tag"
-echo "    git tag --sign --force --message \"Signed release tag for Apache Wicket $version\" $tag >> $log"
-echo "    git checkout $branch"
-echo ""
+echo "========================================================================
+
+The signatures for the source release artefacts:
+
+" > /tmp/release-$version-sigs.txt
+
+pushd target/dist > /dev/null
+for i in apache-wicket*{zip,tar.gz}
+do
+	echo "Signature for $i:
+
+$(cat $i.asc)
+" >> /tmp/release-$version-sigs.txt
+done
+popd > /dev/null
+
+echo "========================================================================
+
+CHANGELOG for $version:
+" >> /tmp/release-$version-sigs.txt
+
+awk "/Release Notes - Wicket - Version $version/{flag=1;next} /==================/{flag=0} flag { print }" CHANGELOG-6.x >> /tmp/release-$version-sigs.txt
+
+
+echo "Generating Vote email"
+
+echo "This is a vote to release Apache Wicket $version
+
+Please download the source distributions found in our staging area
+linked below.
+
+I have included the signatures for both the source archives. This vote
+lasts for 72 hours minimum.
+
+[ ] Yes, release Apache Wicket $version
+[ ] No, don't release Apache Wicket $version, because ...
+
+Distributions, changelog, keys and signatures can be found at:
+
+    https://dist.apache.org/repos/dist/dev/wicket/$version
+
+Staging repository:
+
+    https://repository.apache.org/content/repositories/$stagingrepoid/
+
+The binaries are available in the above link, as are a staging
+repository for Maven. Typically the vote is on the source, but should
+you find a problem with one of the binaries, please let me know, I can
+re-roll them some way or the other.
+
+" > release-vote.txt
+
+cat /tmp/release-$version-sigs.txt >> release-vote.txt
+
+echo "The Apache Wicket PMC is proud to announce Apache Wicket $version!
+
+This release marks another minor release of Wicket $major_version. We
+use semantic versioning for the development of Wicket, and as such no
+API breaks are present breaks are present in this release compared to
+$major_version.0.0.
+
+New and noteworthy
+------------------
+
+<OPTIONAL>
+
+Using this release
+------------------
+
+With Apache Maven update your dependency to (and don't forget to
+update any other dependencies on Wicket projects to the same version):
+
+<dependency>
+    <groupId>org.apache.wicket</groupId>
+    <artifactId>wicket-core</artifactId>
+    <version>$version</version>
+</dependency>
+
+Or download and build the distribution yourself, or use our
+convenience binary package
+
+ * Source: http://www.apache.org/dyn/closer.cgi/wicket/$version
+ * Binary: http://www.apache.org/dyn/closer.cgi/wicket/$version/binaries
+
+Upgrading from earlier versions
+-------------------------------
+
+If you upgrade from $major_version.y.z this release is a drop in replacement. If
+you come from a version prior to $major_version.0.0, please read our Wicket $major_version
+migration guide found at
+
+ * http://s.apache.org/wicket${major_version}migrate
+
+Have fun!
+
+— The Wicket team
+
+" > release-announce.txt
+
+cat /tmp/release-$version-sigs.txt >> release-announce.txt
+
+# Done with the tasks, now print out the next things the release manager
+# needs to do
 
 mvn_version_to_replace="$major_version.$minor_version.1-SNAPSHOT"
-next_dev_version="$major_version.$(expr $minor_version + 1).0-SNAPSHOT"
-
-echo "To renumber the next development iteration $next_dev_version:"
-echo ""
-echo "    git checkout master"
-echo "    mvn release:update-versions --batch-mode"
-echo "    find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml -exec sed -i \"\" -E \"s/$mvn_version_to_replace/$next_dev_version/g\" {} \\;"
-# do the same for the original snapshot version, as our maven release
-# plugin friend doesn't do that for us in the dependency management section
-mvn_version_to_replace="$major_version.$minor_version.0-SNAPSHOT"
-echo "    find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml -exec sed -i \"\" -E \"s/$mvn_version_to_replace/$next_dev_version/g\" {} \\;"
-echo "    git add \`find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml\`"
-echo "    git commit -m \"Start next development version\""
-echo "    git push"
-echo ""
+mvn_version_to_replace2="$major_version.$minor_version.0-SNAPSHOT"
+
+echo "
+The release has been created. It is up to you to check if the release is up
+to par, and perform the following commands yourself when you start the vote
+to enable future development during the vote and after.
+
+A vote email has been generated in release-vote.txt, you can copy/paste it using:
+
+    cat release-vote.txt | pbcopy
+
+You can find the distribution in target/dist
+
+    cd target/dist
+
+To verify all signatures:
+
+    find . -name \"*.asc\" -exec gpg --verify {} \; 
+
+To push the release branch to ASF git servers
+
+    git push origin $branch:refs/heads/$branch
+
+To move the release from staging to the mirrors:
+
+    svn mv https://dist.apache.org/repos/dist/dev/wicket/$version https://dist.apache.org/repos/dist/release/wicket -m \"Upload release to the mirrors\"
+
+Remove previous version $previous_version from the mirrors
+
+    svn rm https://dist.apache.org/repos/dist/release/wicket/$previous_version -m \"Remove previous version from mirrors\"
+
+To sign the release tag issue the following three commands: 
+
+    git checkout $tag
+    git tag --sign --force --message \"Signed release tag for Apache Wicket $version\" $tag >> $log
+    git checkout $branch
+
+To renumber the next development iteration $next_version:
+
+    git checkout $GIT_BRANCH
+    mvn release:update-versions --batch-mode
+    find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml -exec sed -i \"\" -E \"s/$mvn_version_to_replace/$next_version/g\" {} \\;
+    find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml -exec sed -i \"\" -E \"s/$mvn_version_to_replace/$next_version/g\" {} \\;
+    git add \`find . ! \\( -type d -name \"target\" -prune \\) -name pom.xml\`
+    git commit -m \"Start next development version\"
+    git push
+
+To release the Maven artefacts:
+
+	mvn org.sonatype.plugins:nexus-staging-maven-plugin:LATEST:rc-release -DstagingRepositoryId=$stagingrepoid -DnexusUrl=https://repository.apache.org -DserverId=apache.releases.https -Ddescription=\"Release vote has passed\"
+
+Or in case of a failed vote, to drop the staging repository:
+
+	mvn org.sonatype.plugins:nexus-staging-maven-plugin:LATEST:rc-drop -DstagingRepositoryId=$stagingrepoid -DnexusUrl=https://repository.apache.org -DserverId=apache.releases.https -Ddescription=\"Release vote has failed\"
+" > release.txt
+
+cat release.txt

http://git-wip-us.apache.org/repos/asf/wicket/blob/f041bf0b/release-milestone.py
----------------------------------------------------------------------
diff --git a/release-milestone.py b/release-milestone.py
deleted file mode 100755
index c85913f..0000000
--- a/release-milestone.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/python
-#
-# prints a release.properties file for instructing the Maven Release Plugin
-# to generate the proper release artefacts without having to manually version
-# everything.
-#
-# Usage:
-#
-#     release-milestone.py <release-version> <dev-version>
-#
-# This will generate a release.properties file that will release the 
-# release-version, and will continue development on dev-version.
-#
-# Example:
-#
-#    release-milestone.py 7.0.0-M1 7.0.0-SNAPSHOT
-#
-
-import sys
-from xml.dom.minidom import parse
-
-groupId = "org.apache.wicket"
-
-if len(sys.argv) != 3:
-    print "Usage: %s <release-version> <dev-version>" % sys.argv[0]
-    sys.exit(1)
-
-relVersion = sys.argv[1]
-devVersion = sys.argv[2]
-
-relVersions = []
-devVersions = []
-
-def addVersions(groupId, module):
-    relVersions.append("project.rel." + groupId + "\\:" + module + "=" + relVersion)
-    devVersions.append("project.dev." + groupId + "\\:" + module + "=" + devVersion)
-    
-def getModulesFromParent(parentPomFile):
-    pom = parse(parentPomFile)
-
-    for moduleTag in pom.getElementsByTagName('module'):
-        module = moduleTag.childNodes[0].nodeValue.replace("testing/", "").replace("archetypes/quickstart", "wicket-archetype-quickstart")
-        addVersions(groupId, module)
-
-addVersions(groupId, "wicket-parent")
-addVersions("org.apache.wicket.experimental.wicket7", "wicket-experimental")
-
-getModulesFromParent("pom.xml")
-getModulesFromParent("wicket-native-websocket/pom.xml")
-
-for version in relVersions:
-    print version
-
-print
-
-for version in devVersions:
-    print version

http://git-wip-us.apache.org/repos/asf/wicket/blob/f041bf0b/release-milestone.sh
----------------------------------------------------------------------
diff --git a/release-milestone.sh b/release-milestone.sh
index ee32dca..1cc45d2 100755
--- a/release-milestone.sh
+++ b/release-milestone.sh
@@ -23,51 +23,6 @@ function fail {
 	exit
 }
 
-function setup_gpg {
-
-	if [ "$agentcount" -ne 1 ]; then
-		echo "Found gpg-agent running, killing all agents"
-		killall gpg-agent
-	fi
-
-	echo ""
-	echo "You are asked twice for your passphrase, one for scripting purposes, and one "
-	echo "for gpg-agent using pinentry such that gpg and git are able to sign things."
-	echo ""
-	echo "Enter your GPG passphrase (input will be hidden) \c"
-	stty_orig=`stty -g` 
-	stty -echo 
-	read passphrase
-	stty $stty_orig
-
-	# test the GPGP passphrase to fail-fast:
-	echo "$passphrase" | gpg --passphrase-fd 0 --armor --output pom.xml.asc --detach-sig pom.xml
-	gpg --verify pom.xml.asc
-	if [ $? -ne 0 ]; then
-	        echo "It appears that you fat-fingered your GPG passphrase"
-			rm pom.xml.asc
-	        exit $?
-	fi
-	rm pom.xml.asc
-
-	echo "Starting new gpg-agent"
-	eval $(gpg-agent --daemon --pinentry-program $(which pinentry))
-	if [ $? -ne 0 ] ; then
-		fail "ERROR: Unable to start gpg-agent"
-	fi
-	gpg --armor --detach-sign --use-agent --sign pom.xml >& $log
-	if [ $? -ne 0 ] ; then
-		fail "ERROR: Unable to run gpg properly"
-	fi
-
-	gpg --verify pom.xml.asc >& $log
-	if [ $? -ne 0 ]; then
-		rm pom.xml.asc
-	    fail "It appears that you fat-fingered your GPG passphrase"
-	fi
-	rm pom.xml.asc
-}
-
 function getVersion {
 	cat << EOF | xmllint --noent --shell pom.xml | grep content | cut -f2 -d=
 setns pom=http://maven.apache.org/POM/4.0.0
@@ -154,7 +109,7 @@ echo "# Release configuration for Wicket-$version
 scm.tag=${tag}
 " > release.properties
 
-./release-milestone.py $version 7.0.0-SNAPSHOT >> release.properties
+./build-versions.py $version 7.0.0-SNAPSHOT >> release.properties
 
 cat ./release.properties
 


[3/8] wicket git commit: minor javadoc correction

Posted by ad...@apache.org.
minor javadoc correction


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/dc2f6361
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/dc2f6361
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/dc2f6361

Branch: refs/heads/WICKET-5906-7.x
Commit: dc2f6361c2df66d171a70c1260c07af5077b7db4
Parents: 1cbbc76
Author: Sven Meier <sv...@apache.org>
Authored: Sun Jun 7 19:16:02 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 .../wicket/protocol/http/CsrfPreventionRequestCycleListener.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/dc2f6361/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
index 21d5569..2ec69ca 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
@@ -162,7 +162,7 @@ public class CsrfPreventionRequestCycleListener extends AbstractRequestCycleList
 	private CsrfAction conflictingOriginAction = CsrfAction.ABORT;
 
 	/**
-	 * The error code to report when the action to take for a CSRF request is {@code ERROR}. Default
+	 * The error code to report when the action to take for a CSRF request is {@link CsrfAction#ABORT}. Default
 	 * {@code 400 BAD REQUEST}.
 	 */
 	private int errorCode = javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;


[8/8] wicket git commit: Fix Maven enforcer errors

Posted by ad...@apache.org.
Fix Maven enforcer errors


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/ae41fa13
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/ae41fa13
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/ae41fa13

Branch: refs/heads/WICKET-5906-7.x
Commit: ae41fa1335b54abb62d6529556eb8eea55b29e1b
Parents: b9718f9
Author: Martijn Dashorst <da...@apache.org>
Authored: Fri Jun 12 13:52:36 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 pom.xml                   | 10 +++---
 wicket-core/pom.xml       | 18 +++++++---
 wicket-examples/pom.xml   | 10 +++---
 wicket-jmx/pom.xml        |  8 ++---
 wicket-user-guide/pom.xml | 75 ++++++++++++++++++++++++------------------
 5 files changed, 70 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/ae41fa13/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 6b11c5a..643d8d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -258,11 +258,6 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.wicket</groupId>
-				<artifactId>wicket-eclipse-settings</artifactId>
-				<version>2</version>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.wicket</groupId>
 				<artifactId>wicket</artifactId>
 				<version>7.0.0-SNAPSHOT</version>
 				<!-- It seems there is a bug in Maven (2.2.1 & 3.0.1) and
@@ -314,6 +309,11 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.wicket</groupId>
+				<artifactId>wicket-eclipse-settings</artifactId>
+				<version>2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.wicket</groupId>
 				<artifactId>wicket-extensions</artifactId>
 				<version>7.0.0-SNAPSHOT</version>
 				<type>jar</type>

http://git-wip-us.apache.org/repos/asf/wicket/blob/ae41fa13/wicket-core/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-core/pom.xml b/wicket-core/pom.xml
index bf6006d..3b88940 100644
--- a/wicket-core/pom.xml
+++ b/wicket-core/pom.xml
@@ -68,15 +68,23 @@
 		</dependency>
 	</dependencies>
 	<build>
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>net.alchim31.maven</groupId>
+					<artifactId>yuicompressor-maven-plugin</artifactId>
+					<configuration>
+						<excludes>
+							<exclude>**/jquery*.js</exclude>
+						</excludes>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
 		<plugins>
 			<plugin>
 				<groupId>net.alchim31.maven</groupId>
 				<artifactId>yuicompressor-maven-plugin</artifactId>
-				<configuration>
-					<excludes>
-						<exclude>**/jquery*.js</exclude>
-					</excludes>
-				</configuration>
 			</plugin>
 		</plugins>
 	</build>

http://git-wip-us.apache.org/repos/asf/wicket/blob/ae41fa13/wicket-examples/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-examples/pom.xml b/wicket-examples/pom.xml
index c184b27..e7eb538 100644
--- a/wicket-examples/pom.xml
+++ b/wicket-examples/pom.xml
@@ -97,6 +97,11 @@
 			<artifactId>jhighlight</artifactId>
 		</dependency>
 		<dependency>
+			<groupId>javax.validation</groupId>
+			<artifactId>validation-api</artifactId>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
 			<groupId>log4j</groupId>
 			<artifactId>log4j</artifactId>
 		</dependency>
@@ -158,11 +163,6 @@
             <scope>compile</scope>
         </dependency>
 		<dependency>
-			<groupId>javax.validation</groupId>
-			<artifactId>validation-api</artifactId>
-			<scope>compile</scope>
-		</dependency>
-		<dependency>
 			<groupId>org.jboss.weld.servlet</groupId>
 			<artifactId>weld-servlet</artifactId>
 		</dependency>

http://git-wip-us.apache.org/repos/asf/wicket/blob/ae41fa13/wicket-jmx/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-jmx/pom.xml b/wicket-jmx/pom.xml
index 7efc48b..ca35450 100644
--- a/wicket-jmx/pom.xml
+++ b/wicket-jmx/pom.xml
@@ -33,12 +33,12 @@
 			<artifactId>cglib</artifactId>
 		</dependency>
 		<dependency>
-			<groupId>org.ow2.asm</groupId>
-			<artifactId>asm-util</artifactId>
-		</dependency>
-		<dependency>
 			<groupId>org.apache.wicket</groupId>
 			<artifactId>wicket-core</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.ow2.asm</groupId>
+			<artifactId>asm-util</artifactId>
+		</dependency>
 	</dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/wicket/blob/ae41fa13/wicket-user-guide/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-user-guide/pom.xml b/wicket-user-guide/pom.xml
index 5829954..50f6e97 100644
--- a/wicket-user-guide/pom.xml
+++ b/wicket-user-guide/pom.xml
@@ -30,28 +30,52 @@
 	<description>
 		Provides the user guide of wicket
 	</description>
-
+	<repositories>
+		<repository>
+			<id>grails</id>
+			<name>grails</name>
+			<url>http://repo.grails.org/grails/core</url>
+		</repository>
+		<repository>
+			<id>grails-plugins</id>
+			<name>grails-plugins</name>
+			<url>http://repo.grails.org/grails/plugins</url>
+		</repository>
+	</repositories>
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.grails</groupId>
+				<artifactId>grails-dependencies</artifactId>
+				<version>${grails.version}</version>
+				<type>pom</type>
+				<exclusions>
+					<exclusion>
+						<groupId>log4j</groupId>
+						<artifactId>log4j</artifactId>
+					</exclusion>
+					<exclusion>
+						<groupId>org.grails</groupId>
+						<artifactId>grails-plugin-log4j</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+			<dependency>
+				<groupId>org.grails</groupId>
+				<artifactId>grails-docs</artifactId>
+				<version>${grails.version}</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
 	<dependencies>
 		<dependency>
 			<groupId>org.grails</groupId>
 			<artifactId>grails-dependencies</artifactId>
-			<version>${grails.version}</version>
 			<type>pom</type>
-			<exclusions>
-				<exclusion>
-					<artifactId>log4j</artifactId>
-					<groupId>log4j</groupId>
-				</exclusion>
-				<exclusion>
-					<artifactId>grails-plugin-log4j</artifactId>
-					<groupId>org.grails</groupId>
-				</exclusion>
-			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.grails</groupId>
 			<artifactId>grails-docs</artifactId>
-			<version>${grails.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>org.slf4j</groupId>
@@ -63,19 +87,6 @@
 		</dependency>
 	</dependencies>
 
-	<repositories>
-		<repository>
-			<id>grails</id>
-			<name>grails</name>
-			<url>http://repo.grails.org/grails/core</url>
-		</repository>
-		<repository>
-			<id>grails-plugins</id>
-			<name>grails-plugins</name>
-			<url>http://repo.grails.org/grails/plugins</url>
-		</repository>
-	</repositories>
-
 	<build>
 		<plugins>
 			<plugin>
@@ -84,11 +95,11 @@
 			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-source-plugin</artifactId>
+				<artifactId>maven-javadoc-plugin</artifactId>
 			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-javadoc-plugin</artifactId>
+				<artifactId>maven-source-plugin</artifactId>
 			</plugin>
 		</plugins>
 		<pluginManagement>
@@ -103,18 +114,18 @@
 				</plugin>
 				<plugin>
 					<groupId>org.apache.maven.plugins</groupId>
-					<artifactId>maven-source-plugin</artifactId>
+					<artifactId>maven-javadoc-plugin</artifactId>
 					<configuration>
 						<!-- Just documentation, no sources -->
-						<skipSource>true</skipSource>
+						<skip>true</skip>
 					</configuration>
 				</plugin>
 				<plugin>
 					<groupId>org.apache.maven.plugins</groupId>
-					<artifactId>maven-javadoc-plugin</artifactId>
+					<artifactId>maven-source-plugin</artifactId>
 					<configuration>
 						<!-- Just documentation, no sources -->
-						<skip>true</skip>
+						<skipSource>true</skipSource>
 					</configuration>
 				</plugin>
 			</plugins>


[5/8] wicket git commit: Added a CSRF prevention measure

Posted by ad...@apache.org.
Added a CSRF prevention measure


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/1cbbc76c
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/1cbbc76c
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/1cbbc76c

Branch: refs/heads/WICKET-5906-7.x
Commit: 1cbbc76cbd99b1e2a825ab0f43ed8e6ff738e6cf
Parents: 70d0965
Author: Martijn Dashorst <ma...@gmail.com>
Authored: Sun Jun 7 16:34:55 2015 +0200
Committer: Andrea Del Bene <“adelbene@apache.org”>
Committed: Mon Jun 15 10:20:28 2015 +0200

----------------------------------------------------------------------
 .../CsrfPreventionRequestCycleListener.java     | 754 +++++++++++++++++++
 .../CsrfPreventionRequestCycleListenerTest.java | 639 ++++++++++++++++
 .../apache/wicket/protocol/http/ThirdPage.html  |  10 +
 .../apache/wicket/protocol/http/ThirdPage.java  |  44 ++
 4 files changed, 1447 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/1cbbc76c/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
new file mode 100644
index 0000000..21d5569
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
@@ -0,0 +1,754 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.http;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.core.request.handler.IPageRequestHandler;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.flow.AbortWithHttpErrorCodeException;
+import org.apache.wicket.util.lang.Checks;
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Prevents CSRF attacks on Wicket components by checking the {@code Origin} HTTP header for cross
+ * domain requests. By default only checks requests that try to perform an action on a component,
+ * such as a form submit, or link click.
+ * <p>
+ * <h3>Installation</h3>
+ * <p>
+ * You can enable this CSRF prevention filter by adding it to the request cycle listeners in your
+ * {@link WebApplication#init() application's init method}:
+ * 
+ * <pre>
+ * &#064;Override
+ * protected void init()
+ * {
+ * 	// ...
+ * 	getRequestCycleListeners().add(new CsrfPreventionRequestCycleListener());
+ * 	// ...
+ * }
+ * </pre>
+ * <p>
+ * <h3>Configuration</h3>
+ * <p>
+ * A missing {@code Origin} HTTP header is (by default) handled as if it were a good request and
+ * accepted. You can {@link #setNoOriginAction(CsrfAction) configure the specific action} to a
+ * different value, suppressing or aborting the request when the {@code Origin} HTTP header is
+ * missing.
+ * <p>
+ * When the {@code Origin} HTTP header is present and has the value {@code null} it is considered to
+ * be from a "privacy-sensitive" context and will trigger the conflicting origin action. You can
+ * customize what happens in those actions by overriding the respective {@code onXXXX} methods.
+ * <p>
+ * When the {@code Origin} HTTP header is present but doesn't match the requested URL this listener
+ * will by default throw a HTTP error ( {@code 400 BAD REQUEST}) and abort the request. You can
+ * {@link #setConflictingOriginAction(CsrfAction) configure} this specific action.
+ * <p>
+ * When you want to accept certain cross domain request from a range of hosts, you can
+ * {@link #addAcceptedOrigin(String) whitelist those domains}.
+ * <p>
+ * You can {@link #isEnabled() enable or disable} this listener by overriding {@link #isEnabled()}.
+ * <p>
+ * You can {@link #isChecked(IRequestablePage) customize} whether a particular page should be
+ * checked for CSRF requests. For example you can skip checking pages that have a
+ * {@code @NoCsrfCheck} annotation, or only those pages that extend your base secure page class. For
+ * example:
+ * 
+ * <pre>
+ * &#064;Override
+ * protected boolean isChecked(IRequestablePage requestedPage)
+ * {
+ * 	return requestedPage.getPage() instanceof SecurePage;
+ * }
+ * </pre>
+ * <p>
+ * You can also tweak the request handlers that are checked. The CSRF prevention request cycle
+ * listener checks only action handlers, not render handlers. Override
+ * {@link #isChecked(IRequestHandler)} to customize this behavior.
+ * </p>
+ * <p>
+ * You can override the default actions that are performed by overriding the event handlers for
+ * them:
+ * <ul>
+ * <li>{@link #onWhitelisted(HttpServletRequest, String, IRequestablePage)} when an origin was
+ * whitelisted</li>
+ * <li>{@link #onMatchingOrigin(HttpServletRequest, String, IRequestablePage)} when an origin was
+ * matching</li>
+ * <li>{@link #onAborted(HttpServletRequest, String, IRequestablePage)} when an origin was in
+ * conflict and the request should be aborted</li>
+ * <li>{@link #onAllowed(HttpServletRequest, String, IRequestablePage)} when an origin was in
+ * conflict and the request should be allowed</li>
+ * <li>{@link #onSuppressed(HttpServletRequest, String, IRequestablePage)} when an origin was in
+ * conflict and the request should be suppressed</li>
+ * </ul>
+ */
+public class CsrfPreventionRequestCycleListener extends AbstractRequestCycleListener
+	implements
+		IRequestCycleListener
+{
+	private static final Logger log = LoggerFactory.getLogger(CsrfPreventionRequestCycleListener.class);
+
+	/**
+	 * The action to perform when a missing or conflicting Origin header is detected.
+	 */
+	public static enum CsrfAction {
+		/** Aborts the request and throws an exception when a CSRF request is detected. */
+		ABORT {
+			@Override
+			public String toString()
+			{
+				return "aborted";
+			}
+		},
+
+		/**
+		 * Ignores the action of a CSRF request, and just renders the page it was targeted against.
+		 */
+		SUPPRESS {
+			@Override
+			public String toString()
+			{
+				return "suppressed";
+			}
+		},
+
+		/** Detects a CSRF request, logs it and allows the request to continue. */
+		ALLOW {
+			@Override
+			public String toString()
+			{
+				return "allowed";
+			}
+		},
+	}
+
+	/**
+	 * Action to perform when no Origin header is present in the request.
+	 */
+	private CsrfAction noOriginAction = CsrfAction.ALLOW;
+
+	/**
+	 * Action to perform when a conflicing Origin header is found.
+	 */
+	private CsrfAction conflictingOriginAction = CsrfAction.ABORT;
+
+	/**
+	 * The error code to report when the action to take for a CSRF request is {@code ERROR}. Default
+	 * {@code 400 BAD REQUEST}.
+	 */
+	private int errorCode = javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+
+	/**
+	 * The error message to report when the action to take for a CSRF request is {@code ERROR}.
+	 * Default {@code "Origin does not correspond to request"}.
+	 */
+	private String errorMessage = "Origin does not correspond to request";
+
+	/**
+	 * A white list of accepted origins (host names/domain names) presented as
+	 * &lt;domainname&gt;.&lt;TLD&gt;. The domain part can contain subdomains.
+	 */
+	private Collection<String> acceptedOrigins = new ArrayList<>();
+
+	/**
+	 * Sets the action when no Origin header is present in the request. Default {@code ALLOW}.
+	 *
+	 * @param action
+	 *            the alternate action
+	 *
+	 * @return this (for chaining)
+	 */
+	public CsrfPreventionRequestCycleListener setNoOriginAction(CsrfAction action)
+	{
+		this.noOriginAction = action;
+		return this;
+	}
+
+	/**
+	 * Sets the action when a conflicting Origin header is detected. Default is {@code ERROR}.
+	 *
+	 * @param action
+	 *            the alternate action
+	 *
+	 * @return this
+	 */
+	public CsrfPreventionRequestCycleListener setConflictingOriginAction(CsrfAction action)
+	{
+		this.conflictingOriginAction = action;
+		return this;
+	}
+
+	/**
+	 * Modifies the HTTP error code in the exception when a conflicting Origin header is detected.
+	 *
+	 * @param errorCode
+	 *            the alternate HTTP error code, default {@code 400 BAD REQUEST}
+	 *
+	 * @return this
+	 */
+	public CsrfPreventionRequestCycleListener setErrorCode(int errorCode)
+	{
+		this.errorCode = errorCode;
+		return this;
+	}
+
+	/**
+	 * Modifies the HTTP message in the exception when a conflicting Origin header is detected.
+	 *
+	 * @param errorMessage
+	 *            the alternate message
+	 *
+	 * @return this
+	 */
+	public CsrfPreventionRequestCycleListener setErrorMessage(String errorMessage)
+	{
+		this.errorMessage = errorMessage;
+		return this;
+	}
+
+	/**
+	 * Adds an origin (host name/domain name) to the white list. An origin is in the form of
+	 * &lt;domainname&gt;.&lt;TLD&gt;, and can contain a subdomain. Every Origin header that matches
+	 * a domain from the whitelist is accepted and not checked any further for CSRF issues.
+	 * 
+	 * E.g. when {@code example.com} is in the white list, this allows requests from (i.e. with an
+	 * {@code Origin:} header containing) {@code example.com} and {@code blabla.example.com} but
+	 * rejects requests from {@code blablaexample.com} and {@code example2.com}.
+	 *
+	 * @param acceptedOrigin
+	 *            the acceptable origin
+	 * @return this
+	 */
+	public CsrfPreventionRequestCycleListener addAcceptedOrigin(String acceptedOrigin)
+	{
+		Checks.notNull("acceptedOrigin", acceptedOrigin);
+
+		// strip any leading dot characters
+		final int len = acceptedOrigin.length();
+		int i = 0;
+		while (i < len && acceptedOrigin.charAt(i) == '.')
+		{
+			i++;
+		}
+		acceptedOrigins.add(acceptedOrigin.substring(i));
+		return this;
+	}
+
+	@Override
+	public void onBeginRequest(RequestCycle cycle)
+	{
+		if (log.isDebugEnabled())
+		{
+			HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
+				.getContainerRequest();
+			String origin = containerRequest.getHeader("Origin");
+			log.debug("Request header Origin: {}", origin);
+		}
+	}
+
+	/**
+	 * Dynamic override for enabling/disabling the CSRF detection. Might be handy for specific
+	 * tenants in a multi-tenant application. When false, the CSRF detection is not performed for
+	 * the running request. Default {@code true}
+	 * 
+	 * @return {@code true} when the CSRF checks need to be performed.
+	 */
+	protected boolean isEnabled()
+	{
+		return true;
+	}
+
+	/**
+	 * Override to limit whether the request to the specific page should be checked for a possible
+	 * CSRF attack.
+	 * 
+	 * @param targetedPage
+	 *            the page that is the target for the action
+	 * @return {@code true} when the request to the page should be checked for CSRF issues.
+	 */
+	protected boolean isChecked(IRequestablePage targetedPage)
+	{
+		return true;
+	}
+
+	/**
+	 * Override to change the request handler types that are checked. Currently only action handlers
+	 * (form submits, link clicks, AJAX events) are checked for a matching Origin HTTP header.
+	 * 
+	 * @param handler
+	 *            the handler that is currently processing
+	 * @return true when the Origin HTTP header should be checked for this {@code handler}
+	 */
+	protected boolean isChecked(IRequestHandler handler)
+	{
+		return handler instanceof IPageRequestHandler &&
+			!(handler instanceof RenderPageRequestHandler);
+	}
+
+	@Override
+	public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
+	{
+		if (!isEnabled())
+		{
+			log.trace("CSRF listener is disabled, no checks performed");
+			return;
+		}
+
+		// check if the request is targeted at a page
+		if (isChecked(handler))
+		{
+			IPageRequestHandler prh = (IPageRequestHandler)handler;
+			IRequestablePage targetedPage = prh.getPage();
+			HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
+				.getContainerRequest();
+			String origin = containerRequest.getHeader("Origin");
+
+			// Check if the page should be CSRF protected
+			if (isChecked(targetedPage))
+			{
+				// if so check the Origin HTTP header
+				checkOrigin(containerRequest, origin, targetedPage);
+			}
+			else
+			{
+				log.debug("Targeted page {} was opted out of the CSRF origin checks, allowed",
+					targetedPage.getClass().getName());
+				allowHandler(containerRequest, origin, targetedPage);
+			}
+		}
+		else
+		{
+			if (log.isTraceEnabled())
+				log.trace("Resolved handler {} doesn't target a page, no CSRF check performed",
+					handler.getClass().getName());
+		}
+	}
+
+	/**
+	 * Performs the check of the {@code Origin} header that is targeted at the {@code page}.
+	 *
+	 * @param request
+	 *            the current container request
+	 * @param origin
+	 *            the {@code Origin} header
+	 * @param page
+	 *            the page that is the target of the request
+	 */
+	private void checkOrigin(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		if (origin == null || origin.isEmpty())
+		{
+			log.debug("Origin-header not present in request, {}", noOriginAction);
+			switch (noOriginAction)
+			{
+				case ALLOW :
+					allowHandler(request, origin, page);
+					break;
+				case SUPPRESS :
+					suppressHandler(request, origin, page);
+					break;
+				case ABORT :
+					abortHandler(request, origin, page);
+					break;
+			}
+			return;
+		}
+		origin = origin.toLowerCase();
+
+		// if the origin is a know and trusted origin, don't check any further but allow the request
+		if (isWhitelistedOrigin(origin))
+		{
+			whitelistedHandler(request, origin, page);
+			return;
+		}
+
+		// check if the origin HTTP header matches the request URI
+		if (!isLocalOrigin(request, origin))
+		{
+			log.debug("Origin-header conflicts with request origin, {}", conflictingOriginAction);
+			switch (conflictingOriginAction)
+			{
+				case ALLOW :
+					allowHandler(request, origin, page);
+					break;
+				case SUPPRESS :
+					suppressHandler(request, origin, page);
+					break;
+				case ABORT :
+					abortHandler(request, origin, page);
+					break;
+			}
+		}
+		else
+		{
+			matchingOrigin(request, origin, page);
+		}
+	}
+
+	/**
+	 * Checks whether the domain part of the {@code Origin} HTTP header is whitelisted.
+	 * 
+	 * @param origin
+	 *            the {@code Origin} HTTP header
+	 * @return {@code true} when the origin domain was whitelisted
+	 */
+	private boolean isWhitelistedOrigin(final String origin)
+	{
+		try
+		{
+			final URI originUri = new URI(origin);
+			final String originHost = originUri.getHost();
+			if (Strings.isEmpty(originHost))
+				return false;
+			for (String whitelistedOrigin : acceptedOrigins)
+			{
+				if (originHost.equalsIgnoreCase(whitelistedOrigin) ||
+					originHost.endsWith("." + whitelistedOrigin))
+				{
+					log.trace("Origin {} matched whitelisted origin {}, request accepted", origin,
+						whitelistedOrigin);
+					return true;
+				}
+			}
+		}
+		catch (URISyntaxException e)
+		{
+			log.debug("Origin: {} not parseable as an URI. Whitelisted-origin check skipped.",
+				origin);
+		}
+
+		return false;
+	}
+
+	/**
+	 * Checks whether the {@code Origin} HTTP header of the request matches where the request came
+	 * from.
+	 * 
+	 * @param containerRequest
+	 *            the current container request
+	 * @param originHeader
+	 *            the contents of the {@code Origin} HTTP header
+	 * @return {@code true} when the origin of the request matches the {@code Origin} HTTP header
+	 */
+	private boolean isLocalOrigin(HttpServletRequest containerRequest, String originHeader)
+	{
+		// Make comparable strings from Origin and Location
+		String origin = getOriginHeaderOrigin(originHeader);
+		if (origin == null)
+			return false;
+
+		String request = getLocationHeaderOrigin(containerRequest);
+		if (request == null)
+			return false;
+
+		return origin.equalsIgnoreCase(request);
+	}
+
+	/**
+	 * Creates a RFC-6454 comparable origin from the {@code origin} string.
+	 * 
+	 * @param origin
+	 *            the contents of the Origin HTTP header
+	 * @return only the scheme://host[:port] part, or {@code null} when the origin string is not
+	 *         compliant
+	 */
+	private String getOriginHeaderOrigin(String origin)
+	{
+		// the request comes from a privacy sensitive context, flag as non-local origin. If
+		// alternative action is required, an implementor can override any of the onAborted,
+		// onSuppressed or onAllowed and implement such needed action.
+
+		if ("null".equals(origin))
+			return null;
+
+		StringBuilder target = new StringBuilder();
+
+		try
+		{
+			URI originUri = new URI(origin);
+			String scheme = originUri.getScheme();
+			if (scheme == null)
+			{
+				return null;
+			}
+			else
+			{
+				scheme = scheme.toLowerCase(Locale.ENGLISH);
+			}
+
+			target.append(scheme);
+			target.append("://");
+
+			String host = originUri.getHost();
+			if (host == null)
+			{
+				return null;
+			}
+			target.append(host);
+
+			int port = originUri.getPort();
+			if (port != -1 && "http".equals(scheme) && port != 80 || "https".equals(scheme) &&
+				port != 443)
+			{
+				target.append(':');
+				target.append(port);
+			}
+			return target.toString();
+		}
+		catch (URISyntaxException e)
+		{
+			log.debug("Invalid Origin header provided: {}, marked conflicting", origin);
+			return null;
+		}
+	}
+
+	/**
+	 * Creates a RFC-6454 comparable origin from the {@code request} requested resource.
+	 * 
+	 * @param request
+	 *            the incoming request
+	 * @return only the scheme://host[:port] part, or {@code null} when the origin string is not
+	 *         compliant
+	 */
+	private String getLocationHeaderOrigin(HttpServletRequest request)
+	{
+		// Build scheme://host:port from request
+		StringBuilder target = new StringBuilder();
+		String scheme = request.getScheme();
+		if (scheme == null)
+		{
+			return null;
+		}
+		else
+		{
+			scheme = scheme.toLowerCase(Locale.ENGLISH);
+		}
+		target.append(scheme);
+		target.append("://");
+
+		String host = request.getServerName();
+		if (host == null)
+		{
+			return null;
+		}
+		target.append(host);
+
+		int port = request.getServerPort();
+		if ("http".equals(scheme) && port != 80 || "https".equals(scheme) && port != 443)
+		{
+			target.append(':');
+			target.append(port);
+		}
+
+		return target.toString();
+	}
+
+	/**
+	 * Handles the case where an origin is in the whitelist. Default action is to allow the
+	 * whitelisted origin.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	private void whitelistedHandler(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		onWhitelisted(request, origin, page);
+		if (log.isDebugEnabled())
+		{
+			log.debug("CSRF Origin {} was whitelisted, allowed for page {}", origin,
+				page.getClass().getName());
+		}
+	}
+
+	/**
+	 * Called when the origin was available in the whitelist. Override this method to implement your
+	 * own custom action.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	protected void onWhitelisted(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+	}
+
+	/**
+	 * Handles the case where an origin was checked and matched the request origin. Default action
+	 * is to allow the whitelisted origin.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	private void matchingOrigin(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		onMatchingOrigin(request, origin, page);
+		if (log.isDebugEnabled())
+		{
+			log.debug("CSRF Origin {} matched requested resource, allowed for page {}", origin,
+				page.getClass().getName());
+		}
+	}
+
+	/**
+	 * Called when the origin HTTP header matched the request. Override this method to implement
+	 * your own custom action.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	protected void onMatchingOrigin(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+	}
+
+	/**
+	 * Handles the case where an Origin HTTP header was not present or did not match the request
+	 * origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code ALLOW}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	private void allowHandler(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		onAllowed(request, origin, page);
+		log.info("Possible CSRF attack, request URL: {}, Origin: {}, action: allowed",
+			request.getRequestURL(), origin);
+	}
+
+	/**
+	 * Override this method to customize the case where an Origin HTTP header was not present or did
+	 * not match the request origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code ALLOW}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	protected void onAllowed(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+	}
+
+	/**
+	 * Handles the case where an Origin HTTP header was not present or did not match the request
+	 * origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code SUPPRESS}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	private void suppressHandler(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		onSuppressed(request, origin, page);
+		log.info("Possible CSRF attack, request URL: {}, Origin: {}, action: suppressed",
+			request.getRequestURL(), origin);
+		throw new RestartResponseException(page);
+	}
+
+	/**
+	 * Override this method to customize the case where an Origin HTTP header was not present or did
+	 * not match the request origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code SUPPRESSED}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	protected void onSuppressed(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+	}
+
+	/**
+	 * Handles the case where an Origin HTTP header was not present or did not match the request
+	 * origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code ABORT}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	private void abortHandler(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+		onAborted(request, origin, page);
+		log.info(
+			"Possible CSRF attack, request URL: {}, Origin: {}, action: aborted with error {} {}",
+			request.getRequestURL(), origin, errorCode, errorMessage);
+		throw new AbortWithHttpErrorCodeException(errorCode, errorMessage);
+	}
+
+	/**
+	 * Override this method to customize the case where an Origin HTTP header was not present or did
+	 * not match the request origin, and the corresponding action ({@link #noOriginAction} or
+	 * {@link #conflictingOriginAction}) is set to {@code ABORTED}.
+	 * 
+	 * @param request
+	 *            the request
+	 * @param origin
+	 *            the contents of the {@code Origin} HTTP header, may be {@code null} or empty
+	 * @param page
+	 *            the page that is targeted with this request
+	 */
+	protected void onAborted(HttpServletRequest request, String origin, IRequestablePage page)
+	{
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/1cbbc76c/wicket-core/src/test/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListenerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListenerTest.java
new file mode 100644
index 0000000..4dca4b8
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListenerTest.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.http;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.protocol.http.CsrfPreventionRequestCycleListener.CsrfAction;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.component.IRequestablePage;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for the CsrfPreventionRequestCycleListener. FirstPage has a link that when clicked
+ * should render SecondPage.
+ */
+public class CsrfPreventionRequestCycleListenerTest extends WicketTestCase
+{
+	/**
+	 * Sets up the test cases. Installs the CSRF listener and renders the FirstPage.
+	 */
+	@Before
+	public void startWithFirstPageRender()
+	{
+		WebApplication application = tester.getApplication();
+
+		csrfListener = new MockCsrfPreventionRequestCycleListener();
+		setErrorCode(errorCode);
+		setErrorMessage(errorMessage);
+		application.getRequestCycleListeners().add(csrfListener);
+
+		// Rendering a page is allowed, regardless of Origin (this allows external links into your
+		// website to function)
+
+		tester.addRequestHeader("Origin", "https://google.com/");
+
+		tester.startPage(FirstPage.class);
+		tester.assertRenderedPage(FirstPage.class);
+	}
+
+	/** Tests that disabling the CSRF listener doesn't check Origin headers. */
+	@Test
+	public void disabledListenerDoesntCheckAnything()
+	{
+		csrfEnabled = false;
+		tester.clickLink("link");
+
+		assertOriginsNotChecked();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests that disabling the CSRF listener doesn't check Origin headers. */
+	@Test
+	public void disabledListenerDoesntCheckMismatchedOrigin()
+	{
+		csrfEnabled = false;
+		tester.addRequestHeader("Origin", "http://malicioussite.com/");
+		tester.clickLink("link");
+		assertOriginsNotChecked();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests the default setting of allowing a missing Origin. */
+	@Test
+	public void withoutOriginAllowed()
+	{
+		tester.clickLink("link");
+		assertConflictingOriginsRequestAllowed();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests the alternative action of suppressing a request without Origin header */
+	@Test
+	public void withoutOriginSuppressed()
+	{
+		csrfListener.setNoOriginAction(CsrfAction.SUPPRESS);
+		tester.clickLink("link");
+		tester.assertRenderedPage(FirstPage.class);
+		assertConflictingOriginsRequestSuppressed();
+	}
+
+	/** Tests the alternative action of aborting a request without Origin header */
+	@Test
+	public void withoutOriginAborted()
+	{
+		csrfListener.setNoOriginAction(CsrfAction.ABORT);
+		tester.clickLink("link");
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests when the Origin header matches the request. */
+	@Test
+	public void matchingOriginsAllowed()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.ALLOW);
+		tester.addRequestHeader("Origin", "http://localhost/");
+
+		tester.clickLink("link");
+
+		assertOriginsMatched();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests when the default action is changed to ALLOW when origins conflict. */
+	@Test
+	public void conflictingOriginsAllowed()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.ALLOW);
+		tester.addRequestHeader("Origin", "http://example.com/");
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAllowed();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests when the default action is changed to SUPPRESS when origins conflict. */
+	@Test
+	public void conflictingOriginsSuppressed()
+	{
+		tester.addRequestHeader("Origin", "http://example.com/");
+		csrfListener.setConflictingOriginAction(CsrfAction.SUPPRESS);
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestSuppressed();
+		tester.assertRenderedPage(FirstPage.class);
+	}
+
+	/** Tests the default action to ABORT when origins conflict. */
+	@Test
+	public void conflictingOriginsAborted()
+	{
+		tester.addRequestHeader("Origin", "http://example.com/");
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests custom error code/message when the default action is ABORT. */
+	@Test
+	public void conflictingOriginsAbortedWith401Unauhorized()
+	{
+		setErrorCode(401);
+		setErrorMessage("NOT AUTHORIZED");
+
+		tester.addRequestHeader("Origin", "http://example.com/");
+		csrfListener.setNoOriginAction(CsrfAction.ABORT);
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests whitelisting for conflicting origins. */
+	@Test
+	public void conflictingButWhitelistedOriginAllowed()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.ALLOW);
+		csrfListener.addAcceptedOrigin("example.com");
+		tester.addRequestHeader("Origin", "http://example.com/");
+
+		tester.clickLink("link");
+
+		assertOriginsWhitelisted();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests whitelisting with conflicting subdomain origin. */
+	@Test
+	public void conflictingButWhitelistedSubdomainOriginAllowed()
+	{
+		csrfListener.addAcceptedOrigin("example.com");
+		csrfListener.setConflictingOriginAction(CsrfAction.ALLOW);
+
+		tester.addRequestHeader("Origin", "http://foo.example.com/");
+
+		tester.clickLink("link");
+
+		tester.assertRenderedPage(SecondPage.class);
+		assertOriginsWhitelisted();
+	}
+
+	/**
+	 * Tests when the listener is disabled for a specific page (by overriding
+	 * {@link CsrfPreventionRequestCycleListener#isChecked(IRequestablePage)})
+	 */
+	@Test
+	public void conflictingOriginPageNotCheckedAllowed()
+	{
+		tester.addRequestHeader("Origin", "http://example.com/");
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		// disable the check for this page
+		checkPage = false;
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAllowed();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests overriding the onSuppressed method for a conflicting origin. */
+	@Test
+	public void conflictingOriginSuppressedCallsCustomHandler()
+	{
+		// redirect to third page to ensure we are not suppressed to the first page, nor that the
+		// request was not suppressed and the second page was rendered erroneously
+
+		Runnable thirdPageRedirect = new Runnable()
+		{
+			@Override
+			public void run()
+			{
+				throw new RestartResponseException(new ThirdPage());
+			}
+		};
+		setSuppressHandler(thirdPageRedirect);
+		csrfListener.setConflictingOriginAction(CsrfAction.SUPPRESS);
+
+		tester.addRequestHeader("Origin", "http://example.com/");
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestSuppressed();
+		tester.assertRenderedPage(ThirdPage.class);
+	}
+
+	/** Tests overriding the onAllowed method for a conflicting origin. */
+	@Test
+	public void conflictingOriginAllowedCallsCustomHandler()
+	{
+		// redirect to third page to ensure we are not suppressed to the first page, nor that the
+		// request was not allowed and the second page was rendered erroneously
+
+		Runnable thirdPageRedirect = new Runnable()
+		{
+			@Override
+			public void run()
+			{
+				throw new RestartResponseException(new ThirdPage());
+			}
+		};
+		setAllowHandler(thirdPageRedirect);
+		csrfListener.setConflictingOriginAction(CsrfAction.ALLOW);
+
+		tester.addRequestHeader("Origin", "http://example.com/");
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAllowed();
+		tester.assertRenderedPage(ThirdPage.class);
+	}
+
+	/** Tests overriding the onAborted method for a conflicting origin. */
+	@Test
+	public void conflictingOriginAbortedCallsCustomHandler()
+	{
+		// redirect to third page to ensure we are not suppressed to the first page, nor that the
+		// request was not aborted and the second page was rendered erroneously
+
+		Runnable thirdPageRedirect = new Runnable()
+		{
+			@Override
+			public void run()
+			{
+				throw new RestartResponseException(new ThirdPage());
+			}
+		};
+		setAbortHandler(thirdPageRedirect);
+
+		tester.addRequestHeader("Origin", "http://example.com/");
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		tester.clickLink("link");
+
+		// have to check manually, as the assert checks the error code (which is not set due to our
+		// custom handler)
+
+		if (!aborted)
+			throw new AssertionError("Request was not aborted");
+
+		tester.assertRenderedPage(ThirdPage.class);
+	}
+
+	/** Tests whether a different port, but same scheme and hostname is considered a conflict. */
+	@Test
+	public void differentPortOriginAborted()
+	{
+		tester.addRequestHeader("Origin", "http://localhost:8080");
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests whether a different scheme, but same port and hostname is considered a conflict. */
+	@Test
+	public void differentSchemeOriginAborted()
+	{
+		tester.addRequestHeader("Origin", "https://localhost");
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		tester.clickLink("link");
+
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests whether only the hostname is considered when matching the Origin header. */
+	@Test
+	public void longerOriginAllowed()
+	{
+		tester.addRequestHeader("Origin", "http://localhost/supercalifragilisticexpialidocious");
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		tester.clickLink("link");
+
+		assertOriginsMatched();
+		tester.assertRenderedPage(SecondPage.class);
+	}
+
+	/** Tests whether AJAX Links are checked through the CSRF listener */
+	@Test
+	public void simulatedCsrfAttackThroughAjaxIsPrevented()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.ABORT);
+
+		// first render a page in the user's session
+		tester.addRequestHeader("Origin", "http://localhost");
+		tester.startPage(ThirdPage.class);
+
+		assertOriginsNotChecked();
+		tester.assertRenderedPage(ThirdPage.class);
+
+		// then click on a link from another external page
+		tester.addRequestHeader("Origin", "http://attacker.com/");
+		tester.clickLink("link", true);
+
+		assertConflictingOriginsRequestAborted();
+	}
+
+	/** Tests whether AJAX Links are checked through the CSRF listener */
+	@Test
+	public void simulatedCsrfAttackIsSuppressed()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.SUPPRESS);
+
+		// first render a page in the user's session
+		tester.addRequestHeader("Origin", "http://localhost");
+		tester.startPage(ThirdPage.class);
+
+		assertOriginsNotChecked();
+		tester.assertRenderedPage(ThirdPage.class);
+
+		// then click on a link from another external page
+		tester.addRequestHeader("Origin", "http://attacker.com/");
+		tester.clickLink("link", true);
+
+		assertConflictingOriginsRequestSuppressed();
+		tester.assertRenderedPage(ThirdPage.class);
+	}
+
+	/** Tests whether form submits are checked through the CSRF listener */
+	@Test
+	public void simulatedCsrfAttackOnFormIsSuppressed()
+	{
+		csrfListener.setConflictingOriginAction(CsrfAction.SUPPRESS);
+
+		// first render a page in the user's session
+		tester.addRequestHeader("Origin", "http://localhost");
+		tester.startPage(ThirdPage.class);
+
+		assertOriginsNotChecked();
+		tester.assertRenderedPage(ThirdPage.class);
+
+		// then click on a link from another external page
+		tester.addRequestHeader("Origin", "http://attacker.com/");
+		tester.submitForm("form");
+
+		assertConflictingOriginsRequestSuppressed();
+		tester.assertRenderedPage(ThirdPage.class);
+	}
+
+	/*
+	 * Infrastructure code for these test cases starts here.
+	 */
+
+	/** The listener under test */
+	private CsrfPreventionRequestCycleListener csrfListener;
+
+	/** Flag for enabling/disabling the CSRF listener */
+	private boolean csrfEnabled = true;
+
+	/** Flag for enabling/disabling the page check of the CSRF listener */
+	private boolean checkPage = true;
+
+	/** Value for reporting the error code when the request was aborted */
+	private int errorCode = 400;
+
+	/** Value for reporting the error message when the request was aborted */
+	private String errorMessage = "BAD REQUEST";
+
+	/** Checks for asserting the functionality of the CSRF listener */
+	private boolean matched, whitelisted, aborted, allowed, suppressed;
+
+	/**
+	 * Manner to override the default check whether the current request handler should be checked
+	 * for CSRF attacks.
+	 */
+	private Predicate<IRequestHandler> customRequestHandlerCheck;
+
+	/**
+	 * Handlers for specific tests (ensures that the listener calls the right handler in the right
+	 * circumstance.
+	 */
+	private Runnable abortHandler, allowHandler, suppressHandler, matchedHandler, whitelistHandler;
+
+	private void setErrorCode(int errorCode)
+	{
+		this.errorCode = errorCode;
+		csrfListener.setErrorCode(errorCode);
+	}
+
+	private void setCustomRequestHandlerCheck(Predicate<IRequestHandler> check)
+	{
+		this.customRequestHandlerCheck = check;
+	}
+
+	private void setErrorMessage(String errorMessage)
+	{
+		this.errorMessage = errorMessage;
+		csrfListener.setErrorMessage(errorMessage);
+	}
+
+	private void setAbortHandler(Runnable abortHandler)
+	{
+		this.abortHandler = abortHandler;
+	}
+
+	private void setAllowHandler(Runnable allowHandler)
+	{
+		this.allowHandler = allowHandler;
+	}
+
+	private void setSuppressHandler(Runnable suppressHandler)
+	{
+		this.suppressHandler = suppressHandler;
+	}
+
+	private void setWhitelistHandler(Runnable whitelistHandler)
+	{
+		this.whitelistHandler = whitelistHandler;
+	}
+
+	private void setMatchedHandler(Runnable matchedHandler)
+	{
+		this.matchedHandler = matchedHandler;
+	}
+
+	/**
+	 * Asserts that the origins were checked, and found matching.
+	 */
+	private void assertOriginsMatched()
+	{
+		if (!matched)
+			throw new AssertionError("Origins were not matched");
+	}
+
+	/**
+	 * Asserts that the origins were not checked, because the origin was on the whitelist.
+	 */
+	private void assertOriginsWhitelisted()
+	{
+		if (!whitelisted)
+			throw new AssertionError("Origins were not whitelisted");
+	}
+
+	/**
+	 * Asserts that the origins were checked, found conflicting, had an action "ABORTED" and returns
+	 * a HTTP error.
+	 */
+	private void assertConflictingOriginsRequestAborted()
+	{
+		if (!aborted)
+			throw new AssertionError("Request was not aborted");
+
+		assertThat("Response error code", tester.getLastResponse().getStatus(), is(errorCode));
+		assertThat("Response error message", tester.getLastResponse().getErrorMessage(),
+			is(errorMessage));
+	}
+
+	/**
+	 * Asserts that the origins were checked, found conflicting and had an action "SUPPRESS".
+	 */
+	private void assertConflictingOriginsRequestSuppressed()
+	{
+		if (!suppressed)
+			throw new AssertionError("Request was not suppressed");
+	}
+
+	/**
+	 * Asserts that the origins were checked, found conflicting and had an action "ALLOWED".
+	 */
+	private void assertConflictingOriginsRequestAllowed()
+	{
+		if (!allowed)
+			throw new AssertionError("Request was not allowed");
+	}
+
+	/**
+	 * Asserts that the origins were checked and found non-conflicting.
+	 */
+	private void assertOriginsCheckedButNotConflicting()
+	{
+		if (aborted)
+			throw new AssertionError("Origin was checked and aborted");
+		if (suppressed)
+			throw new AssertionError("Origin was checked and suppressed");
+		if (allowed)
+			throw new AssertionError("Origin was checked and allowed");
+		if (whitelisted)
+			throw new AssertionError("Origin was whitelisted");
+		if (!matched)
+			throw new AssertionError("Origin was not checked");
+	}
+
+	/**
+	 * Asserts that no check was performed at all.
+	 */
+	private void assertOriginsNotChecked()
+	{
+		if (aborted)
+			throw new AssertionError("Request was checked and aborted");
+		if (suppressed)
+			throw new AssertionError("Request was checked and suppressed");
+		if (allowed)
+			throw new AssertionError("Request was checked and allowed");
+		if (whitelisted)
+			throw new AssertionError("Origin was whitelisted");
+		if (matched)
+			throw new AssertionError("Origin was checked and matched");
+	}
+
+	private final class MockCsrfPreventionRequestCycleListener extends
+		CsrfPreventionRequestCycleListener
+	{
+		@Override
+		protected boolean isEnabled()
+		{
+			return csrfEnabled;
+		}
+
+		@Override
+		protected boolean isChecked(IRequestHandler handler)
+		{
+			if (customRequestHandlerCheck != null)
+				return customRequestHandlerCheck.apply(handler);
+
+			return super.isChecked(handler);
+		}
+
+		@Override
+		protected boolean isChecked(IRequestablePage targetedPage)
+		{
+			return checkPage;
+		}
+
+		@Override
+		protected void onAborted(HttpServletRequest containerRequest, String origin,
+			IRequestablePage page)
+		{
+			aborted = true;
+			if (abortHandler != null)
+				abortHandler.run();
+		}
+
+		@Override
+		protected void onAllowed(HttpServletRequest containerRequest, String origin,
+			IRequestablePage page)
+		{
+			allowed = true;
+			if (allowHandler != null)
+				allowHandler.run();
+		}
+
+		@Override
+		protected void onSuppressed(HttpServletRequest containerRequest, String origin,
+			IRequestablePage page)
+		{
+			suppressed = true;
+			if (suppressHandler != null)
+				suppressHandler.run();
+		}
+
+		@Override
+		protected void onMatchingOrigin(HttpServletRequest containerRequest, String origin,
+			IRequestablePage page)
+		{
+			matched = true;
+			if (matchedHandler != null)
+				matchedHandler.run();
+		}
+
+		@Override
+		protected void onWhitelisted(HttpServletRequest containerRequest, String origin,
+			IRequestablePage page)
+		{
+			whitelisted = true;
+			if (whitelistHandler != null)
+				whitelistHandler.run();
+		}
+	}
+
+	// Remove when migration to Java 8 is completed
+	private interface Predicate<T>
+	{
+		boolean apply(T t);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/1cbbc76c/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.html b/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.html
new file mode 100644
index 0000000..f081737
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html xmlns:wicket>
+<body>
+<h1>Third page</h1>
+<a href="#" wicket:id="link">AJAX Fallback Link</a>
+<form wicket:id="form">
+<input type="submit">
+</form>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/1cbbc76c/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.java b/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.java
new file mode 100644
index 0000000..28ef342
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/protocol/http/ThirdPage.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.http;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.form.Form;
+
+/** */
+public class ThirdPage extends WebPage
+{
+	private static final long serialVersionUID = 1L;
+
+	/** */
+	public ThirdPage()
+	{
+		add(new AjaxLink<Void>("link")
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void onClick(AjaxRequestTarget target)
+			{
+				setResponsePage(FirstPage.class);
+			}
+		});
+		add(new Form<Void>("form"));
+	}
+}