blob: 9de673931441e5df1fb245780478e7038b1d84b2 [file] [log] [blame]
// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.internal.structure;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.apache.tapestry5.runtime.Component;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.slf4j.Logger;
import java.util.Locale;
/**
* Represents a unique page within the application. Pages are part of the <em>internal</em> structure of a Tapestry
* application; end developers who refer to "page" are really referring to the {@link #getRootComponent() root
* component} of the actual page.
* <p/>
* Starting in release 5.2, the nature of pages changed considerably. Pages are no longer pooled instances. Instead,
* pages are shared instances (per locale) but all internal <em>mutable</em> state is stored inside
* {@link PerthreadManager}. Page construction time is considered to extend past the
* {@link PageLifecycleListener#containingPageDidLoad()} lifecycle notification. This is not quite perfect from a
* threading point-of-view (arguably, even write-once-read-many fields should be protected by synchronized blocks or
* other mechanisms). At best, we can be assured that the entire page construction phase is protected by a single
* synchronized block (but not on the page itself). An ideal system would build the page bottom to top so that all
* assignments could take place in constructors, assigning to final fields. Maybe some day.
* <p/>
* The Page object is never visible to end-user code. The page also exists to provide a kind of service to components
* embedded (directly or indirectly) within the page.
*/
public interface Page
{
/**
* Returns the short, logical name for the page. This is the page name as it might included in
* an action or page
* render URL (though it will be converted to lower case when it is included).
*/
String getName();
/**
* The locale for which the page is localized. This is set when the page is created and does not
* change.
*/
Locale getLocale();
/**
* Invoked during page construction time to connect the page's root component to the page
* instance.
*/
void setRootElement(ComponentPageElement component);
/**
* The root component of the page. This is the wrapper around the end developer's view of the
* page.
*/
ComponentPageElement getRootElement();
/**
* The root component of the page. A convenience over invoking getRootElement().getComponent().
*/
Component getRootComponent();
/**
* Invoked to inform the page that it is being detached from the current request. This occurs
* just before the page
* is returned to the page pool.
* <p/>
* A page may be clean or dirty. A page is dirty if its dirty count is greater than zero (meaning that, during the
* render of the page, some components did not fully render), or if any of its listeners throw an exception from
* containingPageDidDetach().
* <p/>
* The page pool should discard pages that are dirty, rather than store them into the pool.
* <p>
* Under Tapestry 5.2 and pool-less pages, the result is ignored; all mutable state is expected to be discarded
* automatically from the {@link PerthreadManager}. A future release of Tapestry will likely convert this method to
* type void.
*
* @return true if the page is "dirty", false otherwise
* @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidDetach()
*/
boolean detached();
/**
* Invoked to inform the page that it is attached to the current request. This occurs when a
* page is first referenced within a request. If the page was created from scratch for this request, the call
* to {@link #loaded()} will preceded the call to {@link #attached()}.
* <p>
* First all listeners have {@link PageLifecycleListener#restoreStateBeforePageAttach()} invoked, followed by
* {@link PageLifecycleListener#containingPageDidAttach()}.
*/
void attached();
/**
* Inform the page that it is now completely loaded.
*
* @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
*/
void loaded();
/**
* Adds a listener that is notified of large scale page events.
*/
void addLifecycleListener(PageLifecycleListener listener);
/**
* Removes a listener that was previously added.
*
* @since 5.2.0
*/
void removeLifecycleListener(PageLifecycleListener listener);
/**
* Returns the logger of the root component element. Any logging about page construction or
* activity should be sent
* to this logger.
*/
Logger getLogger();
/**
* Retrieves a component element by its nested id (a sequence of simple ids, separated by dots).
* The individual
* names in the nested id are matched without regards to case. A nested id of '' (the empty
* string) returns the root
* element of the page.
*
* @throws IllegalArgumentException
* if the nestedId does not correspond to a component
*/
ComponentPageElement getComponentElementByNestedId(String nestedId);
/**
* Posts a change to a persistent field.
*
* @param resources
* the component resources for the component or mixin containing the field whose
* value changed
* @param fieldName
* the name of the field
* @param newValue
* the new value for the field
*/
void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
/**
* Gets a change for a field within the component.
*
* @param nestedId
* the nested component id of the component containing the field
* @param fieldName
* the name of the persistent field
* @return the value, or null if no value is stored
*/
Object getFieldChange(String nestedId, String fieldName);
/**
* Called as a component initially starts to render itself. This is used to check for the cases
* where a component
* causes a runtime exception that aborts the render early, leaving the page in an invalid
* state.
*
* @deprecated No longer useful with non-pooled pages, to be removed for efficiency
*/
void incrementDirtyCount();
/**
* Called as a component finishes rendering itself.
*
* @deprecated No longer useful with non-pooled pages, to be removed for efficiency
*/
void decrementDirtyCount();
/**
* Discards all persistent field changes for the page containing the component. Changes are
* eliminated from
* persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will
* take effect in the <em>next</em> request (the attached page instance is not affected).
*/
void discardPersistentFieldChanges();
/**
* Adds a new listener for page reset events.
*
* @param listener
* will receive notifications when the page is accessed from a different page
* @since 5.2.0
*/
void addResetListener(PageResetListener listener);
/**
* Returns true if there are any {@link PageResetListener} listeners.
*
* @since 5.2.0
*/
boolean hasResetListeners();
/**
* Invoked to notify {@link PageResetListener} listeners.
*/
void pageReset();
}