org.apache.wicket.core.request.handler.PageProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.core.request.handler.PageProvider.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.handler;

import org.apache.wicket.Application;
import org.apache.wicket.core.request.mapper.IPageSource;
import org.apache.wicket.core.request.mapper.StalePageException;
import org.apache.wicket.page.IPageManager;
import org.apache.wicket.protocol.http.PageExpiredException;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.lang.Args;

/**
 * Provides page instance for request handlers. Each of the constructors has just enough information
 * to get existing or create new page instance. Requesting or creating page instance is deferred
 * until {@link #getPageInstance()} is called.
 * <p>
 * Purpose of this class is to reduce complexity of both {@link IRequestMapper}s and
 * {@link IRequestHandler}s. {@link IRequestMapper} examines the URL, gathers all relevant
 * information about the page in the URL (combination of page id, page class, page parameters and
 * render count), creates {@link PageProvider} object and creates a {@link IRequestHandler} instance
 * that can use the {@link PageProvider} to access the page.
 * <p>
 * Apart from simplifying {@link IRequestMapper}s and {@link IRequestHandler}s {@link PageProvider}
 * also helps performance because creating or obtaining page from {@link IPageManager} is delayed
 * until the {@link IRequestHandler} actually requires the page.
 * 
 * @author Matej Knopp
 */
public class PageProvider implements IPageProvider, IClusterable {
    private static final long serialVersionUID = 1L;

    private final Integer renderCount;

    private final Integer pageId;

    private transient IPageSource pageSource;

    private Class<? extends IRequestablePage> pageClass;

    private PageParameters pageParameters;

    private transient Provision provision;

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return page instance with specified id.
     * 
     * @param pageId
     * @param renderCount
     *            optional argument
     */
    public PageProvider(final Integer pageId, final Integer renderCount) {
        this.pageId = pageId;
        this.renderCount = renderCount;
    }

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return page instance with specified id if it exists and it's class matches pageClass. If
     * none of these is true new page instance will be created.
     * 
     * @param pageId
     * @param pageClass
     * @param renderCount
     *            optional argument
     */
    public PageProvider(final Integer pageId, final Class<? extends IRequestablePage> pageClass,
            Integer renderCount) {
        this(pageId, pageClass, new PageParameters(), renderCount);
    }

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return page instance with specified id if it exists and it's class matches pageClass. If
     * none of these is true new page instance will be created.
     * 
     * @param pageId
     * @param pageClass
     * @param pageParameters
     * @param renderCount
     *            optional argument
     */
    public PageProvider(final Integer pageId, final Class<? extends IRequestablePage> pageClass,
            final PageParameters pageParameters, final Integer renderCount) {
        this.pageId = pageId;
        setPageClass(pageClass);
        setPageParameters(pageParameters);
        this.renderCount = renderCount;
    }

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return new instance of page with specified class.
     * 
     * @param pageClass
     * @param pageParameters
     */
    public PageProvider(final Class<? extends IRequestablePage> pageClass, final PageParameters pageParameters) {
        setPageClass(pageClass);
        if (pageParameters != null) {
            setPageParameters(pageParameters);
        }
        pageId = null;
        renderCount = null;
    }

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return new instance of page with specified class.
     * 
     * @param pageClass
     */
    public PageProvider(Class<? extends IRequestablePage> pageClass) {
        this(pageClass, null);
    }

    /**
     * Creates a new page provider object. Upon calling of {@link #getPageInstance()} this provider
     * will return the given page instance.
     * 
     * @param page
     */
    public PageProvider(IRequestablePage page) {
        Args.notNull(page, "page");

        provision = new Provision().resolveTo(page);
        pageId = page.getPageId();
        renderCount = page.getRenderCount();
    }

    private Provision getProvision() {
        if (provision == null) {
            provision = new Provision().resolve();
        }
        return provision;
    }

    @Override
    public IRequestablePage getPageInstance() throws PageExpiredException {
        IRequestablePage page = getProvision().getPage();

        if (page == null && wasExpired()) {
            throw new PageExpiredException("Page with id '" + pageId + "' has expired.");
        }

        return page;
    }

    @Override
    public PageParameters getPageParameters() throws PageExpiredException {
        if (pageParameters != null) {
            return pageParameters;
        } else if (hasPageInstance()) {
            return getPageInstance().getPageParameters();
        } else {
            return null;
        }
    }

    /**
     * @return negates {@link PageProvider#hasPageInstance()}
     * @deprecated use {@link PageProvider#hasPageInstance()} negation instead
     */
    @Override
    public boolean isNewPageInstance() {
        return !hasPageInstance();
    }

