/* * 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.core.util.string; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ThreadContext; import org.apache.wicket.core.request.handler.PageProvider; import org.apache.wicket.markup.IMarkupCacheKeyProvider; import org.apache.wicket.markup.IMarkupResourceStreamProvider; import org.apache.wicket.markup.MarkupNotFoundException; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.protocol.http.BufferedWebResponse; import org.apache.wicket.request.Response; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.StringResourceStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A helper class for rendering components and pages. * * <p><strong>Note</strong>: {@link #renderComponent(Component)} does <strong>not</strong> * support rendering {@link org.apache.wicket.markup.html.panel.Fragment} instances!</p> */ public class ComponentRenderer { private static final Logger LOGGER = LoggerFactory.getLogger(ComponentRenderer.class); /** * Collects the html generated by the rendering of a page. * * @param pageProvider * the provider of the page class/instance and its parameters * @return the html rendered by a page */ public static CharSequence renderPage(final PageProvider pageProvider) { Application application = Application.get(); RequestCycle originalRequestCycle = RequestCycle.get(); BufferedWebResponse tempResponse = new BufferedWebResponse(null); RequestCycle tempRequestCycle = application.createRequestCycle(originalRequestCycle.getRequest(), tempResponse); try { ThreadContext.setRequestCycle(tempRequestCycle); pageProvider.getPageInstance().renderPage(); } finally { ThreadContext.setRequestCycle(originalRequestCycle); } return tempResponse.getText(); } /** * Collects the html generated by the rendering of a component. * * <p> * NOTE: this method is meant to render fresh component instances that are disposed after the * html has been generate. To avoid unwanted side effects do not use it with components that * are from an existing hierarchy. * </p> * * @param component * the component to render. * @return the html rendered by the component */ public static CharSequence renderComponent(final Component component) { RequestCycle requestCycle = RequestCycle.get(); final Response originalResponse = requestCycle.getResponse(); BufferedWebResponse tempResponse = new BufferedWebResponse(null); MarkupContainer oldParent = component.getParent(); if (oldParent != null && LOGGER.isWarnEnabled()) { LOGGER.warn("Component '{}' with a parent '{}' is passed for standalone rendering. " + "It is recommended to render only orphan components because they are not cleaned up/detached" + " after the rendering.", component, oldParent); } try { requestCycle.setResponse(tempResponse); // add the component to a dummy page just for the rendering RenderPage page = new RenderPage(component); page.internalInitialize(); component.render(); } finally { if (oldParent != null) { oldParent.add(component); // re-add the child to its old parent } requestCycle.setResponse(originalResponse); } return tempResponse.getText(); } /** * A page used as a parent for the component based rendering. */ private static class RenderPage extends WebPage implements IMarkupResourceStreamProvider, IMarkupCacheKeyProvider { /** * Markup to use when the component to render is not already added to a MarkupContainer */ private static final String DEFAULT_MARKUP = "<wicket:container wicket:id='%s'></wicket:container>"; /** * The markup of the component to render */ private final String markup; private RenderPage(Component component) { // do not store the page in IPageStore/IDataStore. WICKET-5422 setStatelessHint(true); String componentMarkup; try { componentMarkup = component.getMarkup().toString(true); } catch (MarkupNotFoundException mnfx) { componentMarkup = String.format(DEFAULT_MARKUP, component.getId()); } this.markup = componentMarkup; add(component); // this changes the component's parent } @Override public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass) { return new StringResourceStream(markup); } @Override public String getCacheKey(MarkupContainer container, Class<?> containerClass) { // no caching for this page return null; } @Override public boolean isBookmarkable() { // pretend the page is bookmarkable to make it stateless. WICKET-5422 return true; } } }