    001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.structure;
    016
    017    import org.apache.tapestry5.ComponentResources;
    018    import org.apache.tapestry5.ioc.services.PerthreadManager;
    019    import org.apache.tapestry5.runtime.Component;
    020    import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
    021    import org.apache.tapestry5.runtime.PageLifecycleListener;
    022    import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
    023    import 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     * <p/>
    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     * <p/>
    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     */
    042    public 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. You'll often see that the first page is expensive to construct,
    054             * and later pages that use a similar mix of components are very cheap.
    055             */
    056            public final long assemblyTime;
    057
    058            /**
    059             * The total number of components in the page, including the root component. This does not include the number of mixins.
    060             */
    061            public final int componentCount;
    062
    063            /**
    064             * The "weight" of the page is an arbitrary number that factors the number of components, mixins, component template elements,
    065             * bindings, and other factors.
    066             */
    067            public final int weight;
    068
    069            public Stats(long assemblyTime, int componentCount, int weight)
    070            {
    071                this.assemblyTime = assemblyTime;
    072                this.componentCount = componentCount;
    073                this.weight = weight;
    074            }
    075        }
    076
    077        /**
    078         * Returns the short, logical name for the page. This is the page name as it might included in
    079         * an action or page
    080         * render URL (though it will be converted to lower case when it is included).
    081         */
    082        String getName();
    083
    084        /**
    085         * The selector (which includes Locale) used when the page was constructor.
    086         */
    087        ComponentResourceSelector getSelector();
    088
    089        /**
    090         * Invoked during page construction time to connect the page's root component to the page
    091         * instance.
    092         */
    093        void setRootElement(ComponentPageElement component);
    094
    095        /**
    096         * The root component of the page. This is the wrapper around the end developer's view of the
    097         * page.
    098         */
    099        ComponentPageElement getRootElement();
    100
    101        /**
    102         * The root component of the page. A convenience over invoking getRootElement().getComponent().
    103         */
    104        Component getRootComponent();
    105
    106        /**
    107         * Invoked to inform the page that it is being detached from the current request. This occurs
    108         * just before the page
    109         * is returned to the page pool.
    110         * <p/>
    111         * A page may be clean or dirty. A page is dirty if its dirty count is greater than zero (meaning that, during the
    112         * render of the page, some components did not fully render), or if any of its listeners throw an exception from
    113         * containingPageDidDetach().
    114         * <p/>
    115         * The page pool should discard pages that are dirty, rather than store them into the pool.
    116         * <p/>
    117         * Under Tapestry 5.2 and pool-less pages, the result is ignored; all mutable state is expected to be discarded
    118         * automatically from the {@link PerthreadManager}. A future release of Tapestry will likely convert this method to
    119         * type void.
    120         *
    121         * @return true if the page is "dirty", false otherwise
    122         * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidDetach()
    123         */
    124        boolean detached();
    125
    126        /**
    127         * Invoked to inform the page that it is attached to the current request. This occurs when a
    128         * page is first referenced within a request. If the page was created from scratch for this request, the call
    129         * to {@link #loaded()} will preceded the call to {@link #attached()}.
    130         * <p/>
    131         * First all listeners have {@link PageLifecycleListener#restoreStateBeforePageAttach()} invoked, followed by
    132         * {@link PageLifecycleListener#containingPageDidAttach()}.
    133         */
    134        void attached();
    135
    136        /**
    137         * Inform the page that it is now completely loaded.
    138         *
    139         * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
    140         */
    141        void loaded();
    142
    143        /**
    144         * Adds a listener that is notified of large scale page events.
    145         *
    146         * @deprecated in 5.3.4; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
    147         *             {@link #addPageDetachedCallback(Runnable)}  instead
    148         */
    149        void addLifecycleListener(PageLifecycleListener listener);
    150
    151        /**
    152         * Removes a listener that was previously added.
    153         *
    154         * @since 5.2.0
    155         * @deprecated in 5.3.4, due to introduction of {@link #addPageLoadedCallback(Runnable)}
    156         */
    157        void removeLifecycleListener(PageLifecycleListener listener);
    158
    159        /**
    160         * Returns the logger of the root component element. Any logging about page construction or
    161         * activity should be sent
    162         * to this logger.
    163         */
    164        Logger getLogger();
    165
    166        /**
    167         * Retrieves a component element by its nested id (a sequence of simple ids, separated by dots).
    168         * The individual
    169         * names in the nested id are matched without regards to case. A nested id of '' (the empty
    170         * string) returns the root
    171         * element of the page.
    172         *
    173         * @throws IllegalArgumentException
    174         *         if the nestedId does not correspond to a component
    175         */
    176        ComponentPageElement getComponentElementByNestedId(String nestedId);
    177
    178        /**
    179         * Posts a change to a persistent field.
    180         *
    181         * @param resources
    182         *         the component resources for the component or mixin containing the field whose
    183         *         value changed
    184         * @param fieldName
    185         *         the name of the field
    186         * @param newValue
    187         *         the new value for the field
    188         */
    189        void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
    190
    191        /**
    192         * Gets a change for a field within the component.
    193         *
    194         * @param nestedId
    195         *         the nested component id of the component containing the field
    196         * @param fieldName
    197         *         the name of the persistent field
    198         * @return the value, or null if no value is stored
    199         */
    200        Object getFieldChange(String nestedId, String fieldName);
    201
    202        /**
    203         * Discards all persistent field changes for the page containing the component. Changes are
    204         * eliminated from
    205         * persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will
    206         * take effect in the <em>next</em> request (the attached page instance is not affected).
    207         */
    208        void discardPersistentFieldChanges();
    209
    210        /**
    211         * Adds a new listener for page reset events.
    212         *
    213         * @param listener
    214         *         will receive notifications when the page is accessed from a different page
    215         * @since 5.2.0
    216         * @deprecated in 5.3.4,
    217         */
    218        void addResetListener(PageResetListener listener);
    219
    220        /**
    221         * Returns true if there are any {@link PageResetListener} listeners.
    222         *
    223         * @since 5.2.0
    224         */
    225        boolean hasResetListeners();
    226
    227        /**
    228         * Invoked to notify {@link PageResetListener} listeners.
    229         */
    230        void pageReset();
    231
    232        /**
    233         * Invoked once at the end of page construction, to provide page construction statistics.
    234         *
    235         * @since 5.3
    236         */
    237        void setStats(Stats stats);
    238
    239        /**
    240         * Returns the page construction statistics for this page.
    241         */
    242        Stats getStats();
    243
    244        /**
    245         * Returns the number of times the page has been attached to a request. This is a rough measure
    246         * of how important the page is, relative to other pages. This value is volatile, changing constantly.
    247         *
    248         * @since 5.3
    249         */
    250        int getAttachCount();
    251
    252
    253    }