org.apache.wicket.request.handler.render.WebPageRenderer Maven / Gradle / Ivy
Go to download
A module that creates a .jar from the classes in wicket, wicket-util and wicket-request modules in order
to create a valid OSGi bundle of the wicket framework.
/*
* 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.request.handler.render;
import java.util.List;
import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler.RedirectPolicy;
import org.apache.wicket.feedback.FeedbackCollector;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.protocol.http.BufferedWebResponse;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.lang.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link PageRenderer} for web applications.
*
* @author Matej Knopp
*/
public class WebPageRenderer extends PageRenderer
{
private static final Logger logger = LoggerFactory.getLogger(WebPageRenderer.class);
/**
* Construct.
*
* @param renderPageRequestHandler
*/
public WebPageRenderer(RenderPageRequestHandler renderPageRequestHandler)
{
super(renderPageRequestHandler);
}
protected boolean isAjax(RequestCycle requestCycle)
{
boolean isAjax = false;
Request request = requestCycle.getRequest();
if (request instanceof WebRequest)
{
WebRequest webRequest = (WebRequest)request;
isAjax = webRequest.isAjax();
}
return isAjax;
}
/**
* Store the buffered response at application level. If current session is
* temporary, a permanent one is created.
*
* @param url
* @param response
*/
protected void storeBufferedResponse(Url url, BufferedWebResponse response)
{
if (isSessionTemporary())
{
Session.get().bind();
}
WebApplication.get().storeBufferedResponse(getSessionId(), url, response);
}
/**
* Renders page to a {@link BufferedWebResponse}. All URLs in page will be rendered relative to
* targetUrl
*
* @param targetUrl
* @param requestCycle
* @return BufferedWebResponse containing page body
*/
protected BufferedWebResponse renderPage(Url targetUrl, RequestCycle requestCycle)
{
// get the page before checking for a scheduled request handler because
// the page may call setResponsePage in its constructor
IRequestablePage requestablePage = getPage();
IRequestHandler scheduled = requestCycle.getRequestHandlerScheduledAfterCurrent();
if (scheduled != null)
{
// no need to render
return null;
}
// keep the original response
final WebResponse originalResponse = (WebResponse)requestCycle.getResponse();
// buffered web response for page
BufferedWebResponse response = new BufferedWebResponse(originalResponse);
// keep the original base URL
Url originalBaseUrl = requestCycle.getUrlRenderer().setBaseUrl(targetUrl);
try
{
requestCycle.setResponse(response);
requestablePage.renderPage();
if (requestCycle.getRequestHandlerScheduledAfterCurrent() != null)
{
// This is a special case.
// During page render another request handler got scheduled and will want to
// overwrite the response, so we need to let it.
// Just preserve the meta data headers. Clear the initial actions because they are
// already copied into the new response's actions
originalResponse.reset();
response.writeMetaData(originalResponse);
return null;
}
else
{
return response;
}
}
finally
{
// restore original response and base URL
requestCycle.setResponse(originalResponse);
requestCycle.getUrlRenderer().setBaseUrl(originalBaseUrl);
}
}
/**
*
* @param url
* @param requestCycle
*/
protected void redirectTo(Url url, RequestCycle requestCycle)
{
bindSessionIfNeeded();
WebResponse response = (WebResponse)requestCycle.getResponse();
String relativeUrl = requestCycle.getUrlRenderer().renderUrl(url);
response.sendRedirect(relativeUrl);
}
/**
* Bind the session if there are feedback messages pending.
* https://issues.apache.org/jira/browse/WICKET-5165
*/
private void bindSessionIfNeeded()
{
// check for session feedback messages only
FeedbackCollector collector = new FeedbackCollector();
List feedbackMessages = collector.collect();
if (feedbackMessages.size() > 0)
{
Session.get().bind();
}
}
/*
* TODO: simplify the code below. See WICKET-3347
*/
@Override
public void respond(RequestCycle requestCycle)
{
Url currentUrl = requestCycle.getUrlRenderer().getBaseUrl();
Url targetUrl = requestCycle.mapUrlFor(getRenderPageRequestHandler());
//
// the code below is little hairy but we have to handle 3 redirect policies,
// 3 rendering strategies and two kind of requests (ajax and normal)
//
if (shouldRenderPageAndWriteResponse(requestCycle, currentUrl, targetUrl))
{
BufferedWebResponse response = renderPage(currentUrl, requestCycle);
if (response != null)
{
response.writeTo((WebResponse)requestCycle.getResponse());
}
}
else if (shouldRedirectToTargetUrl(requestCycle, currentUrl, targetUrl))
{
redirectTo(targetUrl, requestCycle);
// note: if we had session here we would render the page to buffer and then
// redirect to URL generated *after* page has been rendered (the statelessness
// may change during render). this would save one redirect because now we have
// to render to URL generated *before* page is rendered, render the page, get
// URL after render and if the URL is different (meaning page is not stateless),
// save the buffer and redirect again (which is pretty much what the next step
// does)
}
else
{
if (isRedirectToBuffer() == false && logger.isDebugEnabled())
{
String details = String
.format(
"redirect strategy: '%s', isAjax: '%s', redirect policy: '%s', "
+ "current url: '%s', target url: '%s', is new: '%s', is stateless: '%s', is temporary: '%s'",
Application.get().getRequestCycleSettings().getRenderStrategy(),
isAjax(requestCycle), getRedirectPolicy(), currentUrl, targetUrl,
isNewPageInstance(), isPageStateless(), isSessionTemporary());
logger
.debug("Falling back to Redirect_To_Buffer render strategy because none of the conditions "
+ "matched. Details: " + details);
}
// force creation of possible stateful page to get the final target url
getPage();
Url beforeRenderUrl = requestCycle.mapUrlFor(getRenderPageRequestHandler());
// redirect to buffer
BufferedWebResponse response = renderPage(beforeRenderUrl, requestCycle);
if (response == null)
{
return;
}
// the url might have changed after page has been rendered (e.g. the
// stateless flag might have changed because stateful components
// were added)
final Url afterRenderUrl = requestCycle
.mapUrlFor(getRenderPageRequestHandler());
if (beforeRenderUrl.getSegments().equals(afterRenderUrl.getSegments()) == false)
{
// the amount of segments is different - generated relative URLs
// will not work, we need to rerender the page. This can happen
// with IRequestHandlers that produce different URLs with
// different amount of segments for stateless and stateful pages
response = renderPage(afterRenderUrl, requestCycle);
}
if (currentUrl.equals(afterRenderUrl))
{
// no need to redirect when both urls are exactly the same
response.writeTo((WebResponse)requestCycle.getResponse());
}
// if page is still stateless after render
else if (isPageStateless() && !enableRedirectForStatelessPage())
{
// we don't want the redirect to happen for stateless page
// example:
// when a normal mounted stateful page is hit at /mount/point
// wicket renders the page to buffer and redirects to /mount/point?12
// but for stateless page the redirect is not necessary
// also for listener interface on stateful page we want to redirect
// after the listener is invoked, but on stateless page the user
// must ask for redirect explicitly
response.writeTo((WebResponse)requestCycle.getResponse());
}
else
{
storeBufferedResponse(afterRenderUrl, response);
redirectTo(afterRenderUrl, requestCycle);
}
}
}
protected boolean isPageStateless()
{
return getPage().isPageStateless();
}
protected boolean isNewPageInstance()
{
return getPageProvider().isNewPageInstance();
}
protected boolean shouldPreserveClientUrl(RequestCycle requestCycle)
{
return ((WebRequest)requestCycle.getRequest()).shouldPreserveClientUrl();
}
/**
* Should the client be redirected to target url.
*/
protected boolean shouldRedirectToTargetUrl(RequestCycle cycle, Url currentUrl, Url targetUrl)
{
return alwaysRedirect(getRedirectPolicy()) //
|| isRedirectToRender() //
|| (isAjax(cycle) && targetUrl.equals(currentUrl)) //
|| (!targetUrl.equals(currentUrl) && (isNewPageInstance() || (isSessionTemporary() && isPageStateless())));
// if target URL is different and session is temporary and page is stateless
// this is special case when page is stateless but there is no session so we can't
// render it to buffer
// alternatively if URLs are different and we have a page class and not an instance we
// can redirect to the url which will instantiate the instance of us
}
/**
* Should the page be rendered immediately.
*/
protected boolean shouldRenderPageAndWriteResponse(RequestCycle cycle, Url currentUrl,
Url targetUrl)
{
// WICKET-5484 never render and write for Ajax requests
if (isAjax(cycle))
{
return false;
}
return (compatibleProtocols(currentUrl.getProtocol(), targetUrl.getProtocol())) &&
(neverRedirect(getRedirectPolicy())
|| ((isOnePassRender() && notForcedRedirect(getRedirectPolicy())) || (targetUrl
.equals(currentUrl) && (!isNewPageInstance() && !isPageStateless()))) || (targetUrl.equals(currentUrl) && isRedirectToRender())
|| (shouldPreserveClientUrl(cycle) && notForcedRedirect(getRedirectPolicy())));
}
private static boolean neverRedirect(RedirectPolicy redirectPolicy)
{
return redirectPolicy == RedirectPolicy.NEVER_REDIRECT;
}
private static boolean alwaysRedirect(RedirectPolicy redirectPolicy)
{
return redirectPolicy == RedirectPolicy.ALWAYS_REDIRECT;
}
private static boolean notForcedRedirect(RedirectPolicy redirectPolicy)
{
return !alwaysRedirect(redirectPolicy);
}
/**
* Compares the protocols of two {@link Url}s
*
* @param p1
* the first protocol
* @param p2
* the second protocol
* @return {@code false} if the protocols are both non-null and not equal,
* {@code true} - otherwise
*/
protected boolean compatibleProtocols(String p1, String p2)
{
if (p1 != null && p2 != null)
{
return Objects.equal(p1, p2);
}
return true;
}
}