wicket.protocol.http.request.AbstractWebRequestCodingStrategy.java Source code

Java tutorial

Introduction

Here is the source code for wicket.protocol.http.request.AbstractWebRequestCodingStrategy.java

Source

/*  
 * ==============================================================================
 * 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 wicket.protocol.http.request;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import wicket.Application;
import wicket.Component;
import wicket.IRedirectListener;
import wicket.IRequestTarget;
import wicket.Page;
import wicket.PageMap;
import wicket.PageParameters;
import wicket.Request;
import wicket.RequestCycle;
import wicket.RequestListenerInterface;
import wicket.Session;
import wicket.WicketRuntimeException;
import wicket.request.IRequestCodingStrategy;
import wicket.request.RequestParameters;
import wicket.request.target.coding.IRequestTargetUrlCodingStrategy;
import wicket.request.target.component.BookmarkableListenerInterfaceRequestTarget;
import wicket.request.target.component.IBookmarkablePageRequestTarget;
import wicket.request.target.component.IPageRequestTarget;
import wicket.request.target.component.listener.IListenerInterfaceRequestTarget;
import wicket.request.target.resource.ISharedResourceRequestTarget;
import wicket.util.string.AppendingStringBuffer;
import wicket.util.string.Strings;

/**
 * Request parameters factory implementation helper class
 * 
 * @see WebRequestCodingStrategy
 * @see PortletRequestCodingStrategy
 * 
 * @author Eelco Hillenius
 * @author Jonathan Locke
 * @author Janne Hietamäki
 */
public abstract class AbstractWebRequestCodingStrategy implements IRequestCodingStrategy {

    /** Name of interface target query parameter */
    public static final String NAME_SPACE = "wicket:";

    /** Name of interface target query parameter */
    public static final String INTERFACE_PARAMETER_NAME = NAME_SPACE + "interface";

    /** AJAX query parameter name */
    public static final String BEHAVIOR_ID_PARAMETER_NAME = NAME_SPACE + "behaviorId";

    /** Parameter name used all over the place */
    public static final String BOOKMARKABLE_PAGE_PARAMETER_NAME = NAME_SPACE + "bookmarkablePage";

    /** Pagemap parameter constant */
    public static final String PAGEMAP = NAME_SPACE + "pageMapName";

    /** log. */
    private static final Log log = LogFactory.getLog(AbstractWebRequestCodingStrategy.class);