    /**
     * If this provider returns existing page, regardless if it was already created by PageProvider
     * itself or is or can be found in the data store. The only guarantee is that by calling
     * {@link PageProvider#getPageInstance()} this provider will return an existing instance and no
     * page will be created.
     * 
     * @return if provides an existing page
     */
    @Override
    public final boolean hasPageInstance() {
        if (provision != null || pageId != null) {
            return getProvision().didResolveToPage();
        } else {
            return false;
        }
    }

    /**
     * Returns whether or not the page instance held by this provider has been instantiated by the
     * provider.
     * 
     * @return {@code true} iff the page instance held by this provider was instantiated by the
     *         provider
     */
    @Override
    public final boolean doesProvideNewPage() {
        return getProvision().doesProvideNewPage();
    }

    @Override
    public boolean wasExpired() {
        return pageId != null && getProvision().didFailToFindStoredPage();
    }

    @Override
    public Class<? extends IRequestablePage> getPageClass() throws PageExpiredException {
        if (pageClass != null) {
            return pageClass;
        } else {
            return getPageInstance().getClass();
        }
    }

    protected IPageSource getPageSource() {
        if (pageSource != null) {
            return pageSource;
        }
        if (Application.exists()) {
            return Application.get().getMapperContext();
        } else {
            throw new IllegalStateException(
                    "No application is bound to current thread. Call setPageSource() to manually assign pageSource to this provider.");
        }
    }

    /**
     * Detaches the page if it has been loaded (that means either
     * {@link #PageProvider(IRequestablePage)} constructor has been used or
     * {@link #getPageInstance()} has been called).
     */
    @Override
    public void detach() {
        if (provision != null) {
            provision.detach();
            provision = null;
        }
    }

    /**
     * If the {@link PageProvider} is used outside request thread (thread that does not have
     * application instance assigned) it is necessary to specify a {@link IPageSource} instance so
     * that {@link PageProvider} knows how to get a page instance.
     * 
     * @param pageSource
     */
    public void setPageSource(IPageSource pageSource) {
        if (provision != null) {
            throw new IllegalStateException("A page was already provided.");
        }
        this.pageSource = pageSource;
    }

    /**
     * 
     * @param pageClass
     */
    private void setPageClass(Class<? extends IRequestablePage> pageClass) {
        Args.notNull(pageClass, "pageClass");

        this.pageClass = pageClass;
    }

    /**
     * 
     * @param pageParameters
     */
    protected void setPageParameters(PageParameters pageParameters) {
        this.pageParameters = pageParameters;
    }

    /**
     * 
     * @return page id
     */
    @Override
    public Integer getPageId() {
        return pageId;
    }

    @Override
    public Integer getRenderCount() {
        return renderCount;
    }

    @Override
    public String toString() {
        return "PageProvider{" + "renderCount=" + renderCount + ", pageId=" + pageId + ", pageClass=" + pageClass
                + ", pageParameters=" + pageParameters + '}';
    }

    /**
     * A provision is the work necessary to provide a page. It includes to resolve parameters to a
     * page, to track the resolution metadata and to keep a reference of the resolved page.
     * 
     * The logic based on {@link PageProvider}'s parameters:
     * 
     * - having an stored page id, the stored page is provided
     * 
     * - having only a page class, a new instance of it is provided
     * 
     * - having non stored page id plus page class, a new instance of the page class is provided
     * 
     * - having non stored page id and no page class, no page is provided
     * 
     * - being a page instance, the instance itself will be the provided page
     *
     * @author pedro
     */
    private class Provision {
        transient IRequestablePage page;
        boolean failedToFindStoredPage;

        IRequestablePage getPage() {
            if (page == null && doesProvideNewPage()) {
                page = getPageSource().newPageInstance(pageClass, pageParameters);
            }
            return page;
        }

        boolean didResolveToPage() {
            return page != null;
        }

        boolean doesProvideNewPage() {
            return (pageId == null || failedToFindStoredPage) && pageClass != null;
        }

        boolean didFailToFindStoredPage() {
            return failedToFindStoredPage;
        }

        Provision resolveTo(IRequestablePage page) {
            this.page = page;

            return this;
        }

        Provision resolve() {

            if (pageId != null) {
                IRequestablePage stored = getPageSource().getPageInstance(pageId);
                if (stored != null && (pageClass == null || pageClass.equals(stored.getClass()))) {

                    page = stored;

                    if (renderCount != null && page.getRenderCount() != renderCount)
                        throw new StalePageException(page);
                }

                failedToFindStoredPage = page == null;
            }

            return this;
        }

        void detach() {
            if (page != null) {
                page.detach();
            }
        }

    }
}