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 }