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

Java tutorial

Introduction

Here is the source code for org.apache.wicket.core.request.handler.ListenerRequestHandler.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.Component;
import org.apache.wicket.IRequestListener;
import org.apache.wicket.Page;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler.RedirectPolicy;
import org.apache.wicket.core.request.handler.logger.ListenerLogData;
import org.apache.wicket.request.ILoggableRequestHandler;
import org.apache.wicket.request.IRequestCycle;
import org.apache.wicket.request.component.IRequestableComponent;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.lang.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Request handler that invokes an {@link IRequestListener} on component or behavior and renders page afterwards.
 *
 * @author Matej Knopp
 */
public class ListenerRequestHandler
        implements IPageRequestHandler, IComponentRequestHandler, ILoggableRequestHandler {

    private static final Logger LOG = LoggerFactory.getLogger(ListenerRequestHandler.class);

    private final IPageAndComponentProvider pageComponentProvider;

    private final Integer behaviorId;

    private ListenerLogData logData;

    /**
     * Construct.
     *
     * @param pageComponentProvider
     * @param behaviorIndex
     */
    public ListenerRequestHandler(IPageAndComponentProvider pageComponentProvider, Integer behaviorIndex) {
        Args.notNull(pageComponentProvider, "pageComponentProvider");

        this.pageComponentProvider = pageComponentProvider;
        behaviorId = behaviorIndex;
    }

    /**
     * Construct.
     *
     * @param pageComponentProvider
     */
    public ListenerRequestHandler(PageAndComponentProvider pageComponentProvider) {
        this(pageComponentProvider, null);
    }

    public boolean includeRenderCount() {
        if (behaviorId == null) {
            return ((IRequestListener) getComponent()).rendersPage();
        } else {
            return ((IRequestListener) getComponent().getBehaviorById(getBehaviorIndex())).rendersPage();
        }
    }

    @Override
    public IRequestableComponent getComponent() {
        return pageComponentProvider.getComponent();
    }

    @Override
    public IRequestablePage getPage() {
        return pageComponentProvider.getPageInstance();
    }

    @Override
    public Class<? extends IRequestablePage> getPageClass() {
        return pageComponentProvider.getPageClass();
    }

    @Override
    public Integer getPageId() {
        return pageComponentProvider.getPageId();
    }

    @Override
    public PageParameters getPageParameters() {
        return pageComponentProvider.getPageParameters();
    }

    @Override
    public void detach(IRequestCycle requestCycle) {
        if (logData == null) {
            logData = new ListenerLogData(pageComponentProvider, behaviorId);
        }
        pageComponentProvider.detach();
    }

    /**
     * Index of target behavior or <code>null</code> if component is the target.
     *
     * @return behavior index or <code>null</code>
     */
    public Integer getBehaviorIndex() {
        return behaviorId;
    }

    @Override
    public void respond(final IRequestCycle requestCycle) {
        final IRequestablePage page = getPage();
        final boolean freshPage = pageComponentProvider.doesProvideNewPage();
        final boolean isAjax = ((WebRequest) requestCycle.getRequest()).isAjax();

        IRequestableComponent component;
        try {
            component = getComponent();
        } catch (ComponentNotFoundException e) {
            // either the page is stateless and the component we are looking for is not added in the
            // constructor
            // or the page is stateful+stale and a new instances was created by pageprovider
            // we denote this by setting component to null
            component = null;
        }

        if ((component == null && !freshPage) || (component != null && component.getPage() != page)) {
            throw new ComponentNotFoundException(
                    "Component '" + getComponentPath() + "' has been removed from page.");
        }

        if (page instanceof Page) {
            // initialize the page to be able to check whether it is stateless
            ((Page) page).internalInitialize();
        }

        RedirectPolicy policy = page.isPageStateless() ? RedirectPolicy.NEVER_REDIRECT
                : RedirectPolicy.AUTO_REDIRECT;

        boolean blockIfExpired = component != null && !component.canCallListenerAfterExpiry();

        boolean lateComponent = component == null && freshPage;

        if ((pageComponentProvider.wasExpired() && blockIfExpired) || lateComponent) {
            // A request listener is invoked on an expired page or the component couldn't be
            // determined. The best we can do is to re-paint the newly constructed page.
            // Reference: WICKET-4454, WICKET-6288

            if (LOG.isDebugEnabled()) {
                LOG.debug(
                        "An IRequestListener was called but its page/component({}) couldn't be resolved. "
                                + "Scheduling re-create of the page and ignoring the listener interface...",
                        getComponentPath());
            }

            if (isAjax) {
                policy = RedirectPolicy.ALWAYS_REDIRECT;
            }

            requestCycle.scheduleRequestHandlerAfterCurrent(
                    new RenderPageRequestHandler(new PageProvider(page), policy));
            return;
        }

        invokeListener(requestCycle, policy, isAjax);
    }

    private void invokeListener(IRequestCycle requestCycle, RedirectPolicy policy, boolean ajax) {
        if (getBehaviorIndex() == null) {
            invoke(requestCycle, policy, ajax, getComponent());
        } else {
            try {
                Behavior behavior = getComponent().getBehaviorById(behaviorId);
                invoke(requestCycle, policy, ajax, getComponent(), behavior);
            } catch (IndexOutOfBoundsException e) {
                throw new WicketRuntimeException("Couldn't find component behavior.", e);
            }

        }
    }

    /**
     * Invokes a given interface on a component.
     * 
     * @param rcomponent
     *            The component
     * 
     * @throws ListenerInvocationNotAllowedException
     *             when listener invocation attempted on a component that does not allow it
     */
    private final void invoke(final IRequestCycle requestCycle, RedirectPolicy policy, boolean ajax,
            final IRequestableComponent rcomponent) {
        // we are in Wicket core land
        final Component component = (Component) rcomponent;

        if (!component.canCallListener()) {
            // just return so that we have a silent fail and just re-render the
            // page
            LOG.info("component not enabled or visible; ignoring call. Component: " + component);
            throw new ListenerInvocationNotAllowedException(component, null,
                    "Component rejected interface invocation");
        }

        internalInvoke(requestCycle, policy, ajax, component, component);
    }

    /**
     * Invokes a given interface on a component's behavior.
     * 
     * @param rcomponent
     *            The component
     * @param behavior
     * @throws ListenerInvocationNotAllowedException
     *             when listener invocation attempted on a component that does not allow it
     */
    private final void invoke(final IRequestCycle requestCycle, RedirectPolicy policy, boolean ajax,
            final IRequestableComponent rcomponent, final Behavior behavior) {
        // we are in Wicket core land
        final Component component = (Component) rcomponent;

        if (!behavior.canCallListener(component)) {
            LOG.warn("behavior not enabled; ignore call. Behavior {} at component {}", behavior, component);
            throw new ListenerInvocationNotAllowedException(component, behavior,
                    "Behavior rejected interface invocation. ");
        }

        internalInvoke(requestCycle, policy, ajax, component, behavior);
    }

    private void internalInvoke(final IRequestCycle requestCycle, RedirectPolicy policy, boolean ajax,
            final Component component, final Object target) {
        // save a reference to the page because the component can be removed
        // during the invocation of the listener and thus lose its parent
        Page page = component.getPage();

        // initialization is required for stateless pages
        if (!page.isInitialized()) {
            page.internalInitialize();
        }

        IRequestListener requestListener = (IRequestListener) target;

        if (requestListener.rendersPage() && !ajax) {
            // schedule page render after current request handler is done. this can be
            // overridden during invocation of listener
            // method (i.e. by calling RequestCycle#setResponsePage)
            requestCycle.scheduleRequestHandlerAfterCurrent(
                    new RenderPageRequestHandler(new PageProvider(page), policy));
        }

        requestListener.onRequest();
    }

    @Override
    public final boolean isPageInstanceCreated() {
        return pageComponentProvider.hasPageInstance();
    }

    @Override
    public final String getComponentPath() {
        return pageComponentProvider.getComponentPath();
    }

    @Override
    public final Integer getRenderCount() {
        return pageComponentProvider.getRenderCount();
    }

    @Override
    public ListenerLogData getLogData() {
        return logData;
    }
}