From b2abacfa63ecd9cb5765a9887e72f7687528f77d Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 23 Mar 2012 16:22:09 -0400 Subject: [PATCH] Implemented SessionlessForm to workaround Wicket's stateful nature --- src/com/gitblit/wicket/SessionlessForm.java | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/com/gitblit/wicket/SessionlessForm.java diff --git a/src/com/gitblit/wicket/SessionlessForm.java b/src/com/gitblit/wicket/SessionlessForm.java new file mode 100644 index 00000000..484e85e3 --- /dev/null +++ b/src/com/gitblit/wicket/SessionlessForm.java @@ -0,0 +1,148 @@ +/* + * Copyright 2012 gitblit.com. + * + * Licensed 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 com.gitblit.wicket; + +import java.text.MessageFormat; + +import org.apache.wicket.Component; +import org.apache.wicket.PageParameters; +import org.apache.wicket.markup.ComponentTag; +import org.apache.wicket.markup.MarkupStream; +import org.apache.wicket.markup.html.form.StatelessForm; +import org.apache.wicket.protocol.http.WicketURLDecoder; +import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy; +import org.apache.wicket.util.string.AppendingStringBuffer; +import org.apache.wicket.util.string.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.wicket.pages.BasePage; + +/** + * This class is used to create a stateless form that can POST or GET to a + * bookmarkable page regardless of the pagemap and even after session expiration + * or a server restart. + * + * The trick is to embed "wicket:bookmarkablePage" as a hidden field of the form. + * Wicket already has logic to extract this parameter when it is trying + * to determine which page should receive the request. + * + * The parameters of the containing page can optionally be included as hidden + * fields in this form. Note that if a page parameter's name collides with any + * child's wicket:id in this form then the page parameter is excluded. + * + * @author James Moger + * + */ +public class SessionlessForm extends StatelessForm { + + private static final long serialVersionUID = 1L; + + private static final String HIDDEN_DIV_START = "
"; + + private final Class pageClass; + + private final PageParameters pageParameters; + + private final Logger log = LoggerFactory.getLogger(SessionlessForm.class); + + /** + * Sessionless forms must have a bookmarkable page class. A bookmarkable + * page is defined as a page that has only a default and/or a PageParameter + * constructor. + * + * @param id + * @param bookmarkablePageClass + */ + public SessionlessForm(String id, Class bookmarkablePageClass) { + this(id, bookmarkablePageClass, null); + } + + /** + * Sessionless forms must have a bookmarkable page class. A bookmarkable + * page is defined as a page that has only a default and/or a PageParameter + * constructor. + * + * @param id + * @param bookmarkablePageClass + * @param pageParameters + */ + public SessionlessForm(String id, Class bookmarkablePageClass, + PageParameters pageParameters) { + super(id); + this.pageClass = bookmarkablePageClass; + this.pageParameters = pageParameters; + } + + + /** + * Append an additional hidden input tag that forces Wicket to correctly + * determine the destination page class even after a session expiration or + * a server restart. + * + * @param markupStream + * The markup stream + * @param openTag + * The open tag for the body + */ + @Override + protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) + { + // render the hidden bookmarkable page field + AppendingStringBuffer buffer = new AppendingStringBuffer(HIDDEN_DIV_START); + buffer.append(""); + + // insert the page parameters, if any, as hidden fields as long as they + // do not collide with any child wicket:id of the form. + if (pageParameters != null) { + for (String key : pageParameters.keySet()) { + Component c = get(key); + if (c != null) { + // this form has a field id which matches the + // parameter name, skip embedding a hidden value + log.warn(MessageFormat.format("Skipping page parameter \"{0}\" from sessionless form hidden fields because it collides with a form child wicket:id", key)); + continue; + } + String value = pageParameters.getString(key); + buffer.append(""); + } + } + + buffer.append("
"); + getResponse().write(buffer); + super.onComponentTagBody(markupStream, openTag); + } + + /** + * Take URL-encoded query string value, unencode it and return HTML-escaped version + * + * @param s + * value to reencode + * @return reencoded value + */ + private String recode(String s) { + String un = WicketURLDecoder.QUERY_INSTANCE.decode(s); + return Strings.escapeMarkup(un).toString(); + } +} -- 2.39.5