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
015package org.apache.tapestry5.internal.structure;
016
017import org.apache.tapestry5.ComponentResources;
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 * <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 */
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. 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}