org.apache.wicket.core.request.mapper.MountedMapper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.core.request.mapper.MountedMapper.java

Source

/*
 * 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.core.request.mapper;

import java.util.Objects;
import java.util.function.Supplier;

import org.apache.wicket.core.request.handler.ListenerRequestHandler;
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.mapper.info.ComponentInfo;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.info.PageInfo;
import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.reference.ClassReference;
import org.apache.wicket.util.string.Strings;

/**
 * Encoder for mounted URL. The mount path can contain parameter placeholders, i.e.
 * <code>/mount/${foo}/path</code>. In that case the appropriate segment from the URL will be
 * accessible as named parameter "foo" in the {@link PageParameters}. Similarly when the URL is
 * constructed, the second segment will contain the value of the "foo" named page parameter.
 * Optional parameters are denoted by using a # instead of $: <code>/mount/#{foo}/path/${bar}</code>
 * has an optional {@code foo} parameter, a fixed {@code /path/} part and a required {@code bar}
 * parameter. When in doubt, parameters are matched from left to right, where required parameters
 * are matched before optional parameters, and optional parameters eager (from left to right).
 * <p>
 * Decodes and encodes the following URLs:
 * 
 * <pre>
 *  Page Class - Render (BookmarkablePageRequestHandler for mounted pages)
 *  /mount/point
 *  (these will redirect to hybrid alternative if page is not stateless)
 * 
 *  IPage Instance - Render Hybrid (RenderPageRequestHandler for mounted pages)
 *  /mount/point?2
 * 
 *  IPage Instance - Bookmarkable Listener (BookmarkableListenerRequestHandler for mounted pages)
 *  /mount/point?2-click-foo-bar-baz
 *  /mount/point?2-5.click.1-foo-bar-baz (1 is behavior index, 5 is render count)
 *  (these will redirect to hybrid if page is not stateless)
 * </pre>
 * 
 * @author Matej Knopp
 */
public class MountedMapper extends AbstractBookmarkableMapper {
    /** bookmarkable page class. */
    private final Supplier<Class<? extends IRequestablePage>> pageClassProvider;

    /**
     * Construct.
     * 
     * @param mountPath
     * @param pageClass
     */
    public MountedMapper(String mountPath, Class<? extends IRequestablePage> pageClass) {
        this(mountPath, pageClass, new PageParametersEncoder());
    }

    /**
     * Construct.
     * 
     * @param mountPath
     * @param pageClassProvider
     */
    public MountedMapper(String mountPath, Supplier<Class<? extends IRequestablePage>> pageClassProvider) {
        this(mountPath, pageClassProvider, new PageParametersEncoder());
    }

    /**
     * Construct.
     * 
     * @param mountPath
     * @param pageClass
     * @param pageParametersEncoder
     */
    public MountedMapper(String mountPath, Class<? extends IRequestablePage> pageClass,
            IPageParametersEncoder pageParametersEncoder) {
        this(mountPath, new ClassReference(pageClass), pageParametersEncoder);
    }

    /**
     * Construct.
     * 
     * @param mountPath
     * @param pageClassProvider
     * @param pageParametersEncoder
     */
    public MountedMapper(String mountPath, Supplier<Class<? extends IRequestablePage>> pageClassProvider,
            IPageParametersEncoder pageParametersEncoder) {
        super(mountPath, pageParametersEncoder);

        Args.notNull(pageClassProvider, "pageClassProvider");

        this.pageClassProvider = pageClassProvider;
    }

    @Override
    protected UrlInfo parseRequest(Request request) {
        Url url = request.getUrl();

        // when redirect to buffer/render is active and redirectFromHomePage returns true
        // check mounted class against the home page class. if it matches let wicket redirect
        // to the mounted URL
        if (redirectFromHomePage() && checkHomePage(url)) {
            return new UrlInfo(null, getContext().getHomePageClass(), newPageParameters());
        }
        // check if the URL starts with the proper segments
        else if (urlStartsWithMountedSegments(url)) {
            // try to extract page and component information from URL
            PageComponentInfo info = getPageComponentInfo(url);
            Class<? extends IRequestablePage> pageClass = getPageClass();
            PageParameters pageParameters = extractPageParameters(request, url);

            return new UrlInfo(info, pageClass, pageParameters);
        } else {
            return null;
        }
    }

    @Override
    public Url mapHandler(IRequestHandler requestHandler) {
        Url url = super.mapHandler(requestHandler);

        if (url == null && requestHandler instanceof ListenerRequestHandler
                && getRecreateMountedPagesAfterExpiry()) {
            ListenerRequestHandler handler = (ListenerRequestHandler) requestHandler;
            IRequestablePage page = handler.getPage();
            if (checkPageInstance(page)) {
                Integer renderCount = null;
                if (handler.includeRenderCount()) {
                    renderCount = page.getRenderCount();
                }

                String componentPath = handler.getComponentPath();
                PageInfo pageInfo = getPageInfo(handler);
                ComponentInfo componentInfo = new ComponentInfo(renderCount, componentPath,
                        handler.getBehaviorIndex());
                PageComponentInfo pageComponentInfo = new PageComponentInfo(pageInfo, componentInfo);
                PageParameters parameters = newPageParameters();
                parameters.mergeWith(page.getPageParameters());
                UrlInfo urlInfo = new UrlInfo(pageComponentInfo, page.getClass(),
                        parameters.mergeWith(handler.getPageParameters()));
                url = buildUrl(urlInfo);
            }
        }

        return url;
    }

    /**
     * @see AbstractBookmarkableMapper#buildUrl(AbstractBookmarkableMapper.UrlInfo)
     */
    @Override
    protected Url buildUrl(UrlInfo info) {
        Url url = new Url();
        for (String s : mountSegments) {
            url.getSegments().add(s);
        }
        encodePageComponentInfo(url, info.getPageComponentInfo());

        PageParameters copy = newPageParameters();
        copy.mergeWith(info.getPageParameters());
        if (setPlaceholders(copy, url) == false) {
            // mandatory parameter is not provided => cannot build Url
            return null;
        }

        return encodePageParameters(url, copy, pageParametersEncoder);
    }

    /**
     * Check if the URL is for home page and the home page class match mounted class. If so,
     * redirect to mounted URL.
     * 
     * @param url
     * @return request handler or <code>null</code>
     */
    private boolean checkHomePage(Url url) {
        if (url.getSegments().isEmpty() && url.getQueryParameters().isEmpty()) {
            // this is home page
            if (getPageClass().equals(getContext().getHomePageClass())) {
                return true;
            }
        }
        return false;
    }

    /**
     * If this method returns <code>true</code> and application home page class is same as the class
     * mounted with this encoder, request to home page will result in a redirect to the mounted
     * path.
     * 
     * @return whether this encode should respond to home page request when home page class is same
     *         as mounted class.
     */
    protected boolean redirectFromHomePage() {
        return true;
    }

    /**
     * @see AbstractBookmarkableMapper#pageMustHaveBeenCreatedBookmarkable()
     */
    @Override
    protected boolean pageMustHaveBeenCreatedBookmarkable() {
        return false;
    }

    /**
     * @see AbstractBookmarkableMapper#checkPageClass(java.lang.Class)
     */
    @Override
    protected boolean checkPageClass(Class<? extends IRequestablePage> pageClass) {
        return Objects.equals(pageClass, this.getPageClass());
    }

    private Class<? extends IRequestablePage> getPageClass() {
        return pageClassProvider.get();
    }

    @Override
    public String toString() {
        return "MountedMapper [mountSegments=" + Strings.join("/", mountSegments) + "]";
    }
}