    /**
     * @see wicket.request.IRequestCodingStrategy#decode(wicket.Request)
     */
    public final RequestParameters decode(final Request request) {
        final RequestParameters parameters = new RequestParameters();
        String pathInfo = getRequestPath(request);
        if (pathInfo != null && !pathInfo.startsWith("/")) {
            pathInfo = "/" + pathInfo;
        }
        parameters.setPath(pathInfo);
        parameters.setPageMapName(request.getParameter(PAGEMAP));
        addInterfaceParameters(request, parameters);
        addBookmarkablePageParameters(request, parameters);
        addResourceParameters(request, parameters);

        parameters.setBehaviorId(request.getParameter(BEHAVIOR_ID_PARAMETER_NAME));

        Map<String, ? extends Object> map = request.getParameterMap();
        Iterator<String> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (key.startsWith(NAME_SPACE)) {
                iterator.remove();
            }
        }
        parameters.setParameters(map);
        return parameters;
    }

    /**
     * @see wicket.request.IRequestCodingStrategy#targetForRequest(wicket.request.RequestParameters)
     */
    public final IRequestTarget targetForRequest(RequestParameters requestParameters) {
        IRequestTargetUrlCodingStrategy encoder = urlCodingStrategyForPath(requestParameters.getPath());
        return (encoder != null) ? encoder.decode(requestParameters) : null;
    }

    /**
     * Adds bookmarkable page related parameters (page alias and optionally page
     * parameters). Any bookmarkable page alias mount will override this method;
     * hence if a mount is found, this method will not be called.
     * 
     * If you override this method to behave different then also
     * {@link #encode(RequestCycle, IBookmarkablePageRequestTarget)} should be
     * overridden to by in sync with that behaviour.
     * 
     * @param request
     *            the incoming request
     * @param parameters
     *            the parameters object to set the found values on
     */
    protected void addBookmarkablePageParameters(final Request request, final RequestParameters parameters) {
        final String requestString = request
                .getParameter(WebRequestCodingStrategy.BOOKMARKABLE_PAGE_PARAMETER_NAME);
        if (requestString != null) {
            final String[] components = Strings.split(requestString, Component.PATH_SEPARATOR);
            if (components.length != 2) {
                throw new WicketRuntimeException("Invalid bookmarkablePage parameter: " + requestString
                        + ", expected: 'pageMapName:pageClassName'");
            }

            // Extract any pagemap name
            final String pageMapName = components[0];
            parameters.setPageMapName(pageMapName.length() == 0 ? PageMap.DEFAULT_NAME : pageMapName);

            // Extract bookmarkable page class name
            final String pageClassName = components[1];
            parameters.setBookmarkablePageClass(pageClassName);
        }
    }

    /**
     * Adds page related parameters (path and pagemap and optionally version and
     * interface).
     * 
     * If you override this method to behave different then also
     * {@link #encode(RequestCycle, IListenerInterfaceRequestTarget)} should be
     * overridden to by in sync with that behaviour.
     * 
     * @param request
     *            the incoming request
     * @param parameters
     *            the parameters object to set the found values on
     */
    protected void addInterfaceParameters(final Request request, final RequestParameters parameters) {
        // Format of interface target parameter is
        // <page-map-name>:<path>:<version>:<interface>
        final String requestString = request.getParameter(INTERFACE_PARAMETER_NAME);
        if (requestString != null) {
            // Split into array of strings
            String[] pathComponents = Strings.split(requestString, Component.PATH_SEPARATOR);

            // There must be at least 4 components
            if (pathComponents.length < 4) {
                throw new WicketRuntimeException(
                        "Internal error parsing " + INTERFACE_PARAMETER_NAME + " = " + requestString);
            }

            // Set pagemap name
            final String pageMapName = pathComponents[0];
            parameters.setPageMapName(pageMapName.length() == 0 ? PageMap.DEFAULT_NAME : pageMapName);

            // Extract interface name after last colon
            final String interfaceName = pathComponents[pathComponents.length - 1];
            parameters.setInterfaceName(
                    interfaceName.length() != 0 ? interfaceName : IRedirectListener.INTERFACE.getName());

            // Extract version
            final String versionNumberString = pathComponents[pathComponents.length - 2];
            final int versionNumber = Strings.isEmpty(versionNumberString) ? 0
                    : Integer.parseInt(versionNumberString);
            parameters.setVersionNumber(versionNumber);

            // Component path is everything after pageMapName and before version
            final int start = pageMapName.length() + 1;
            final int end = requestString.length() - interfaceName.length() - versionNumberString.length() - 2;
            final String componentPath = requestString.substring(start, end);
            parameters.setComponentPath(componentPath);
        }
    }

    /**
     * Adds (shared) resource related parameters (resource key). Any shared
     * resource key mount will override this method; hence if a mount is found,
     * this method will not be called.
     * 
     * If you override this method to behave different then also
     * {@link #encode(RequestCycle, ISharedResourceRequestTarget)} should be
     * overridden to by in sync with that behaviour.
     * 
     * @param request
     *            the incomming request
     * @param parameters
     *            the parameters object to set the found values on
     */
    protected void addResourceParameters(Request request, RequestParameters parameters) {
        String pathInfo = request.getPath();
        if (pathInfo != null) {
            if (pathInfo.startsWith("/")) {
                pathInfo = pathInfo.substring(1);
            }
            if (pathInfo.startsWith("resources/")) {
                int ix = "resources/".length();
                if (pathInfo.length() > ix) {
                    StringBuilder path = new StringBuilder(pathInfo.substring(ix));
                    int ixSemiColon = path.indexOf(";");
                    // strip off any jsession id
                    if (ixSemiColon != -1) {
                        int ixEnd = path.indexOf("?");
                        if (ixEnd == -1) {
                            ixEnd = path.length();
                        }
                        path.delete(ixSemiColon, ixEnd);
                    }
                    parameters.setResourceKey(path.toString());
                }
            }
        }
    }

    /**
     * In case you are using custom targets that are not part of the default
     * target hierarchy, you need to override this method, which will be called
     * after the defaults have been tried. When this doesn't provide a url
     * either (returns null), an exception will be thrown by the encode method
     * saying that encoding could not be done.
     * 
     * @param requestCycle
     *            the current request cycle (for efficient access)
     * 
     * @param requestTarget
     *            the request target
     * @return the url to the provided target
     */
    protected String doEncode(RequestCycle requestCycle, IRequestTarget requestTarget) {
        return null;
    }

    /**
     * Encode a page class target.
     * 
     * If you override this method to behave different then also
     * {@link #addBookmarkablePageParameters(Request, RequestParameters)} should
     * be overridden to by in sync with that behaviour.
     * 
     * @param requestCycle
     *            the current request cycle
     * @param requestTarget
     *            the target to encode
     * @return the encoded url
     */
    protected CharSequence encode(RequestCycle requestCycle, IBookmarkablePageRequestTarget requestTarget) {
        // Begin encoding URL
        final AppendingStringBuffer url = new AppendingStringBuffer(64);
        url.append(urlPrefix(requestCycle));

        // Get page Class
        final Class pageClass = requestTarget.getPageClass();
        final Application application = Application.get();

        // Find pagemap name
        String pageMapName = requestTarget.getPageMapName();
        if (pageMapName == null) {
            IRequestTarget currentTarget = requestCycle.getRequestTarget();
            if (currentTarget instanceof IPageRequestTarget) {
                Page currentPage = ((IPageRequestTarget) currentTarget).getPage();
                final PageMap pageMap = currentPage.getPageMap();
                if (pageMap.isDefault()) {
                    pageMapName = "";
                } else {
                    pageMapName = pageMap.getName();
                }
            } else {
                pageMapName = "";
            }
        }

        boolean firstParameter = true;
        if (!application.getHomePage().equals(pageClass) || !"".equals(pageMapName)
                || requestTarget instanceof BookmarkableListenerInterfaceRequestTarget) {
            firstParameter = false;
            url.append('?');
            url.append(WebRequestCodingStrategy.BOOKMARKABLE_PAGE_PARAMETER_NAME);
            url.append('=');

            // Add <page-map-name>:<bookmarkable-page-class>
            url.append(pageMapName + Component.PATH_SEPARATOR + pageClass.getName());
        }

        // Is it a bookmarkable interface listener?
        if (requestTarget instanceof BookmarkableListenerInterfaceRequestTarget) {
            BookmarkableListenerInterfaceRequestTarget listenerTarget = (BookmarkableListenerInterfaceRequestTarget) requestTarget;
            if (firstParameter == true) {
                url.append("?");
            } else {
                url.append("&");
            }
            firstParameter = false;
            url.append(INTERFACE_PARAMETER_NAME);
            url.append("=");
            url.append(Component.PATH_SEPARATOR);
            url.append(listenerTarget.getComponentPath());
            url.append(Component.PATH_SEPARATOR);
            url.append(Component.PATH_SEPARATOR);
            url.append(listenerTarget.getInterfaceName());
        }

        // Get page parameters
        final PageParameters parameters = requestTarget.getPageParameters();
        if (parameters != null) {
            for (Object element : parameters.keySet()) {
                final String key = (String) element;
                final String value = parameters.getString(key);
                if (value != null) {
                    String escapedValue = value;
                    try {
                        escapedValue = URLEncoder.encode(escapedValue,
                                application.getRequestCycleSettings().getResponseRequestEncoding());
                    } catch (UnsupportedEncodingException ex) {
                        log.error(ex.getMessage(), ex);
                    }
                    if (!firstParameter) {
                        url.append('&');
                    } else {
                        firstParameter = false;
                        url.append('?');
                    }
                    url.append(key);
                    url.append('=');
                    url.append(escapedValue);
                }
            }
        }
        return requestCycle.getOriginalResponse().encodeURL(url);
    }

    /**
     * Encode a shared resource target.
     * 
     * If you override this method to behave different then also
     * {@link #addResourceParameters(Request, RequestParameters)} should be
     * overridden to by in sync with that behaviour.
     * 
     * @param requestCycle
     *            the current request cycle
     * @param requestTarget
     *            the target to encode
     * @return the encoded url
     */
    protected CharSequence encode(RequestCycle requestCycle, ISharedResourceRequestTarget requestTarget) {
        final CharSequence prefix = urlPrefix(requestCycle);
        final String sharedResourceKey = requestTarget.getResourceKey();
        if ((sharedResourceKey == null) || (sharedResourceKey.trim().length() == 0)) {
            return prefix;
        } else {
            final AppendingStringBuffer buffer = new AppendingStringBuffer(
                    sharedResourceKey.length() + prefix.length() + 11);
            buffer.append(prefix);
            if ((buffer.length() > 0) && buffer.charAt(buffer.length() - 1) == '/') {
                buffer.append("resources/");
            } else {
                buffer.append("/resources/");
            }
            buffer.append(sharedResourceKey);
            Map<String, ? extends Object> map = requestTarget.getRequestParameters().getParameters();
            if (map != null && map.size() > 0) {
                buffer.append('?');
                for (String key : map.keySet()) {
                    buffer.append(key);
                    buffer.append('=');
                    buffer.append(map.get(key));
                    buffer.append('&');
                }
                buffer.setLength(buffer.length() - 1);
            }
            return requestCycle.getOriginalResponse().encodeURL(buffer);
        }
    }

    /**
     * Encode a listener interface target.
     * 
     * If you override this method to behave different then also
     * {@link #addInterfaceParameters(Request, RequestParameters)} should be
     * overridden to by in sync with that behaviour.
     * 
     * @param requestCycle
     *            the current request cycle
     * @param requestTarget
     *            the target to encode
     * @return the encoded url
     */
    protected CharSequence encode(RequestCycle requestCycle, IListenerInterfaceRequestTarget requestTarget) {
        final RequestListenerInterface rli = requestTarget.getRequestListenerInterface();

        // Start string buffer for url
        final AppendingStringBuffer url = new AppendingStringBuffer(64);
        url.append(urlPrefix(requestCycle));
        url.append('?');
        url.append(INTERFACE_PARAMETER_NAME);
        url.append('=');

        // Get component and page for request target
        final Component component = requestTarget.getTarget();
        final Page page = component.getPage();

        // Add pagemap
        final PageMap pageMap = page.getPageMap();
        if (!pageMap.isDefault()) {
            url.append(pageMap.getName());
        }
        url.append(Component.PATH_SEPARATOR);

        // Add path to component
        url.append(component.getPath());
        url.append(Component.PATH_SEPARATOR);

        // Add version
        final int versionNumber = component.getPage().getCurrentVersionNumber();
        if (!rli.getRecordsPageVersion()) {
            url.append(Page.LATEST_VERSION);
        } else if (versionNumber > 0) {
            url.append(versionNumber);
        }
        url.append(Component.PATH_SEPARATOR);

        // Add listener interface
        final String listenerName = rli.getName();
        if (!IRedirectListener.INTERFACE.getName().equals(listenerName)) {
            url.append(listenerName);
        }

        return requestCycle.getOriginalResponse().encodeURL(url);
    }

    /**
     * Encode a page target.
     * 
     * @param requestCycle
     *            the current request cycle
     * @param requestTarget
     *            the target to encode
     * @return the encoded url
     */
    protected CharSequence encode(RequestCycle requestCycle, IPageRequestTarget requestTarget) {
        // Get the page we want a url from:
        Page page = requestTarget.getPage();

        // A url to a page is the IRedirectListener interface:
        CharSequence urlRedirect = page.urlFor(IRedirectListener.INTERFACE);

        // Touch the page once because it could be that it did go from stateless
        // to statefull or it was a internally made page where just a url must
        // be made for (frames)
        Session.get().touch(page);
        return urlRedirect;
    }

    /**
     * Gets the request info path. This is an overridable method in order to
     * provide users with a means to implement e.g. a path encryption scheme.
     * This method by default returns {@link Request#getPath()}.
     * 
     * @param request
     *            the request
     * @return the path info object, possibly processed
     */
    protected String getRequestPath(Request request) {
        return request.getPath();
    }

    /**
     * Gets prefix.
     * 
     * @param requestCycle
     *            the request cycle
     * 
     * @return prefix
     */
    protected abstract CharSequence urlPrefix(final RequestCycle requestCycle);

}