    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.internal.structure;
    014
    015import org.apache.tapestry5.ComponentResources;
    016import org.apache.tapestry5.beaneditor.NonVisual;
    017import org.apache.tapestry5.commons.util.UnknownValueException;
    018import org.apache.tapestry5.ioc.services.PerthreadManager;
    019import org.apache.tapestry5.runtime.Component;
    020import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
    021import org.apache.tapestry5.runtime.PageLifecycleListener;
    022import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
    023import org.slf4j.Logger;
    024
    025/**
    026 * Represents a unique page within the application. Pages are part of the <em>internal</em> structure of a Tapestry
    027 * application; end developers who refer to "page" are really referring to the {@link #getRootComponent() root
    028 * component} of the actual page.
    029 *
    030 * Starting in release 5.2, the nature of pages changed considerably. Pages are no longer pooled instances. Instead,
    031 * pages are shared instances (per locale) but all internal <em>mutable</em> state is stored inside
    032 * {@link PerthreadManager}. Page construction time is considered to extend past the
    033 * {@linkplain  PageLifecycleCallbackHub#addPageLoadedCallback(Runnable) page loaded callback}. This is not quite perfect from a
    034 * threading point-of-view (arguably, even write-once-read-many fields should be protected by synchronized blocks or
    035 * other mechanisms). At best, we can be assured that the entire page construction phase is protected by a single
    036 * synchronized block (but not on the page itself). An ideal system would build the page bottom to top so that all
    037 * assignments could take place in constructors, assigning to final fields. Maybe some day.
    038 *
    039 * The Page object is never visible to end-user code, though it exposes an interface ({@link PageLifecycleCallbackHub} that
    040 * {@linkplain org.apache.tapestry5.ComponentResources#getPageLifecycleCallbackHub() is}).
    041 */
    042public interface Page extends PageLifecycleCallbackHub
    043{
    044    /**
    045     * Page construction statistics for the page.
    046     *
    047     * @since 5.3
    048     */
    049    public final class Stats
    050    {
    051        /**
    052         * Time, in milliseconds, to construct the page. This includes time to construct components inside the page,
    053         * as well as hooking everything together, and includes the execution of {@link org.apache.tapestry5.internal.structure.Page#loaded()}.
    054         * You'll often see that the first page is expensive to construct,
    055         * and later pages that use a similar mix of components are very cheap.
    056         */
    057        public final double assemblyTime;
    058
    059        /**
    060         * The total number of components in the page, including the root component. This does not include the number of mixins.
    061         */
    062        public final int componentCount;
    063
    064        /**
    065         * The "weight" of the page is an arbitrary number that factors the number of components, mixins, component template elements,
    066         * bindings, and other factors.
    067         */
    068        public final int weight;
    069
    070        public Stats(double assemblyTime, int componentCount, int weight)
    071        {
    072            this.assemblyTime = assemblyTime;
    073            this.componentCount = componentCount;
    074            this.weight = weight;
    075        }
    076    }
    077
    078    /**
    079     * Returns the short, logical name for the page. This is the page name as it might included in
    080     * an action or page
    081     * render URL (though it will be converted to lower case when it is included).
    082     */
    083    String getName();
    084
    085    /**
    086     * The selector (which includes Locale) used when the page was constructor.
    087     */
    088    ComponentResourceSelector getSelector();
    089
    090    /**
    091     * Invoked during page construction time to connect the page's root component to the page
    092     * instance.
    093     */
    094    void setRootElement(ComponentPageElement component);
    095
    096    /**
    097     * The root component of the page. This is the wrapper around the end developer's view of the
    098     * page.
    099     */
    100    ComponentPageElement getRootElement();
    101
    102    /**
    103     * The root component of the page. A convenience over invoking getRootElement().getComponent().
    104     */
    105    Component getRootComponent();
    106
    107    /**
    108     * Invoked to inform the page that it is being detached from the current request. This occurs
    109     * just before the page
    110     * is returned to the page pool.
    111     *
    112     * A page may be clean or dirty. A page is dirty if its dirty count is greater than zero (meaning that, during the
    113     * render of the page, some components did not fully render), or if any of its listeners throw an exception from
    114     * containingPageDidDetach().
    115     *
    116     * The page pool should discard pages that are dirty, rather than store them into the pool.
    117     *
    118     * Under Tapestry 5.2 and pool-less pages, the result is ignored; all mutable state is expected to be discarded
    119     * automatically from the {@link PerthreadManager}. A future release of Tapestry will likely convert this method to
    120     * type void.
    121     *
    122     * @return true if the page is "dirty", false otherwise
    123     * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidDetach()
    124     */
    125    boolean detached();
    126
    127    /**
    128     * Invoked to inform the page that it is attached to the current request. This occurs when a
    129     * page is first referenced within a request. If the page was created from scratch for this request, the call
    130     * to {@link #loaded()} will preceded the call to {@link #attached()}.
    131     *
    132     * First all listeners have {@link PageLifecycleListener#restoreStateBeforePageAttach()} invoked, followed by
    133     * {@link PageLifecycleListener#containingPageDidAttach()}.
    134     */
    135    void attached();
    136
    137    /**
    138     * Inform the page that it is now completely loaded.
    139     *
    140     * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
    141     */
    142    void loaded();
    143
    144    /**
    145     * Adds a listener that is notified of large scale page events.
    146     *
    147     * @deprecated in 5.3.4; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
    148     * {@link #addPageDetachedCallback(Runnable)}  instead
    149     */
    150    void addLifecycleListener(PageLifecycleListener listener);
    151
    152    /**
    153     * Removes a listener that was previously added.
    154     *
    155     * @since 5.2.0
    156     * @deprecated in 5.3.4, due to introduction of {@link #addPageLoadedCallback(Runnable)}
    157     */
    158    void removeLifecycleListener(PageLifecycleListener listener);
    159
    160    /**
    161     * Returns the logger of the root component element. Any logging about page construction or
    162     * activity should be sent
    163     * to this logger.
    164     */
    165    Logger getLogger();
    166
    167    /**
    168     * Retrieves a component element by its nested id (a sequence of simple ids, separated by dots).
    169     * The individual
    170     * names in the nested id are matched without regards to case. A nested id of '' (the empty
    171     * string) returns the root
    172     * element of the page.
    173     *
    174     * @throws UnknownValueException
    175     *         if the nestedId does not correspond to a component
    176     */
    177    ComponentPageElement getComponentElementByNestedId(String nestedId);
    178
    179    /**
    180     * Posts a change to a persistent field.
    181     *
    182     * @param resources
    183     *         the component resources for the component or mixin containing the field whose
    184     *         value changed
    185     * @param fieldName
    186     *         the name of the field
    187     * @param newValue
    188     *         the new value for the field
    189     */
    190    void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
    191
    192    /**
    193     * Gets a change for a field within the component.
    194     *
    195     * @param nestedId
    196     *         the nested component id of the component containing the field
    197     * @param fieldName
    198     *         the name of the persistent field
    199     * @return the value, or null if no value is stored
    200     */
    201    Object getFieldChange(String nestedId, String fieldName);
    202
    203    /**
    204     * Discards all persistent field changes for the page containing the component. Changes are
    205     * eliminated from
    206     * persistent storage (such as the {@link org.apache.tapestry5.http.services.Session}) which will
    207     * take effect in the <em>next</em> request (the attached page instance is not affected).
    208     */
    209    void discardPersistentFieldChanges();
    210
    211    /**
    212     * Adds a new listener for page reset events.
    213     *
    214     * @param listener
    215     *         will receive notifications when the page is accessed from a different page
    216     * @since 5.2.0
    217     * @deprecated in 5.3.4,
    218     */
    219    void addResetListener(PageResetListener listener);
    220
    221    /**
    222     * Returns true if there are any {@link PageResetListener} listeners.
    223     *
    224     * @since 5.2.0
    225     */
    226    boolean hasResetListeners();
    227
    228    /**
    229     * Invoked to notify {@link PageResetListener} listeners.
    230     */
    231    void pageReset();
    232
    233    /**
    234     * Invoked once at the end of page construction, to provide page construction statistics.
    235     *
    236     * @since 5.3
    237     */
    238    void setStats(Stats stats);
    239
    240    /**
    241     * Returns the page construction statistics for this page.
    242     */
    243    Stats getStats();
    244
    245    /**
    246     * Returns the number of times the page has been attached to a request. This is a rough measure
    247     * of how important the page is, relative to other pages. This value is volatile, changing constantly.
    248     *
    249     * @since 5.3
    250     */
    251    int getAttachCount();
    252
    253    /**
    254     * Returns true if extract parameter count matching is enabled.
    255     *
    256     * @see org.apache.tapestry5.MetaDataConstants#UNKNOWN_ACTIVATION_CONTEXT_CHECK
    257     * @since 5.4
    258     */
    259    @NonVisual
    260    boolean isExactParameterCountMatch();
    261
    262
    263}