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    }