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 }