blob: 7bd97c8888001f6944f892826331995b8558528a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.wicket;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.authorization.UnauthorizedActionException;
import org.apache.wicket.authorization.strategies.page.SimplePageAuthorizationStrategy;
import org.apache.wicket.markup.MarkupException;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.MarkupType;
import org.apache.wicket.markup.resolver.IComponentResolver;
import org.apache.wicket.model.IModel;
import org.apache.wicket.page.IPageManager;
import org.apache.wicket.pageStore.IPageStore;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.session.ISessionStore;
import org.apache.wicket.settings.IDebugSettings;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.lang.Generics;
import org.apache.wicket.util.lang.WicketObjects;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract base class for pages. As a MarkupContainer subclass, a Page can contain a component
* hierarchy and markup in some markup language such as HTML. Users of the framework should not
* attempt to subclass Page directly. Instead they should subclass a subclass of Page that is
* appropriate to the markup type they are using, such as WebPage (for HTML markup).
* <ul>
* <li><b>Construction </b>- When a page is constructed, it is automatically added to the current
* PageMap in the Session. When a Page is added to the Session's PageMap, the PageMap assigns the
* Page an id. A PageMap is roughly equivalent to a browser window and encapsulates a set of pages
* accessible through that window. When a popup window is created, a new PageMap is created for the
* popup.
*
* <li><b>Identity </b>- The Session that a Page is contained in can be retrieved by calling
* Page.getSession(). Page identifiers start at 0 for each PageMap in the Session and increment as
* new pages are added to the map. The PageMap-(and Session)-unique identifier assigned to a given
* Page can be retrieved by calling getId(). So, the first Page added to a new user Session will
* always be named "0".
*
* <li><b>LifeCycle </b>- Subclasses of Page which are interested in lifecycle events can override
* onBeginRequest, onEndRequest() and onModelChanged(). The onBeginRequest() method is inherited
* from Component. A call to onBeginRequest() is made for every Component on a Page before page
* rendering begins. At the end of a request (when rendering has completed) to a Page, the
* onEndRequest() method is called for every Component on the Page.
*
* <li><b>Nested Component Hierarchy </b>- The Page class is a subclass of MarkupContainer. All
* MarkupContainers can have "associated markup", which resides alongside the Java code by default.
* All MarkupContainers are also Component containers. Through nesting, of containers, a Page can
* contain any arbitrary tree of Components. For more details on MarkupContainers, see
* {@link org.apache.wicket.MarkupContainer}.
*
* <li><b>Bookmarkable Pages </b>- Pages can be constructed with any constructor when they are being
* used in a Wicket session, but if you wish to link to a Page using a URL that is "bookmarkable"
* (which implies that the URL will not have any session information encoded in it, and that you can
* call this page directly without having a session first directly from your browser), you need to
* implement your Page with a no-arg constructor or with a constructor that accepts a PageParameters
* argument (which wraps any query string parameters for a request). In case the page has both
* constructors, the constructor with PageParameters will be used.
*
* <li><b>Models </b>- Pages, like other Components, can have models (see {@link IModel}). A Page
* can be assigned a model by passing one to the Page's constructor, by overriding initModel() or
* with an explicit invocation of setModel(). If the model is a
* {@link org.apache.wicket.model.CompoundPropertyModel}, Components on the Page can use the Page's
* model implicitly via container inheritance. If a Component is not assigned a model, the
* initModel() override in Component will cause that Component to use the nearest CompoundModel in
* the parent chain, in this case, the Page's model. For basic CompoundModels, the name of the
* Component determines which property of the implicit page model the component is bound to. If more
* control is desired over the binding of Components to the page model (for example, if you want to
* specify some property expression other than the component's name for retrieving the model
* object), BoundCompoundPropertyModel can be used.
*
* <li><b>Back Button </b>- Pages can support the back button by enabling versioning with a call to
* setVersioned(boolean). If a Page is versioned and changes occur to it which need to be tracked, a
* version manager will be installed using the {@link ISessionStore}'s factory method
* newVersionManager().
*
* <li><b>Security </b>- See {@link IAuthorizationStrategy}, {@link SimplePageAuthorizationStrategy}
*
* @see org.apache.wicket.markup.html.WebPage
* @see org.apache.wicket.MarkupContainer
* @see org.apache.wicket.model.CompoundPropertyModel
* @see org.apache.wicket.Component
*
* @author Jonathan Locke
* @author Chris Turner
* @author Eelco Hillenius
* @author Johan Compagner
*
*/
public abstract class Page extends MarkupContainer implements IRedirectListener, IRequestablePage
{
/** True if the page hierarchy has been modified in the current request. */
private static final int FLAG_IS_DIRTY = FLAG_RESERVED3;
/** Set to prevent marking page as dirty under certain circumstances. */
private static final int FLAG_PREVENT_DIRTY = FLAG_RESERVED4;
/** True if the page should try to be stateless */
private static final int FLAG_STATELESS_HINT = FLAG_RESERVED5;
/** TODO WICKET-NG JAVADOC */
private static final int FLAG_WAS_CREATED_BOOKMARKABLE = FLAG_RESERVED8;
/** Log. */
private static final Logger log = LoggerFactory.getLogger(Page.class);
private static final long serialVersionUID = 1L;
/** Used to create page-unique numbers */
private int autoIndex;
/** Numeric version of this page's id */
private int numericId;
/** Set of components that rendered if component use checking is enabled */
private transient Set<Component> renderedComponents;
/**
* Boolean if the page is stateless, so it doesn't have to be in the page map, will be set in
* urlFor
*/
private transient Boolean stateless = null;
/** Page parameters used to construct this page */
private final PageParameters pageParameters;
/**
* The purpose of render count is to detect stale listener interface links. For example: there
* is a page A rendered in tab 1. Then page A is opened also in tab 2. During render page state
* changes (i.e. some repeater gets rebuilt). This makes all links on tab 1 stale - because they
* no longer match the actual page state. This is done by incrementing render count. When link
* is clicked Wicket checks if it's render count matches the render count value of page
*/
private int renderCount = 0;
/**
* Constructor.
*/
protected Page()
{
this(null, null);
}
/**
* Constructor.
*
* @param model
* See Component
* @see Component#Component(String, IModel)
*/
protected Page(final IModel<?> model)
{
this(null, model);
}
/**
* The {@link PageParameters} parameter will be stored in this page and then those parameters
* will be used to create stateless links to this bookmarkable page.
*
* @param parameters
* externally passed parameters
* @see PageParameters
*/
protected Page(final PageParameters parameters)
{
this(parameters, null);
}
/**
* Construct.
*
* @param parameters
* @param model
*/
private Page(final PageParameters parameters, IModel<?> model)
{
super(null, model);
if (parameters == null)
{
pageParameters = new PageParameters();
}
else
{
pageParameters = parameters;
}
init();
}
/**
* The {@link PageParameters} object that was used to construct this page. This will be used in
* creating stateless/bookmarkable links to this page
*
* @return {@link PageParameters} The construction page parameter
*/
public PageParameters getPageParameters()
{
return pageParameters;
}
/**
* Adds a component to the set of rendered components.
*
* @param component
* The component that was rendered
*/
public final void componentRendered(final Component component)
{
// Inform the page that this component rendered
if (getApplication().getDebugSettings().getComponentUseCheck())
{
if (renderedComponents == null)
{
renderedComponents = new HashSet<Component>();
}
if (renderedComponents.add(component) == false)
{
throw new MarkupException(
"The component " +
component +
" was rendered already. You can render it only once during a render phase. Class relative path: " +
component.getClassRelativePath());
}
if (log.isDebugEnabled())
{
log.debug("Rendered " + component);
}
}
}
/**
* Detaches any attached models referenced by this page.
*/
@Override
public void detachModels()
{
super.detachModels();
}
/**
* @see org.apache.wicket.Component#internalPrepareForRender(boolean)
*/
@Override
public void internalPrepareForRender(boolean setRenderingFlag)
{
if (!isInitialized())
{
// initialize the page if not yet initialized
internalInitialize();
}
super.internalPrepareForRender(setRenderingFlag);
}
/**
* @see #dirty(boolean)
*/
public final void dirty()
{
dirty(false);
}
/** {@inheritDoc} */
public boolean setFreezePageId(boolean freeze)
{
boolean frozen = getFlag(FLAG_PREVENT_DIRTY);
setFlag(FLAG_PREVENT_DIRTY, freeze);
return frozen;
}
/**
* Mark this page as modified in the session. If versioning is supported then a new version of
* the page will be stored in {@link IPageStore page store}
*
* @param isInitialization
* a flag whether this is a page instantiation
*/
public void dirty(final boolean isInitialization)
{
checkHierarchyChange(this);
if (getFlag(FLAG_PREVENT_DIRTY))
{
return;
}
final IPageManager pageManager = getSession().getPageManager();
if (!getFlag(FLAG_IS_DIRTY) && (isVersioned() && pageManager.supportsVersioning() ||
// we need to get pageId for new page instances even when the page doesn't need
// versioning, otherwise pages override each other in the page store and back button
// support is broken
isInitialization))
{
setFlag(FLAG_IS_DIRTY, true);
setNextAvailableId();
pageManager.touchPage(this);
}
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* This method is called when a component was rendered standalone. If it is a <code>
* MarkupContainer</code> then the rendering for that container is checked.
*
* @param component
*/
public final void endComponentRender(Component component)
{
if (component instanceof MarkupContainer)
{
checkRendering((MarkupContainer)component);
}
else
{
renderedComponents = null;
}
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL IT.
*
* Get a page unique number, which will be increased with each call.
*
* @return A page unique number
*/
public final int getAutoIndex()
{
return autoIndex++;
}
/**
* @see org.apache.wicket.Component#getId()
*/
@Override
public final String getId()
{
return Integer.toString(numericId);
}
/**
*
* @return page class
*/
public final Class<? extends Page> getPageClass()
{
return getClass();
}
/**
* @return Size of this page in bytes
*/
@Override
public final long getSizeInBytes()
{
return WicketObjects.sizeof(this);
}
/**
* Returns whether the page should try to be stateless. To be stateless, getStatelessHint() of
* every component on page (and it's behavior) must return true and the page must be
* bookmarkable.
*
* @see org.apache.wicket.Component#getStatelessHint()
*/
@Override
public final boolean getStatelessHint()
{
return getFlag(FLAG_STATELESS_HINT);
}
/**
* @return This page's component hierarchy as a string
*/
public final String hierarchyAsString()
{
final StringBuilder buffer = new StringBuilder();
buffer.append("Page ").append(getId());
visitChildren(new IVisitor<Component, Void>()
{
public void component(final Component component, final IVisit<Void> visit)
{
int levels = 0;
for (Component current = component; current != null; current = current.getParent())
{
levels++;
}
buffer.append(StringValue.repeat(levels, " "))
.append(component.getPageRelativePath())
.append(":")
.append(Classes.simpleName(component.getClass()));
}
});
return buffer.toString();
}
/**
* Bookmarkable page can be instantiated using a bookmarkable URL.
*
* @return Returns true if the page is bookmarkable.
*/
public boolean isBookmarkable()
{
return getApplication().getPageFactory().isBookmarkable(getClass());
}
/**
* Override this method and return true if your page is used to display Wicket errors. This can
* help the framework prevent infinite failure loops.
*
* @return True if this page is intended to display an error to the end user.
*/
public boolean isErrorPage()
{
return false;
}
/**
* Determine the "statelessness" of the page while not changing the cached value.
*
* @return boolean value
*/
private boolean peekPageStateless()
{
Boolean old = stateless;
Boolean res = isPageStateless();
stateless = old;
return res;
}
/**
* Gets whether the page is stateless. Components on stateless page must not render any stateful
* urls, and components on stateful page must not render any stateless urls. Stateful urls are
* urls, which refer to a certain (current) page instance.
*
* @return Whether this page is stateless
*/
public final boolean isPageStateless()
{
if (isBookmarkable() == false)
{
stateless = Boolean.FALSE;
if (getStatelessHint())
{
log.warn("Page '" + this + "' is not stateless because it is not bookmarkable, " +
"but the stateless hint is set to true!");
}
}
if (getStatelessHint() == false)
{
return false;
}
if (stateless == null)
{
if (isStateless() == false)
{
stateless = Boolean.FALSE;
}
}
if (stateless == null)
{
Component statefulComponent = visitChildren(Component.class,
new IVisitor<Component, Component>()
{
public void component(final Component component, final IVisit<Component> visit)
{
if (!component.isStateless())
{
visit.stop(component);
}
}
});
stateless = statefulComponent == null;
if (log.isDebugEnabled() && !stateless.booleanValue() && getStatelessHint())
{
log.debug("Page '{}' is not stateless because of component with path '{}'.", this,
statefulComponent.getPageRelativePath());
}
}
return stateless;
}
/**
* Redirect to this page.
*
* @see org.apache.wicket.IRedirectListener#onRedirect()
*/
public final void onRedirect()
{
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* Set the id for this Page. This method is called by PageMap when a Page is added because the
* id, which is assigned by PageMap, is not known until this time.
*
* @param id
* The id
*/
public final void setNumericId(final int id)
{
numericId = id;
}
/**
* Sets whether the page should try to be stateless. To be stateless, getStatelessHint() of
* every component on page (and it's behavior) must return true and the page must be
* bookmarkable.
*
* @param value
* whether the page should try to be stateless
*/
public final void setStatelessHint(boolean value)
{
if (value && !isBookmarkable())
{
throw new WicketRuntimeException(
"Can't set stateless hint to true on a page when the page is not bookmarkable, page: " +
this);
}
setFlag(FLAG_STATELESS_HINT, value);
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* This method is called when a component will be rendered standalone.
*
* @param component
*
*/
public final void startComponentRender(Component component)
{
renderedComponents = null;
}
/**
* Get the string representation of this container.
*
* @return String representation of this container
*/
@Override
public String toString()
{
return "[Page class = " + getClass().getName() + ", id = " + getId() + ", render count = " +
getRenderCount() + "]";
}
/**
* Throw an exception if not all components rendered.
*
* @param renderedContainer
* The page itself if it was a full page render or the container that was rendered
* standalone
*/
private void checkRendering(final MarkupContainer renderedContainer)
{
// If the application wants component uses checked and
// the response is not a redirect
final IDebugSettings debugSettings = getApplication().getDebugSettings();
if (debugSettings.getComponentUseCheck())
{
final List<Component> unrenderedComponents = new ArrayList<Component>();
final StringBuilder buffer = new StringBuilder();
renderedContainer.visitChildren(new IVisitor<Component, Void>()
{
public void component(final Component component, final IVisit<Void> visit)
{
// If component never rendered
if (renderedComponents == null || !renderedComponents.contains(component))
{
// If not an auto component ...
if (!component.isAuto() && component.isVisibleInHierarchy())
{
// Increase number of unrendered components
unrenderedComponents.add(component);
// Add to explanatory string to buffer
buffer.append(Integer.toString(unrenderedComponents.size()))
.append(". ")
.append(component)
.append('\n');
String metadata = component.getMetaData(Component.CONSTRUCTED_AT_KEY);
if (metadata != null)
{
buffer.append(metadata);
}
metadata = component.getMetaData(Component.ADDED_AT_KEY);
if (metadata != null)
{
buffer.append(metadata);
}
}
else
{
// if the component is not visible in hierarchy we
// should not visit its children since they are also
// not visible
visit.dontGoDeeper();
}
}
}
});
// Throw exception if any errors were found
if (unrenderedComponents.size() > 0)
{
renderedComponents = null;
List<Component> transparentContainerChildren = Generics.newArrayList();
Iterator<Component> iterator = unrenderedComponents.iterator();
outerWhile : while (iterator.hasNext())
{
Component component = iterator.next();
// If any of the transparentContainerChildren is a parent to component, then
// ignore it.
for (Component transparentContainerChild : transparentContainerChildren)
{
MarkupContainer parent = component.getParent();
while (parent != null)
{
if (parent == transparentContainerChild)
{
iterator.remove();
continue outerWhile;
}
parent = parent.getParent();
}
}
if (hasInvisibleTransparentChild(component.getParent(), component))
{
// If we found a transparent container that isn't visible then ignore this
// component and only do a debug statement here.
if (log.isDebugEnabled())
{
log.debug(
"Component {} wasn't rendered but might have a transparent parent.",
component);
}
transparentContainerChildren.add(component);
iterator.remove();
continue outerWhile;
}
}
// if still > 0
if (unrenderedComponents.size() > 0)
{
// Throw exception
throw new WicketRuntimeException(
"The component(s) below failed to render. Possible reasons could be that: 1) you have added a component in code but forgot to reference it in the markup (thus the component will never be rendered), 2) if your components were added in a parent container then make sure the markup for the child container includes them in <wicket:extend>.\n\n" +
buffer.toString());
}
}
}
// Get rid of set
renderedComponents = null;
}
private boolean hasInvisibleTransparentChild(final MarkupContainer root, final Component self)
{
for (Component sibling : root)
{
if ((sibling != self) && (sibling instanceof IComponentResolver) &&
(sibling instanceof MarkupContainer))
{
if (!sibling.isVisible())
{
return true;
}
else
{
boolean rtn = hasInvisibleTransparentChild((MarkupContainer)sibling, self);
if (rtn == true)
{
return true;
}
}
}
}
return false;
}
/**
* Initializes Page by adding it to the Session and initializing it.
*/
private void init()
{
if (isBookmarkable())
{
setStatelessHint(true);
}
// Set versioning of page based on default
setVersioned(getApplication().getPageSettings().getVersionPagesByDefault());
// All Pages are born dirty so they get clustered right away
dirty(true);
// this is a bit of a dirty hack, but calling dirty(true) results in isStateless called
// which is bound to set the stateless cache to true as there are no components yet
stateless = null;
}
/**
*
*/
private void setNextAvailableId()
{
setNumericId(getSession().nextPageId());
}
/**
* This method will be called for all components that are changed on the page So also auto
* components or components that are not versioned.
*
* If the parent is given that it was a remove or add from that parent of the given component.
* else it was just a internal property change of that component.
*
* @param component
* @param parent
*/
protected void componentChanged(Component component, MarkupContainer parent)
{
if (!component.isAuto())
{
dirty();
}
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL OR OVERRIDE.
*
* @see org.apache.wicket.Component#internalOnModelChanged()
*/
@Override
protected final void internalOnModelChanged()
{
visitChildren(new IVisitor<Component, Void>()
{
public void component(final Component component, final IVisit<Void> visit)
{
// If form component is using form model
if (component.sameInnermostModel(Page.this))
{
component.modelChanged();
}
}
});
}
@Override
void internalOnAfterConfigure()
{
super.internalOnAfterConfigure();
// check if the page can be rendered
if (!isActionAuthorized(RENDER))
{
if (log.isDebugEnabled())
{
log.debug("Page not allowed to render: " + this);
}
throw new UnauthorizedActionException(this, Component.RENDER);
}
}
/**
*
* @see org.apache.wicket.Component#onBeforeRender()
*/
@Override
protected void onBeforeRender()
{
// Make sure it is really empty
renderedComponents = null;
// if the page is stateless, reset the flag so that it is tested again
if (Boolean.TRUE.equals(stateless))
{
stateless = null;
}
super.onBeforeRender();
// If any of the components on page is not stateless, we need to bind the session
// before we start rendering components, as then jsessionid won't be appended
// for links rendered before first stateful component
if (getSession().isTemporary() && !peekPageStateless())
{
getSession().bind();
}
}
/**
* @see org.apache.wicket.Component#onAfterRender()
*/
@Override
protected void onAfterRender()
{
super.onAfterRender();
// Check rendering if it happened fully
checkRendering(this);
// clean up debug meta data if component check is on
if (getApplication().getDebugSettings().getComponentUseCheck())
{
visitChildren(new IVisitor<Component, Void>()
{
public void component(final Component component, final IVisit<Void> visit)
{
component.setMetaData(Component.CONSTRUCTED_AT_KEY, null);
component.setMetaData(Component.ADDED_AT_KEY, null);
}
});
}
if (!isPageStateless())
{
// trigger creation of the actual session in case it was deferred
getSession().getSessionStore().getSessionId(RequestCycle.get().getRequest(), true);
// Add/touch the response page in the session.
getSession().getPageManager().touchPage(this);
}
if (getApplication().getDebugSettings().isOutputMarkupContainerClassName())
{
Class<?> klass = getClass();
while (klass.isAnonymousClass())
{
klass = klass.getSuperclass();
}
getResponse().write("<!-- Page Class ");
getResponse().write(klass.getName());
getResponse().write(" -->\n");
}
}
/**
* @see org.apache.wicket.Component#onDetach()
*/
@Override
protected void onDetach()
{
if (log.isDebugEnabled())
{
log.debug("ending request for page " + this + ", request " + getRequest());
}
setFlag(FLAG_IS_DIRTY, false);
super.onDetach();
}
/**
* @see org.apache.wicket.MarkupContainer#onRender()
*/
@Override
protected void onRender()
{
// Loop through the markup in this container
MarkupStream markupStream = new MarkupStream(getMarkup());
renderAll(markupStream, null);
}
/**
* A component was added.
*
* @param component
* The component that was added
*/
final void componentAdded(final Component component)
{
if (!component.isAuto())
{
dirty();
}
}
/**
* A component's model changed.
*
* @param component
* The component whose model is about to change
*/
final void componentModelChanging(final Component component)
{
dirty();
}
/**
* A component was removed.
*
* @param component
* The component that was removed
*/
final void componentRemoved(final Component component)
{
if (!component.isAuto())
{
dirty();
}
}
/**
*
* @param component
*/
final void componentStateChanging(final Component component)
{
if (!component.isAuto())
{
dirty();
}
}
/**
* Set page stateless
*
* @param stateless
*/
void setPageStateless(Boolean stateless)
{
this.stateless = stateless;
}
/**
* @see org.apache.wicket.MarkupContainer#getMarkupType()
*/
@Override
public MarkupType getMarkupType()
{
throw new UnsupportedOperationException(
"Page does not support markup. This error can happen if you have extended Page directly, instead extend WebPage");
}
/**
* Gets page instance's unique identifier
*
* @return instance unique identifier
*/
public PageReference getPageReference()
{
setStatelessHint(false);
return new PageReference(numericId);
}
/**
* @see org.apache.wicket.page.IManageablePage#getPageId()
*/
public int getPageId()
{
return numericId;
}
/**
* @see org.apache.wicket.request.component.IRequestablePage#getRenderCount()
*/
public int getRenderCount()
{
return renderCount;
}
/**
* TODO WICKET-NG javadoc
*
* @param wasCreatedBookmarkable
*/
public final void setWasCreatedBookmarkable(boolean wasCreatedBookmarkable)
{
setFlag(FLAG_WAS_CREATED_BOOKMARKABLE, wasCreatedBookmarkable);
}
/** TODO WICKET-NG javadoc */
public final boolean wasCreatedBookmarkable()
{
return getFlag(FLAG_WAS_CREATED_BOOKMARKABLE);
}
/**
* @see org.apache.wicket.request.component.IRequestablePage#renderPage()
*/
public void renderPage()
{
// page id is frozen during the render
final boolean frozen = setFreezePageId(true);
try
{
++renderCount;
render();
}
finally
{
setFreezePageId(frozen);
}
}
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
*
* @param component
* @return if this component was render in this page
*/
public final boolean wasRendered(Component component)
{
return renderedComponents != null && renderedComponents.contains(component);
}
}