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