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}