You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2017/12/07 19:43:08 UTC
[1/2] wicket git commit: easier rendering of components, closes #249
Repository: wicket
Updated Branches:
refs/heads/master 693dad38a -> e7597f03f
easier rendering of components, closes #249
setup ThreadLocal independently from filter or tester
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/a148e088
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/a148e088
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/a148e088
Branch: refs/heads/master
Commit: a148e088f13a4ec1cebfc938e24d6215e19576c8
Parents: 693dad3
Author: Sven Meier <sv...@apache.org>
Authored: Thu Dec 7 09:24:12 2017 +0100
Committer: Sven Meier <sv...@apache.org>
Committed: Thu Dec 7 20:40:36 2017 +0100
----------------------------------------------------------------------
.../core/util/string/ComponentRenderer.java | 346 ++++++++++++++++++-
.../ComponentRendererInstanceTest.java | 51 +++
2 files changed, 379 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/wicket/blob/a148e088/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..d583057 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,18 +16,34 @@
*/
package org.apache.wicket.core.util.string;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+
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.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;
@@ -35,20 +51,303 @@ 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>
+ * <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 automatically created
+ * mocked application with sensible defaults.
+ * <p>
+ * Note: For performance reasons 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 Application 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());
+ }
+ });
+ }
+
/**
- * Collects the html generated by the rendering of a page.
+ * 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.
+ *
+ * @param application the application to render components in
+ *
+ * @see Application#initApplication()
+ */
+ public ComponentRenderer(Application application)
+ {
+ this.application = application;
+
+ if (application.getName() == null)
+ {
+ // not yet initialized
+ inThreadContext(this::initApplication);
+ }
+ }
+
+ private void initApplication()
+ {
+ if (application instanceof WebApplication) {
+ WebApplication webApplication = (WebApplication)application;
+
+ // WebApplication requires a servlet context
+ webApplication.setServletContext(new MockServletContext(application, null));
+ }
+
+ application.setName("ComponentRenderer[" + System.identityHashCode(ComponentRenderer.this) + "]");
+ application.initApplication();
+ }
+
+ /**
+ * Destroy this renderer.
+ */
+ public void destroy()
+ {
+ inThreadContext(() -> {
+ application.internalDestroy();
+ application = null;
+ });
+ }
+
+ /**
+ *
+ * Collects the Html generated by rendering a component.
+ *
+ * @param component
+ * supplier of the component
+ * @return html rendered by the panel
+ */
+ public CharSequence renderComponent(final Supplier<Component> component)
+ {
+ return renderPage(() -> new RenderPage(component.get()));
+ }
+
+ /**
+ * Collects the html generated by rendering a page.
+ *
+ * @param page
+ * supplier of the page
+ * @return the html rendered by the panel
+ */
+ public CharSequence renderPage(final Supplier<? extends Page> page)
+ {
+ return inThreadContext(() -> {
+ Request request = newRequest();
+
+ 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 bound {@link ThreadContext}.
+ *
+ * @param runnable
+ * runnable
+ */
+ private void inThreadContext(Runnable runnable)
+ {
+ inThreadContext(() -> {
+ runnable.run();
+ return null;
+ });
+ }
+
+ /**
+ * Get the result from the given supplier inside a bound {@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 Request newRequest()
+ {
+ 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 a page.
+ * <p>
+ * Important note: Must be called on a thread bound to an application's {@link ThreadContext}!
*
* @param pageProvider
* the provider of the page class/instance and its parameters
* @return the html rendered by a page
+ *
+ * @see ThreadContext
*/
public static CharSequence renderPage(final PageProvider pageProvider)
{
@@ -58,7 +357,8 @@ public class ComponentRenderer
BufferedWebResponse tempResponse = new BufferedWebResponse(null);
- RequestCycle tempRequestCycle = application.createRequestCycle(originalRequestCycle.getRequest(), tempResponse);
+ RequestCycle tempRequestCycle = application
+ .createRequestCycle(originalRequestCycle.getRequest(), tempResponse);
try
{
@@ -74,17 +374,23 @@ public class ComponentRenderer
}
/**
- * Collects the html generated by the rendering of a component.
- *
+ * Collects the Html generated by rendering 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>
+ * 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.</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 bound to an application's {@link ThreadContext}!</li>
+ * </ul>
*
* @param component
* the component to render.
* @return the html rendered by the component
+ *
+ * @see ThreadContext
*/
public static CharSequence renderComponent(final Component component)
{
@@ -97,10 +403,9 @@ public class ComponentRenderer
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);
+ 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
@@ -129,7 +434,10 @@ public class ComponentRenderer
/**
* A page used as a parent for the component based rendering.
*/
- private static class RenderPage extends WebPage implements IMarkupResourceStreamProvider, IMarkupCacheKeyProvider
+ private static class RenderPage extends WebPage
+ implements
+ IMarkupResourceStreamProvider,
+ IMarkupCacheKeyProvider
{
/**
* Markup to use when the component to render is not already added to a MarkupContainer
@@ -150,7 +458,8 @@ public class ComponentRenderer
try
{
componentMarkup = component.getMarkup().toString(true);
- } catch (MarkupNotFoundException mnfx)
+ }
+ catch (MarkupNotFoundException mnfx)
{
componentMarkup = String.format(DEFAULT_MARKUP, component.getId());
}
@@ -159,7 +468,8 @@ public class ComponentRenderer
}
@Override
- public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass)
+ public IResourceStream getMarkupResourceStream(MarkupContainer container,
+ Class<?> containerClass)
{
return new StringResourceStream(markup);
}
http://git-wip-us.apache.org/repos/asf/wicket/blob/a148e088/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..fe5aafb
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/core/util/string/componentrenderer/ComponentRendererInstanceTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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
[2/2] wicket git commit: dont search for ajax indicator twice
Posted by sv...@apache.org.
dont search for ajax indicator twice
ajaAttributes already contains the id; removed superfluous arguments from JavaScript
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/e7597f03
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/e7597f03
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/e7597f03
Branch: refs/heads/master
Commit: e7597f03f2c605badfa114011935cb79a51f2717
Parents: a148e08
Author: Sven Meier <sv...@apache.org>
Authored: Thu Dec 7 18:17:29 2017 +0100
Committer: Sven Meier <sv...@apache.org>
Committed: Thu Dec 7 20:41:57 2017 +0100
----------------------------------------------------------------------
.../AbstractAutoCompleteBehavior.java | 15 +---------
.../html/autocomplete/wicket-autocomplete.js | 30 ++++++++++----------
2 files changed, 16 insertions(+), 29 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/wicket/blob/e7597f03/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java
index 7164ee4..6e9db0f 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java
@@ -36,7 +36,6 @@ import org.apache.wicket.request.Response;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.util.string.Strings;
/**
* @since 1.2
@@ -156,20 +155,8 @@ public abstract class AbstractAutoCompleteBehavior extends AbstractDefaultAjaxBe
private void renderAutocompleteHead(final IHeaderResponse response)
{
response.render(JavaScriptHeaderItem.forReference(AUTOCOMPLETE_JS));
- final String id = getComponent().getMarkupId();
- String indicatorId = findIndicatorId();
- if (Strings.isEmpty(indicatorId))
- {
- indicatorId = "null";
- }
- else
- {
- indicatorId = "'" + indicatorId + "'";
- }
-
- String initJS = String.format("new Wicket.AutoComplete('%s', %s, %s, %s);", id,
- renderAjaxAttributes(getComponent(), getAttributes()), constructSettingsJS(), indicatorId);
+ String initJS = String.format("new Wicket.AutoComplete(%s, %s);", renderAjaxAttributes(getComponent()), constructSettingsJS());
final OnDomReadyHeaderItem onDomReady = OnDomReadyHeaderItem.forScript(initJS);
http://git-wip-us.apache.org/repos/asf/wicket/blob/e7597f03/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js
index 5fa8d2e..26f4b8d 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js
@@ -35,7 +35,7 @@
enterHidesWithNoSelection : false
};
- Wicket.AutoComplete=function(elementId, ajaxAttributes, cfg, indicatorId){
+ Wicket.AutoComplete=function(ajaxAttributes, cfg){
var KEY_TAB=9;
var KEY_ENTER=13;
var KEY_ESC=27;
@@ -85,7 +85,7 @@
choiceDiv.parentNode.parentNode.removeChild(choiceDiv.parentNode);
}
- var obj = Wicket.$(elementId);
+ var obj = Wicket.$(ajaxAttributes.c);
initialElement = obj;
Wicket.Event.add(obj, 'blur', function (jqEvent) {
@@ -120,7 +120,7 @@
setSelected(selected-1);
}
- var searchTerm = Wicket.$(elementId).value;
+ var searchTerm = Wicket.$(ajaxAttributes.c).value;
if(selected === -1 && searchTerm) {
// select the last element
setSelected(elementCount-1);
@@ -292,7 +292,7 @@
}
function getMenuId() {
- return elementId+"-autocomplete";
+ return ajaxAttributes.c+"-autocomplete";
}
function getAutocompleteMenu() {
@@ -345,7 +345,7 @@
}
function actualUpdateChoices() {
- prepareAndExecuteAjaxUpdate(doUpdateChoices, Wicket.$(elementId).value);
+ prepareAndExecuteAjaxUpdate(doUpdateChoices, Wicket.$(ajaxAttributes.c).value);
}
function prepareAndExecuteAjaxUpdate(successHandler, currentInput){
@@ -357,7 +357,7 @@
attrs.pre = attrs.pre || [];
attrs.pre.push(function (attributes) {
- var input = Wicket.$(elementId);
+ var input = Wicket.$(ajaxAttributes.c);
if (!input) {
// WICKET-6366 input might no longer be on page
return false;
@@ -385,17 +385,17 @@
}
function showIndicator() {
- Wicket.DOM.show(indicatorId);
+ Wicket.DOM.show(ajaxAttributes.i);
}
function hideIndicator() {
- Wicket.DOM.hide(indicatorId);
+ Wicket.DOM.hide(ajaxAttributes.i);
}
function showAutoComplete() {
- var input = Wicket.$(elementId);
+ var input = Wicket.$(ajaxAttributes.c);
var container = getAutocompleteContainer();
- var index=getOffsetParentZIndex(elementId);
+ var index=getOffsetParentZIndex(ajaxAttributes.c);
container.show();
if (!isNaN(Number(index))) {
container.style.zIndex=(Number(index)+1);
@@ -449,7 +449,7 @@
}
if (triggerChangeOnHide) {
- var input = Wicket.$(elementId);
+ var input = Wicket.$(ajaxAttributes.c);
jQuery(input).triggerHandler('change');
triggerChangeOnHide = false;
}
@@ -587,7 +587,7 @@
getAutocompleteMenu().showingAutocomplete = false;
// check if the input hasn't been cleared in the meanwhile or has been replaced by ajax
- var input=Wicket.$(elementId);
+ var input=Wicket.$(ajaxAttributes.c);
if ((input !== initialElement) || (document.activeElement !== input) || !cfg.showListOnEmptyInput && (input.value === null || input.value === "")) {
hideAutoComplete();
hideIndicator();
@@ -615,7 +615,7 @@
var value = getSelectedValue();
value = handleSelection(value);
- var input = Wicket.$(elementId);
+ var input = Wicket.$(ajaxAttributes.c);
if (value) {
input.value = value;
triggerChangeOnHide = true;
@@ -686,7 +686,7 @@
function scheduleEmptyCheck() {
window.setTimeout(function() {
- var input=Wicket.$(elementId);
+ var input=Wicket.$(ajaxAttributes.c);
// WICKET-6366 input might no longer be on page
if (input) {
@@ -782,7 +782,7 @@
sizeAffected = true;
}
if (sizeAffected) {
- calculateAndSetPopupBounds(Wicket.$(elementId), menu.parentNode);
+ calculateAndSetPopupBounds(Wicket.$(ajaxAttributes.c), menu.parentNode);
} // update stuff related to bounds if needed
}