| /* |
| * 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); |
| } |
| } |