Hello Sven, Is this correct import? import com.google.common.base.Supplier;
On Thu, Dec 7, 2017 at 3:24 PM, <svenme...@apache.org> wrote: > Repository: wicket > Updated Branches: > refs/heads/sandbox/component-renderer [created] 6e6c273fd > > > render independently from web or tester > > > Project: http://git-wip-us.apache.org/repos/asf/wicket/repo > Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/6e6c273f > Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/6e6c273f > Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/6e6c273f > > Branch: refs/heads/sandbox/component-renderer > Commit: 6e6c273fd32325a447cc32c751d5ce0c083e7ed1 > Parents: 693dad3 > Author: Sven Meier <svenme...@apache.org> > Authored: Thu Dec 7 09:24:12 2017 +0100 > Committer: Sven Meier <svenme...@apache.org> > Committed: Thu Dec 7 09:24:12 2017 +0100 > > ---------------------------------------------------------------------- > .../core/util/string/ComponentRenderer.java | 324 ++++++++++++++++++- > .../ComponentRendererInstanceTest.java | 52 +++ > 2 files changed, 369 insertions(+), 7 deletions(-) > ---------------------------------------------------------------------- > > > http://git-wip-us.apache.org/repos/asf/wicket/blob/6e6c273f/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java > ---------------------------------------------------------------------- > diff --git > a/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java > > b/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java > index 0cf63bf..e971bc2 100644 > --- > a/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java > +++ > b/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java > @@ -16,35 +16,341 @@ > */ > package org.apache.wicket.core.util.string; > > +import java.io.Serializable; > +import java.util.List; > +import java.util.Set; > + > import org.apache.wicket.Application; > import org.apache.wicket.Component; > import org.apache.wicket.MarkupContainer; > +import org.apache.wicket.Page; > +import org.apache.wicket.RuntimeConfigurationType; > +import org.apache.wicket.Session; > 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.mock.MockApplication; > +import org.apache.wicket.mock.MockWebRequest; > import org.apache.wicket.protocol.http.BufferedWebResponse; > +import org.apache.wicket.protocol.http.WebApplication; > +import org.apache.wicket.protocol.http.mock.MockServletContext; > +import org.apache.wicket.request.Request; > import org.apache.wicket.request.Response; > +import org.apache.wicket.request.Url; > import org.apache.wicket.request.cycle.RequestCycle; > +import org.apache.wicket.request.http.WebRequest; > +import org.apache.wicket.serialize.ISerializer; > +import org.apache.wicket.session.ISessionStore; > import org.apache.wicket.util.resource.IResourceStream; > import org.apache.wicket.util.resource.StringResourceStream; > import org.slf4j.Logger; > import org.slf4j.LoggerFactory; > > +import com.google.common.base.Supplier; > + > /** > * 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> > + * <p> > + * With the static methods of this class components and pages can be rendered > + * on a thread already processing an {@link Application}. > + * <p> > + * If you want to render independently from any web request processing (e.g. > generating an email > + * body on a worker thread), you can create an instance of this class.<br/> > + * You may use an existing application, create a fresh one or just use the > defaults of > + * {@link #ComponentRenderer()} for a mocked application with sensible > defaults. > + * <p> > + * Note: For performance instances can and should be reused, be sure to call > {@link #destroy()} > + * when they are no longer needed. > */ > public class ComponentRenderer > { > private static final Logger LOGGER = > LoggerFactory.getLogger(ComponentRenderer.class); > > + private WebApplication application; > + > + /** > + * A renderer using a default mocked application, which > + * <ul> > + * <li>never shares anything in a session</li> > + * <li>never serializes anything</li> > + * </ul> > + */ > + public ComponentRenderer() > + { > + this(new MockApplication() > + { > + @Override > + public RuntimeConfigurationType getConfigurationType() > + { > + return RuntimeConfigurationType.DEPLOYMENT; > + } > + > + @Override > + protected void init() > + { > + super.init(); > + > + setSessionStoreProvider(() -> new > NeverSessionStore()); > + getFrameworkSettings().setSerializer(new > NeverSerializer()); > + } > + }); > + } > + > + /** > + * A renderer using the given application. > + * <p> > + * If the application was not yet initialized - e.g. it is not reused > from an already running web > + * container - it will be initialized. > + */ > + public ComponentRenderer(WebApplication application) > + { > + this.application = application; > + > + if (application.getName() == null) { > + // not yet initialized > + > + inThreadContext(new Runnable() > + { > + @Override > + public void run() > + { > + application.setServletContext(new > MockServletContext(application, null)); > + > application.setName("ComponentRenderer[" + > System.identityHashCode(ComponentRenderer.this) + "]"); > + application.initApplication(); > + } > + }); > + } > + } > + > + /** > + * Destroy this renderer. > + */ > + public void destroy() > + { > + inThreadContext(new Runnable() > + { > + @Override > + public void run() > + { > + application.internalDestroy(); > + } > + }); > + } > + > + /** > + * > + * Collects the html generated by the rendering a component. > + * > + * @param component > + * supplier of the component > + * @return the html rendered by the panel > + */ > + public CharSequence renderComponent(final Supplier<Component> > component) > + { > + return renderPage(new Supplier<Page>() > + { > + @Override > + public Page get() > + { > + return new RenderPage(component.get()); > + } > + }); > + } > + > + /** > + * Collects the html generated by the rendered a component. > + * > + * @param page > + * supplier of the page > + * @return the html rendered by the panel > + */ > + public CharSequence renderPage(final Supplier<? extends Page> page) > + { > + return inThreadContext(new Supplier<CharSequence>() > + { > + @Override > + public CharSequence get() > + { > + WebRequest request = newWebRequest(); > + > + BufferedWebResponse response = new > BufferedWebResponse(null); > + > + RequestCycle cycle = > application.createRequestCycle(request, response); > + > + ThreadContext.setRequestCycle(cycle); > + > + page.get().renderPage(); > + > + return response.getText(); > + } > + }); > + } > + > + /** > + * Run the given runnable inside a valid {@link ThreadContext}. > + * > + * @param runnable > + * runnable > + */ > + private void inThreadContext(Runnable runnable) { > + inThreadContext(() -> { > + runnable.run(); > + return null; > + }); > + } > + > + /** > + * Get the result from the given supplier inside a valid {@link > ThreadContext}. > + * > + * @param supplier > + * supplier > + * @return result of {@link Supplier#get()} > + */ > + private <T> T inThreadContext(Supplier<T> supplier) > + { > + ThreadContext oldContext = ThreadContext.detach(); > + > + try > + { > + ThreadContext.setApplication(application); > + > + return supplier.get(); > + } > + finally > + { > + > + ThreadContext.restore(oldContext); > + } > + } > + > + /** > + * Create a new request, by default a {@link MockWebRequest}. > + */ > + protected WebRequest newWebRequest() > + { > + return new MockWebRequest(Url.parse("/")); > + } > + > + /** > + * Never serialize. > + */ > + private static final class NeverSerializer implements ISerializer > + { > + @Override > + public byte[] serialize(Object object) > + { > + return null; > + } > + > + @Override > + public Object deserialize(byte[] data) > + { > + return null; > + } > + } > + > + /** > + * Never share anything. > + */ > + private static class NeverSessionStore implements ISessionStore > + { > + > + @Override > + public Serializable getAttribute(Request request, String name) > + { > + return null; > + } > + > + @Override > + public List<String> getAttributeNames(Request request) > + { > + return null; > + } > + > + @Override > + public void setAttribute(Request request, String name, > Serializable value) > + { > + } > + > + @Override > + public void removeAttribute(Request request, String name) > + { > + } > + > + @Override > + public void invalidate(Request request) > + { > + } > + > + @Override > + public String getSessionId(Request request, boolean create) > + { > + return null; > + } > + > + @Override > + public Session lookup(Request request) > + { > + return null; > + } > + > + @Override > + public void bind(Request request, Session newSession) > + { > + } > + > + @Override > + public void flushSession(Request request, Session session) > + { > + } > + > + @Override > + public void destroy() > + { > + } > + > + @Override > + public void registerUnboundListener(UnboundListener listener) > + { > + } > + > + @Override > + public void unregisterUnboundListener(UnboundListener > listener) > + { > + } > + > + @Override > + public Set<UnboundListener> getUnboundListener() > + { > + return null; > + } > + > + @Override > + public void registerBindListener(BindListener listener) > + { > + } > + > + @Override > + public void unregisterBindListener(BindListener listener) > + { > + } > + > + > + @Override > + > + public Set<BindListener> getBindListeners() > + { > + return null; > + } > + } > + > /** > * Collects the html generated by the rendering of a page. > + * <p> > + * Important note: Must be called on a thread already processing a > {@link WebApplication}! > * > * @param pageProvider > * the provider of the page class/instance and its > parameters > @@ -75,12 +381,16 @@ public class ComponentRenderer > > /** > * 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 > + * Important notes: > + * <ul> > + * <li>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> > + * are from an existing hierarchy.</li> > + * <li>does <strong>not</strong> support rendering > + * {@link org.apache.wicket.markup.html.panel.Fragment} instances</li> > + * <li>must be called on a thread already processing a {@link > WebApplication}!</li> > + * </ul> > * > * @param component > * the component to render. > > http://git-wip-us.apache.org/repos/asf/wicket/blob/6e6c273f/wicket-core/src/test/java/org/apache/wicket/core/util/string/componentrenderer/ComponentRendererInstanceTest.java > ---------------------------------------------------------------------- > diff --git > a/wicket-core/src/test/java/org/apache/wicket/core/util/string/componentrenderer/ComponentRendererInstanceTest.java > > b/wicket-core/src/test/java/org/apache/wicket/core/util/string/componentrenderer/ComponentRendererInstanceTest.java > new file mode 100644 > index 0000000..4aa6dfa > --- /dev/null > +++ > b/wicket-core/src/test/java/org/apache/wicket/core/util/string/componentrenderer/ComponentRendererInstanceTest.java > @@ -0,0 +1,52 @@ > +/* > +/* > + * 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.componentrenderer; > + > +import static org.junit.Assert.assertEquals; > + > +import org.apache.wicket.core.util.string.ComponentRenderer; > +import org.apache.wicket.markup.html.basic.Label; > +import org.junit.After; > +import org.junit.Before; > +import org.junit.Test; > + > +/** > + * Tests for {@link ComponentRenderer} > + */ > +public class ComponentRendererInstanceTest > +{ > + private ComponentRenderer renderer; > + > + @Before > + public void setup() { > + renderer = new ComponentRenderer(); > + } > + > + @After > + public void destroy() { > + renderer.destroy(); > + } > + > + @Test > + public void render() > + { > + CharSequence html = renderer.renderComponent(() -> new > Label("id", "Hello renderer")); > + > + assertEquals("Hello renderer", html.toString()); > + } > +} > \ No newline at end of file > -- WBR Maxim aka solomax