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}