Repository: wicket
Updated Branches:
  refs/heads/master 2f7d43fcc -> 6391f2cf9


WICKET-5929 moved partial updates; added test


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/6391f2cf
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/6391f2cf
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/6391f2cf

Branch: refs/heads/master
Commit: 6391f2cf9a49b39bd934b590f0a20e2871bc5f13
Parents: 2f7d43f
Author: Sven Meier <[email protected]>
Authored: Sat Jun 27 11:45:11 2015 +0200
Committer: Sven Meier <[email protected]>
Committed: Sat Jun 27 11:45:34 2015 +0200

----------------------------------------------------------------------
 .../apache/wicket/ajax/AjaxRequestHandler.java  |   2 +
 .../apache/wicket/ajax/PartialPageUpdate.java   | 787 -------------------
 .../wicket/ajax/XmlPartialPageUpdate.java       | 222 ------
 .../apache/wicket/page/PartialPageUpdate.java   | 784 ++++++++++++++++++
 .../wicket/page/XmlPartialPageUpdate.java       | 222 ++++++
 .../filter/XmlCleaningResponseFilter.java       |   2 +-
 .../wicket/page/PageForPartialUpdate.html       |  19 +
 .../wicket/page/PageForPartialUpdate.java       |  40 +
 .../wicket/page/XmlPartialPageUpdateTest.java   |  39 +
 .../filter/XmlCleaningResponseFilterTest.java   |   5 +-
 .../ws/api/WebSocketRequestHandler.java         |   4 +-
 11 files changed, 1112 insertions(+), 1014 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java 
b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
index f7ab416..0251113 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java
@@ -33,6 +33,8 @@ import 
org.apache.wicket.core.request.handler.RenderPageRequestHandler;
 import org.apache.wicket.core.request.handler.logger.PageLogData;
 import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.page.PartialPageUpdate;
+import org.apache.wicket.page.XmlPartialPageUpdate;
 import org.apache.wicket.request.IRequestCycle;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.Response;

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/ajax/PartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/ajax/PartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/ajax/PartialPageUpdate.java
deleted file mode 100644
index 35ea016..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/PartialPageUpdate.java
+++ /dev/null
@@ -1,787 +0,0 @@
-/*
- * 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.ajax;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.Cookie;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.Page;
-import org.apache.wicket.markup.head.HeaderItem;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.IWrappedHeaderItem;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
-import org.apache.wicket.markup.head.OnEventHeaderItem;
-import org.apache.wicket.markup.head.OnLoadHeaderItem;
-import org.apache.wicket.markup.head.PriorityHeaderItem;
-import org.apache.wicket.markup.head.internal.HeaderResponse;
-import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
-import org.apache.wicket.markup.parser.filter.HtmlHeaderSectionHandler;
-import org.apache.wicket.markup.renderStrategy.AbstractHeaderRenderStrategy;
-import org.apache.wicket.markup.renderStrategy.IHeaderRenderStrategy;
-import org.apache.wicket.markup.repeater.AbstractRepeater;
-import org.apache.wicket.request.IRequestCycle;
-import org.apache.wicket.request.Response;
-import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.http.WebResponse;
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Classes;
-import org.apache.wicket.util.lang.Generics;
-import org.apache.wicket.util.string.AppendingStringBuffer;
-import org.apache.wicket.util.time.Time;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A partial update of a page that collects components and header 
contributions to be written to the client in a specific
- * String-based format (XML, JSON, * ...).
- * <p>
- * The elements of such response are:
- * <ul>
- * <li>priority-evaluate - an item of the prepend JavaScripts</li>
- * <li>component - the markup of the updated component</li>
- * <li>evaluate - an item of the onDomReady and append JavaScripts</li>
- * <li>header-contribution - all HeaderItems which have been contributed in
- * components' and their behaviors' #renderHead(Component, 
IHeaderResponse)</li>
- * </ul>
- */
-public abstract class PartialPageUpdate
-{
-       private static final Logger LOG = 
LoggerFactory.getLogger(PartialPageUpdate.class);
-
-       /**
-        * A list of scripts (JavaScript) which should be executed on the 
client side before the
-        * components' replacement
-        */
-       protected final List<CharSequence> prependJavaScripts = 
Generics.newArrayList();
-
-       /**
-        * A list of scripts (JavaScript) which should be executed on the 
client side after the
-        * components' replacement
-        */
-       protected final List<CharSequence> appendJavaScripts = 
Generics.newArrayList();
-
-       /**
-        * A list of scripts (JavaScript) which should be executed on the 
client side after the
-        * components' replacement.
-        * Executed immediately after the replacement of the components, and 
before appendJavaScripts
-        */
-       protected final List<CharSequence> domReadyJavaScripts = 
Generics.newArrayList();
-
-       /**
-        * The component instances that will be rendered/replaced.
-        */
-       protected final Map<String, Component> markupIdToComponent = new 
LinkedHashMap<String, Component>();
-
-       /**
-        * A flag that indicates that components cannot be added anymore.
-        * See https://issues.apache.org/jira/browse/WICKET-3564
-        * 
-        * @see #add(Component, String)
-        */
-       protected transient boolean componentsFrozen;
-
-       /**
-        * Create a response for component body and javascript that will escape 
output to make it safe
-        * to use inside a CDATA block
-        */
-       protected final ResponseWrapper encodingBodyResponse;
-
-       /**
-        * Response for header contribution that will escape output to make it 
safe to use inside a
-        * CDATA block
-        */
-       protected final ResponseWrapper encodingHeaderResponse;
-
-       protected HtmlHeaderContainer header = null;
-
-       // whether a header contribution is being rendered
-       private boolean headerRendering = false;
-
-       private IHeaderResponse headerResponse;
-
-       /**
-        * The page which components are being updated.
-        */
-       private final Page page;
-
-       /**
-        * Constructor.
-        *
-        * @param page
-        *      the page which components are being updated.
-        */
-       public PartialPageUpdate(final Page page)
-       {
-               this.page = page;
-
-               WebResponse response = (WebResponse) page.getResponse();
-               encodingBodyResponse = new ResponseWrapper(response);
-               encodingHeaderResponse = new ResponseWrapper(response);
-       }
-
-       /**
-        * Serializes this object to the response.
-        *
-        * @param response
-        *      the response to write to
-        * @param encoding
-        *      the encoding for the response
-        */
-       public void writeTo(final Response response, final String encoding)
-       {
-               writeHeader(response, encoding);
-
-               onBeforeRespond(response);
-
-               // process added components
-               writeComponents(response, encoding);
-
-               onAfterRespond(response);
-
-               // queue up prepend javascripts. unlike other steps these are 
executed out of order so that
-               // components can contribute them from inside their 
onbeforerender methods.
-               writePriorityEvaluations(response, prependJavaScripts);
-
-               // execute the dom ready javascripts as first javascripts
-               // after component replacement
-               List<CharSequence> evaluationScripts = new ArrayList<>();
-               evaluationScripts.addAll(domReadyJavaScripts);
-               evaluationScripts.addAll(appendJavaScripts);
-               writeNormalEvaluations(response, evaluationScripts);
-
-               writeFooter(response, encoding);
-       }
-
-       protected void onBeforeRespond(Response response) {
-       }
-
-       protected void onAfterRespond(Response response) {
-       }
-
-       /**
-        * @param response
-        *      the response to write to
-        * @param encoding
-        *      the encoding for the response
-        */
-    protected abstract void writeFooter(Response response, String encoding);
-
-       /**
-        *
-        * @param response
-        *      the response to write to
-        * @param js
-        *      the JavaScript to evaluate
-        */
-       protected abstract void writePriorityEvaluations(Response response, 
Collection<CharSequence> js);
-
-       /**
-        *
-        * @param response
-        *      the response to write to
-        * @param js
-        *      the JavaScript to evaluate
-        */
-       protected abstract void writeNormalEvaluations(Response response, 
Collection<CharSequence> js);
-
-       /**
-        * Processes components added to the target. This involves attaching 
components, rendering
-        * markup into a client side xml envelope, and detaching them
-        *
-        * @param response
-        *      the response to write to
-        * @param encoding
-        *      the encoding for the response
-        */
-       private void writeComponents(Response response, String encoding)
-       {
-               componentsFrozen = true;
-
-               // process component markup
-               for (Map.Entry<String, Component> stringComponentEntry : 
markupIdToComponent.entrySet())
-               {
-                       final Component component = 
stringComponentEntry.getValue();
-
-                       if (!containsAncestorFor(component))
-                       {
-                               writeComponent(response, 
component.getAjaxRegionMarkupId(), component, encoding);
-                       }
-               }
-
-               if (header != null)
-               {
-                       // some header responses buffer all calls to render*** 
until close is called.
-                       // when they are closed, they do something (i.e. 
aggregate all JS resource urls to a
-                       // single url), and then "flush" (by writing to the 
real response) before closing.
-                       // to support this, we need to allow header 
contributions to be written in the close
-                       // tag, which we do here:
-                       headerRendering = true;
-                       // save old response, set new
-                       Response oldResponse = 
RequestCycle.get().setResponse(encodingHeaderResponse);
-                       encodingHeaderResponse.reset();
-
-                       // now, close the response (which may render things)
-                       header.getHeaderResponse().close();
-
-                       // revert to old response
-                       RequestCycle.get().setResponse(oldResponse);
-
-                       // write the XML tags and we're done
-                       writeHeaderContribution(response);
-                       headerRendering = false;
-               }
-       }
-
-       /**
-        * Writes a single component
-        *
-        * @param response
-        *      the response to write to
-        * @param markupId
-        *      the markup id to use for the component replacement
-        * @param component
-        *      the component which markup will be used as replacement
-        * @param encoding
-        *      the encoding for the response
-        */
-       protected abstract void writeComponent(Response response, String 
markupId, Component component, String encoding);
-
-       /**
-        * Writes the head part of the response.
-        * For example XML preamble
-        *
-        * @param response
-        *      the response to write to
-        * @param encoding
-        *      the encoding for the response
-        */
-       protected abstract void writeHeader(Response response, String encoding);
-
-       /**
-        * Writes header contribution (<link/> or <script/>) to the response.
-        *
-        * @param response
-        *      the response to write to
-        */
-       protected abstract void writeHeaderContribution(Response response);
-
-       @Override
-       public boolean equals(Object o)
-       {
-               if (this == o) return true;
-               if (o == null || getClass() != o.getClass()) return false;
-
-               PartialPageUpdate that = (PartialPageUpdate) o;
-
-               if (!appendJavaScripts.equals(that.appendJavaScripts)) return 
false;
-               if (!domReadyJavaScripts.equals(that.domReadyJavaScripts)) 
return false;
-               return prependJavaScripts.equals(that.prependJavaScripts);
-       }
-
-       @Override
-       public int hashCode()
-       {
-               int result = prependJavaScripts.hashCode();
-               result = 31 * result + appendJavaScripts.hashCode();
-               result = 31 * result + domReadyJavaScripts.hashCode();
-               return result;
-       }
-
-       /**
-        * Adds script to the ones which are executed after the component 
replacement.
-        *
-        * @param javascript
-        *      the javascript to execute
-        */
-       public final void appendJavaScript(final CharSequence javascript)
-       {
-               Args.notNull(javascript, "javascript");
-
-               appendJavaScripts.add(javascript);
-       }
-
-       /**
-        * Adds script to the ones which are executed before the component 
replacement.
-        *
-        * @param javascript
-        *      the javascript to execute
-        */
-       public final void prependJavaScript(CharSequence javascript)
-       {
-               Args.notNull(javascript, "javascript");
-
-               prependJavaScripts.add(javascript);
-       }
-
-       /**
-        * Adds a component to be updated at the client side with its current 
markup
-        *
-        * @param component
-        *      the component to update
-        * @param markupId
-        *      the markup id to use to find the component in the page's markup
-        * @throws IllegalArgumentException
-        *      thrown when a Page or an AbstractRepeater is added
-        * @throws IllegalStateException
-        *      thrown when components no more can be added for replacement.
-        */
-       public final void add(final Component component, final String markupId)
-                       throws IllegalArgumentException, IllegalStateException
-       {
-               Args.notEmpty(markupId, "markupId");
-               Args.notNull(component, "component");
-
-               if (component instanceof Page)
-               {
-                       if (component != page)
-                       {
-                               throw new IllegalArgumentException("component 
cannot be a page");
-                       }
-               }
-               else if (component instanceof AbstractRepeater)
-               {
-                       throw new IllegalArgumentException(
-                                       "Component " +
-                                                       
component.getClass().getName() +
-                                                       " has been added to a 
partial page update. This component is a repeater and cannot be repainted 
directly. " +
-                                                       "Instead add its parent 
or another markup container higher in the hierarchy.");
-               }
-
-               assertComponentsNotFrozen();
-
-               component.setMarkupId(markupId);
-               markupIdToComponent.put(markupId, component);
-       }
-
-       /**
-        * @return a read-only collection of all components which have been 
added for replacement so far.
-        */
-       public final Collection<? extends Component> getComponents()
-       {
-               return 
Collections.unmodifiableCollection(markupIdToComponent.values());
-       }
-
-       /**
-        * Detaches the page if at least one of its components was updated.
-        *
-        * @param requestCycle
-        *      the current request cycle
-        */
-       public void detach(IRequestCycle requestCycle)
-       {
-               Iterator<Component> iterator = 
markupIdToComponent.values().iterator();
-               while (iterator.hasNext())
-               {
-                       final Component component = iterator.next();
-                       final Page parentPage = 
component.findParent(Page.class);
-                       if (parentPage != null)
-                       {
-                               parentPage.detach();
-                               break;
-                       }
-               }
-       }
-
-       /**
-        * Checks if the target contains an ancestor for the given component
-        *
-        * @param component
-        *      the component which ancestors should be checked.
-        * @return <code>true</code> if target contains an ancestor for the 
given component
-        */
-       protected boolean containsAncestorFor(Component component)
-       {
-               Component cursor = component.getParent();
-               while (cursor != null)
-               {
-                       if (markupIdToComponent.containsValue(cursor))
-                       {
-                               return true;
-                       }
-                       cursor = cursor.getParent();
-               }
-               return false;
-       }
-
-       /**
-        * @return {@code true} if the page has been added for replacement
-        */
-       boolean containsPage()
-       {
-               return markupIdToComponent.values().contains(page);
-       }
-
-       /**
-        * Gets or creates an IHeaderResponse instance to use for the header 
contributions.
-        *
-        * @return IHeaderResponse instance to use for the header contributions.
-        */
-       public IHeaderResponse getHeaderResponse()
-       {
-               if (headerResponse == null)
-               {
-                       // we don't need to decorate the header response here 
because this is called from
-                       // within PartialHtmlHeaderContainer, which decorates 
the response
-                       headerResponse = new PartialHeaderResponse();
-               }
-               return headerResponse;
-       }
-
-       /**
-        * @param response
-        *      the response to write to
-        * @param component
-        *      to component which will contribute to the header
-        */
-       protected void writeHeaderContribution(final Response response, final 
Component component)
-       {
-               headerRendering = true;
-
-               // create the htmlheadercontainer if needed
-               if (header == null)
-               {
-                       header = new PartialHtmlHeaderContainer(this);
-                       final Page parentPage = component.getPage();
-                       parentPage.addOrReplace(header);
-               }
-
-               RequestCycle requestCycle = component.getRequestCycle();
-
-               // save old response, set new
-               Response oldResponse = 
requestCycle.setResponse(encodingHeaderResponse);
-
-               try {
-                       encodingHeaderResponse.reset();
-
-                       IHeaderRenderStrategy strategy = 
AbstractHeaderRenderStrategy.get();
-
-                       strategy.renderHeader(header, null, component);
-               } finally {
-                       // revert to old response
-                       requestCycle.setResponse(oldResponse);
-               }
-
-               writeHeaderContribution(response);
-
-               headerRendering = false;
-       }
-
-       /**
-        * Sets the Content-Type header to indicate the type of the response.
-        *
-        * @param response
-        *      the current we response
-        * @param encoding
-        *      the encoding to use
-        */
-       protected abstract void setContentType(WebResponse response, String 
encoding);
-
-
-       /**
-        * Header container component partial page updates.
-        *
-        * @author Matej Knopp
-        */
-       private static class PartialHtmlHeaderContainer extends 
HtmlHeaderContainer
-       {
-               private static final long serialVersionUID = 1L;
-
-               private transient PartialPageUpdate update;
-
-               /**
-                * Constructor.
-                *
-                * @param update
-                *      the partial page update
-                */
-               public PartialHtmlHeaderContainer(final PartialPageUpdate 
update)
-               {
-                       super(HtmlHeaderSectionHandler.HEADER_ID);
-                       this.update = update;
-               }
-
-               /**
-                *
-                * @see 
org.apache.wicket.markup.html.internal.HtmlHeaderContainer#newHeaderResponse()
-                */
-               @Override
-               protected IHeaderResponse newHeaderResponse()
-               {
-                   if (update != null)
-            {
-                       return update.getHeaderResponse();
-            }
-                   
-                   return super.newHeaderResponse();
-               }
-               
-               @Override
-               protected void onDetach()
-               {
-                   super.onDetach();
-                   update = null;
-               }
-       }
-
-       /**
-        * Header response for partial updates.
-        *
-        * @author Matej Knopp
-        */
-       private class PartialHeaderResponse extends HeaderResponse
-       {
-               @Override
-               public void render(HeaderItem item)
-               {
-                       PriorityHeaderItem priorityHeaderItem = null;
-                       while (item instanceof IWrappedHeaderItem)
-                       {
-                               if (item instanceof PriorityHeaderItem)
-                               {
-                                       priorityHeaderItem = 
(PriorityHeaderItem) item;
-                               }
-                               item = ((IWrappedHeaderItem) item).getWrapped();
-                       }
-
-                       if (item instanceof OnLoadHeaderItem)
-                       {
-                               if (!wasItemRendered(item))
-                               {
-                                       
PartialPageUpdate.this.appendJavaScript(((OnLoadHeaderItem) 
item).getJavaScript());
-                                       markItemRendered(item);
-                               }
-                       }
-                       else if (item instanceof OnEventHeaderItem)
-                       {
-                               if (!wasItemRendered(item))
-                               {
-                                       
PartialPageUpdate.this.appendJavaScript(((OnEventHeaderItem) 
item).getCompleteJavaScript());
-                                       markItemRendered(item);
-                               }
-                       }
-                       else if (item instanceof OnDomReadyHeaderItem)
-                       {
-                               if (!wasItemRendered(item))
-                               {
-                                       if (priorityHeaderItem != null)
-                                       {
-                                               
PartialPageUpdate.this.domReadyJavaScripts.add(0, 
((OnDomReadyHeaderItem)item).getJavaScript());
-                                       }
-                                       else
-                                       {
-                                               
PartialPageUpdate.this.domReadyJavaScripts.add(((OnDomReadyHeaderItem)item).getJavaScript());
-                                       }
-                                       markItemRendered(item);
-                               }
-                       }
-                       else if (headerRendering)
-                       {
-                               super.render(item);
-                       }
-                       else
-                       {
-                               LOG.debug("Only methods that can be called on 
IHeaderResponse outside renderHead() are #render(OnLoadHeaderItem) and 
#render(OnDomReadyHeaderItem)");
-                       }
-               }
-
-               @Override
-               protected Response getRealResponse()
-               {
-                       return RequestCycle.get().getResponse();
-               }
-       }
-
-       /**
-        * Wrapper of a response.
-        *
-        * @author Igor Vaynberg (ivaynberg)
-        * @author Sven Meier (svenmeier)
-        * 
-        * @see ResponseWrapper#getContents()
-        * @see ResponseWrapper#reset()
-        */
-       protected static final class ResponseWrapper extends WebResponse
-       {
-               private final AppendingStringBuffer buffer = new 
AppendingStringBuffer(256);
-
-               private final WebResponse originalResponse;
-
-               /**
-                * Constructor.
-                *
-                * @param originalResponse
-                *      the original request cycle response
-                */
-               private ResponseWrapper(WebResponse originalResponse)
-               {
-                       this.originalResponse = originalResponse;
-               }
-
-               /**
-                * @see 
org.apache.wicket.request.Response#encodeURL(CharSequence)
-                */
-               @Override
-               public String encodeURL(CharSequence url)
-               {
-                       return originalResponse.encodeURL(url);
-               }
-
-               /**
-                * @return contents of the response
-                */
-               public CharSequence getContents()
-               {
-                       return buffer;
-               }
-
-               /**
-                * @see org.apache.wicket.request.Response#write(CharSequence)
-                */
-               @Override
-               public void write(CharSequence cs)
-               {
-                       buffer.append(cs);
-               }
-
-               /**
-                * Resets the response to a clean state so it can be reused to 
save on garbage.
-                */
-               @Override
-               public void reset()
-               {
-                       buffer.clear();
-               }
-
-               @Override
-               public void write(byte[] array)
-               {
-                       throw new UnsupportedOperationException("Cannot write 
binary data.");
-               }
-
-               @Override
-               public void write(byte[] array, int offset, int length)
-               {
-                       throw new UnsupportedOperationException("Cannot write 
binary data.");
-               }
-
-               @Override
-               public Object getContainerResponse()
-               {
-                       return originalResponse.getContainerResponse();
-               }
-
-               @Override
-               public void addCookie(Cookie cookie)
-               {
-                       originalResponse.addCookie(cookie);
-               }
-
-               @Override
-               public void clearCookie(Cookie cookie)
-               {
-                       originalResponse.clearCookie(cookie);
-               }
-
-               @Override
-               public void setHeader(String name, String value)
-               {
-                       originalResponse.setHeader(name, value);
-               }
-
-               @Override
-               public void addHeader(String name, String value)
-               {
-                       originalResponse.addHeader(name, value);
-               }
-
-               @Override
-               public void setDateHeader(String name, Time date)
-               {
-                       originalResponse.setDateHeader(name, date);
-               }
-
-               @Override
-               public void setContentLength(long length)
-               {
-                       originalResponse.setContentLength(length);
-               }
-
-               @Override
-               public void setContentType(String mimeType)
-               {
-                       originalResponse.setContentType(mimeType);
-               }
-
-               @Override
-               public void setStatus(int sc)
-               {
-                       originalResponse.setStatus(sc);
-               }
-
-               @Override
-               public void sendError(int sc, String msg)
-               {
-                       originalResponse.sendError(sc, msg);
-               }
-
-               @Override
-               public String encodeRedirectURL(CharSequence url)
-               {
-                       return originalResponse.encodeRedirectURL(url);
-               }
-
-               @Override
-               public void sendRedirect(String url)
-               {
-                       originalResponse.sendRedirect(url);
-               }
-
-               @Override
-               public boolean isRedirect()
-               {
-                       return originalResponse.isRedirect();
-               }
-
-               @Override
-               public void flush()
-               {
-                       originalResponse.flush();
-               }
-       }
-
-       private void assertComponentsNotFrozen()
-       {
-               assertNotFrozen(componentsFrozen, Component.class);
-       }
-
-       private void assertNotFrozen(boolean frozen, Class<?> clazz)
-       {
-               if (frozen)
-               {
-                       throw new 
IllegalStateException(Classes.simpleName(clazz) + "s can no " +
-                                       " longer be added");
-               }
-       }
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/ajax/XmlPartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/ajax/XmlPartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/ajax/XmlPartialPageUpdate.java
deleted file mode 100644
index 2fb6bd3..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/XmlPartialPageUpdate.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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.ajax;
-
-import java.util.Collection;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.Page;
-import org.apache.wicket.request.Response;
-import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.http.WebResponse;
-import org.apache.wicket.util.string.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A {@link PartialPageUpdate} that serializes itself to XML.
- */
-public class XmlPartialPageUpdate extends PartialPageUpdate
-{
-       private static final Logger LOG = 
LoggerFactory.getLogger(XmlPartialPageUpdate.class);
-
-       /**
-        * The name of the root element in the produced XML document.
-        */
-       public static final String START_ROOT_ELEMENT = "<ajax-response>";
-       public static final String END_ROOT_ELEMENT = "</ajax-response>";
-
-       public XmlPartialPageUpdate(final Page page)
-       {
-               super(page);
-       }
-
-       @Override
-       public void setContentType(WebResponse response, String encoding)
-       {
-               response.setContentType("text/xml; charset=" + encoding);
-       }
-
-       @Override
-       protected void writeHeader(Response response, String encoding)
-       {
-               response.write("<?xml version=\"1.0\" encoding=\"");
-               response.write(encoding);
-               response.write("\"?>");
-               response.write(START_ROOT_ELEMENT);
-       }
-
-       @Override
-       protected void writeComponent(Response response, String markupId, 
Component component, String encoding)
-       {
-               if (component.getRenderBodyOnly() == true)
-               {
-                       throw new IllegalStateException(
-                                       "A partial update is not possible for a 
component that has renderBodyOnly enabled. Component: " +
-                                                       component.toString());
-               }
-
-               component.setOutputMarkupId(true);
-
-               // Initialize temporary variables
-               final Page page = component.findParent(Page.class);
-               if (page == null)
-               {
-                       // dont throw an exception but just ignore this 
component, somehow
-                       // it got removed from the page.
-                       LOG.warn("Component '{}' with markupid: '{}' not 
rendered because it was already removed from page",
-                                       component, markupId);
-                       return;
-               }
-
-               // substitute our encoding response for the old one so we can 
capture
-               // component's markup in a manner safe for transport inside 
CDATA block
-               Response oldResponse = 
RequestCycle.get().setResponse(encodingBodyResponse);
-
-               try
-               {
-                       encodingBodyResponse.reset();
-                       
-                       page.startComponentRender(component);
-
-                       try
-                       {
-                               component.prepareForRender();
-
-                               // render any associated headers of the 
component
-                               writeHeaderContribution(response, component);
-                       }
-                       catch (RuntimeException e)
-                       {
-                               try
-                               {
-                                       component.afterRender();
-                               }
-                               catch (RuntimeException e2)
-                               {
-                                       // ignore this one could be a result 
off.
-                               }
-                               encodingBodyResponse.reset();
-                               throw e;
-                       }
-
-                       try
-                       {
-                               component.render();
-                       }
-                       catch (RuntimeException e)
-                       {
-                               encodingBodyResponse.reset();
-                               throw e;
-                       }
-
-                       page.endComponentRender(component);
-               }
-               finally
-               {
-                       // Restore original response
-                       RequestCycle.get().setResponse(oldResponse);
-               }
-
-               response.write("<component id=\"");
-               response.write(markupId);
-               response.write("\" ><![CDATA[");
-               response.write(encode(encodingBodyResponse.getContents()));
-               response.write("]]></component>");
-
-               encodingBodyResponse.reset();
-       }
-
-       @Override
-       protected void writeFooter(Response response, String encoding)
-       {
-               response.write(END_ROOT_ELEMENT);
-       }
-
-       @Override
-       protected void writeHeaderContribution(Response response)
-       {
-               if (encodingHeaderResponse.getContents().length() != 0)
-               {
-                       response.write("<header-contribution>");
-
-                       // we need to write response as CDATA and parse it on 
client,
-                       // because konqueror crashes when there is a <script> 
element
-                       response.write("<![CDATA[<head 
xmlns:wicket=\"http://wicket.apache.org\";>");
-                       
response.write(encode(encodingHeaderResponse.getContents()));
-                       response.write("</head>]]>");
-                       response.write("</header-contribution>");
-               }
-       }
-
-       @Override
-       protected void writeNormalEvaluations(final Response response, final 
Collection<CharSequence> scripts)
-       {
-               writeEvaluations(response, "evaluate", scripts);
-
-       }
-
-       @Override
-       protected void writePriorityEvaluations(Response response, 
Collection<CharSequence> scripts)
-       {
-               writeEvaluations(response, "priority-evaluate", scripts);
-       }
-
-       private void writeEvaluations(final Response response, String 
elementName, Collection<CharSequence> scripts)
-       {
-               if (scripts.size() > 0)
-               {
-                       StringBuilder combinedScript = new StringBuilder(1024);
-                       for (CharSequence script : scripts)
-                       {
-                               
combinedScript.append("(function(){").append(script).append("})();");
-                       }
-                       writeEvaluation(elementName, response, combinedScript);
-               }
-       }
-
-       /**
-       * @param invocation
-       *            type of invocation tag, usually {@literal evaluate} or
-       *            {@literal priority-evaluate}
-       * @param response
-       * @param js
-       */
-       private void writeEvaluation(final String invocation, final Response 
response, final CharSequence js)
-       {
-               response.write("<");
-               response.write(invocation);
-               response.write(">");
-
-               response.write("<![CDATA[");
-               response.write(encode(js));
-               response.write("]]>");
-
-               response.write("</");
-               response.write(invocation);
-               response.write(">");
-
-               encodingBodyResponse.reset();
-       }
-
-       protected CharSequence encode(CharSequence str)
-       {
-               return Strings.replaceAll(str, "]]>", "]]]]><![CDATA[>"); 
-       }
-
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
new file mode 100644
index 0000000..eccc78e
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
@@ -0,0 +1,784 @@
+/*
+ * 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.page;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.Cookie;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.markup.head.HeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.IWrappedHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.head.OnEventHeaderItem;
+import org.apache.wicket.markup.head.OnLoadHeaderItem;
+import org.apache.wicket.markup.head.PriorityHeaderItem;
+import org.apache.wicket.markup.head.internal.HeaderResponse;
+import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
+import org.apache.wicket.markup.parser.filter.HtmlHeaderSectionHandler;
+import org.apache.wicket.markup.renderStrategy.AbstractHeaderRenderStrategy;
+import org.apache.wicket.markup.renderStrategy.IHeaderRenderStrategy;
+import org.apache.wicket.markup.repeater.AbstractRepeater;
+import org.apache.wicket.request.IRequestCycle;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Classes;
+import org.apache.wicket.util.lang.Generics;
+import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.apache.wicket.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A partial update of a page that collects components and header 
contributions to be written to the client in a specific
+ * String-based format (XML, JSON, * ...).
+ * <p>
+ * The elements of such response are:
+ * <ul>
+ * <li>priority-evaluate - an item of the prepend JavaScripts</li>
+ * <li>component - the markup of the updated component</li>
+ * <li>evaluate - an item of the onDomReady and append JavaScripts</li>
+ * <li>header-contribution - all HeaderItems which have been contributed in
+ * components' and their behaviors' #renderHead(Component, 
IHeaderResponse)</li>
+ * </ul>
+ */
+public abstract class PartialPageUpdate
+{
+       private static final Logger LOG = 
LoggerFactory.getLogger(PartialPageUpdate.class);
+
+       /**
+        * A list of scripts (JavaScript) which should be executed on the 
client side before the
+        * components' replacement
+        */
+       protected final List<CharSequence> prependJavaScripts = 
Generics.newArrayList();
+
+       /**
+        * A list of scripts (JavaScript) which should be executed on the 
client side after the
+        * components' replacement
+        */
+       protected final List<CharSequence> appendJavaScripts = 
Generics.newArrayList();
+
+       /**
+        * A list of scripts (JavaScript) which should be executed on the 
client side after the
+        * components' replacement.
+        * Executed immediately after the replacement of the components, and 
before appendJavaScripts
+        */
+       protected final List<CharSequence> domReadyJavaScripts = 
Generics.newArrayList();
+
+       /**
+        * The component instances that will be rendered/replaced.
+        */
+       protected final Map<String, Component> markupIdToComponent = new 
LinkedHashMap<String, Component>();
+
+       /**
+        * A flag that indicates that components cannot be added anymore.
+        * See https://issues.apache.org/jira/browse/WICKET-3564
+        * 
+        * @see #add(Component, String)
+        */
+       protected transient boolean componentsFrozen;
+
+       /**
+        * Buffer of response body. 
+        */
+       protected final ResponseBuffer bodyBuffer;
+
+       /**
+        * Buffer of response header.
+        */
+       protected final ResponseBuffer headerBuffer;
+
+       protected HtmlHeaderContainer header = null;
+
+       // whether a header contribution is being rendered
+       private boolean headerRendering = false;
+
+       private IHeaderResponse headerResponse;
+
+       /**
+        * The page which components are being updated.
+        */
+       private final Page page;
+
+       /**
+        * Constructor.
+        *
+        * @param page
+        *      the page which components are being updated.
+        */
+       public PartialPageUpdate(final Page page)
+       {
+               this.page = page;
+
+               WebResponse response = (WebResponse) page.getResponse();
+               bodyBuffer = new ResponseBuffer(response);
+               headerBuffer = new ResponseBuffer(response);
+       }
+
+       /**
+        * Serializes this object to the response.
+        *
+        * @param response
+        *      the response to write to
+        * @param encoding
+        *      the encoding for the response
+        */
+       public void writeTo(final Response response, final String encoding)
+       {
+               writeHeader(response, encoding);
+
+               onBeforeRespond(response);
+
+               // process added components
+               writeComponents(response, encoding);
+
+               onAfterRespond(response);
+
+               // queue up prepend javascripts. unlike other steps these are 
executed out of order so that
+               // components can contribute them from inside their 
onbeforerender methods.
+               writePriorityEvaluations(response, prependJavaScripts);
+
+               // execute the dom ready javascripts as first javascripts
+               // after component replacement
+               List<CharSequence> evaluationScripts = new ArrayList<>();
+               evaluationScripts.addAll(domReadyJavaScripts);
+               evaluationScripts.addAll(appendJavaScripts);
+               writeNormalEvaluations(response, evaluationScripts);
+
+               writeFooter(response, encoding);
+       }
+
+       protected void onBeforeRespond(Response response) {
+       }
+
+       protected void onAfterRespond(Response response) {
+       }
+
+       /**
+        * @param response
+        *      the response to write to
+        * @param encoding
+        *      the encoding for the response
+        */
+    protected abstract void writeFooter(Response response, String encoding);
+
+       /**
+        *
+        * @param response
+        *      the response to write to
+        * @param js
+        *      the JavaScript to evaluate
+        */
+       protected abstract void writePriorityEvaluations(Response response, 
Collection<CharSequence> js);
+
+       /**
+        *
+        * @param response
+        *      the response to write to
+        * @param js
+        *      the JavaScript to evaluate
+        */
+       protected abstract void writeNormalEvaluations(Response response, 
Collection<CharSequence> js);
+
+       /**
+        * Processes components added to the target. This involves attaching 
components, rendering
+        * markup into a client side xml envelope, and detaching them
+        *
+        * @param response
+        *      the response to write to
+        * @param encoding
+        *      the encoding for the response
+        */
+       private void writeComponents(Response response, String encoding)
+       {
+               componentsFrozen = true;
+
+               // process component markup
+               for (Map.Entry<String, Component> stringComponentEntry : 
markupIdToComponent.entrySet())
+               {
+                       final Component component = 
stringComponentEntry.getValue();
+
+                       if (!containsAncestorFor(component))
+                       {
+                               writeComponent(response, 
component.getAjaxRegionMarkupId(), component, encoding);
+                       }
+               }
+
+               if (header != null)
+               {
+                       // some header responses buffer all calls to render*** 
until close is called.
+                       // when they are closed, they do something (i.e. 
aggregate all JS resource urls to a
+                       // single url), and then "flush" (by writing to the 
real response) before closing.
+                       // to support this, we need to allow header 
contributions to be written in the close
+                       // tag, which we do here:
+                       headerRendering = true;
+                       // save old response, set new
+                       Response oldResponse = 
RequestCycle.get().setResponse(headerBuffer);
+                       headerBuffer.reset();
+
+                       // now, close the response (which may render things)
+                       header.getHeaderResponse().close();
+
+                       // revert to old response
+                       RequestCycle.get().setResponse(oldResponse);
+
+                       // write the XML tags and we're done
+                       writeHeaderContribution(response);
+                       headerRendering = false;
+               }
+       }
+
+       /**
+        * Writes a single component
+        *
+        * @param response
+        *      the response to write to
+        * @param markupId
+        *      the markup id to use for the component replacement
+        * @param component
+        *      the component which markup will be used as replacement
+        * @param encoding
+        *      the encoding for the response
+        */
+       protected abstract void writeComponent(Response response, String 
markupId, Component component, String encoding);
+
+       /**
+        * Writes the head part of the response.
+        * For example XML preamble
+        *
+        * @param response
+        *      the response to write to
+        * @param encoding
+        *      the encoding for the response
+        */
+       protected abstract void writeHeader(Response response, String encoding);
+
+       /**
+        * Writes header contribution (<link/> or <script/>) to the response.
+        *
+        * @param response
+        *      the response to write to
+        */
+       protected abstract void writeHeaderContribution(Response response);
+
+       @Override
+       public boolean equals(Object o)
+       {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+
+               PartialPageUpdate that = (PartialPageUpdate) o;
+
+               if (!appendJavaScripts.equals(that.appendJavaScripts)) return 
false;
+               if (!domReadyJavaScripts.equals(that.domReadyJavaScripts)) 
return false;
+               return prependJavaScripts.equals(that.prependJavaScripts);
+       }
+
+       @Override
+       public int hashCode()
+       {
+               int result = prependJavaScripts.hashCode();
+               result = 31 * result + appendJavaScripts.hashCode();
+               result = 31 * result + domReadyJavaScripts.hashCode();
+               return result;
+       }
+
+       /**
+        * Adds script to the ones which are executed after the component 
replacement.
+        *
+        * @param javascript
+        *      the javascript to execute
+        */
+       public final void appendJavaScript(final CharSequence javascript)
+       {
+               Args.notNull(javascript, "javascript");
+
+               appendJavaScripts.add(javascript);
+       }
+
+       /**
+        * Adds script to the ones which are executed before the component 
replacement.
+        *
+        * @param javascript
+        *      the javascript to execute
+        */
+       public final void prependJavaScript(CharSequence javascript)
+       {
+               Args.notNull(javascript, "javascript");
+
+               prependJavaScripts.add(javascript);
+       }
+
+       /**
+        * Adds a component to be updated at the client side with its current 
markup
+        *
+        * @param component
+        *      the component to update
+        * @param markupId
+        *      the markup id to use to find the component in the page's markup
+        * @throws IllegalArgumentException
+        *      thrown when a Page or an AbstractRepeater is added
+        * @throws IllegalStateException
+        *      thrown when components no more can be added for replacement.
+        */
+       public final void add(final Component component, final String markupId)
+                       throws IllegalArgumentException, IllegalStateException
+       {
+               Args.notEmpty(markupId, "markupId");
+               Args.notNull(component, "component");
+
+               if (component instanceof Page)
+               {
+                       if (component != page)
+                       {
+                               throw new IllegalArgumentException("component 
cannot be a page");
+                       }
+               }
+               else if (component instanceof AbstractRepeater)
+               {
+                       throw new IllegalArgumentException(
+                                       "Component " +
+                                                       
component.getClass().getName() +
+                                                       " has been added to a 
partial page update. This component is a repeater and cannot be repainted 
directly. " +
+                                                       "Instead add its parent 
or another markup container higher in the hierarchy.");
+               }
+
+               assertComponentsNotFrozen();
+
+               component.setMarkupId(markupId);
+               markupIdToComponent.put(markupId, component);
+       }
+
+       /**
+        * @return a read-only collection of all components which have been 
added for replacement so far.
+        */
+       public final Collection<? extends Component> getComponents()
+       {
+               return 
Collections.unmodifiableCollection(markupIdToComponent.values());
+       }
+
+       /**
+        * Detaches the page if at least one of its components was updated.
+        *
+        * @param requestCycle
+        *      the current request cycle
+        */
+       public void detach(IRequestCycle requestCycle)
+       {
+               Iterator<Component> iterator = 
markupIdToComponent.values().iterator();
+               while (iterator.hasNext())
+               {
+                       final Component component = iterator.next();
+                       final Page parentPage = 
component.findParent(Page.class);
+                       if (parentPage != null)
+                       {
+                               parentPage.detach();
+                               break;
+                       }
+               }
+       }
+
+       /**
+        * Checks if the target contains an ancestor for the given component
+        *
+        * @param component
+        *      the component which ancestors should be checked.
+        * @return <code>true</code> if target contains an ancestor for the 
given component
+        */
+       protected boolean containsAncestorFor(Component component)
+       {
+               Component cursor = component.getParent();
+               while (cursor != null)
+               {
+                       if (markupIdToComponent.containsValue(cursor))
+                       {
+                               return true;
+                       }
+                       cursor = cursor.getParent();
+               }
+               return false;
+       }
+
+       /**
+        * @return {@code true} if the page has been added for replacement
+        */
+       public boolean containsPage()
+       {
+               return markupIdToComponent.values().contains(page);
+       }
+
+       /**
+        * Gets or creates an IHeaderResponse instance to use for the header 
contributions.
+        *
+        * @return IHeaderResponse instance to use for the header contributions.
+        */
+       public IHeaderResponse getHeaderResponse()
+       {
+               if (headerResponse == null)
+               {
+                       // we don't need to decorate the header response here 
because this is called from
+                       // within PartialHtmlHeaderContainer, which decorates 
the response
+                       headerResponse = new PartialHeaderResponse();
+               }
+               return headerResponse;
+       }
+
+       /**
+        * @param response
+        *      the response to write to
+        * @param component
+        *      to component which will contribute to the header
+        */
+       protected void writeHeaderContribution(final Response response, final 
Component component)
+       {
+               headerRendering = true;
+
+               // create the htmlheadercontainer if needed
+               if (header == null)
+               {
+                       header = new PartialHtmlHeaderContainer(this);
+                       final Page parentPage = component.getPage();
+                       parentPage.addOrReplace(header);
+               }
+
+               RequestCycle requestCycle = component.getRequestCycle();
+
+               // save old response, set new
+               Response oldResponse = requestCycle.setResponse(headerBuffer);
+
+               try {
+                       headerBuffer.reset();
+
+                       IHeaderRenderStrategy strategy = 
AbstractHeaderRenderStrategy.get();
+
+                       strategy.renderHeader(header, null, component);
+               } finally {
+                       // revert to old response
+                       requestCycle.setResponse(oldResponse);
+               }
+
+               writeHeaderContribution(response);
+
+               headerRendering = false;
+       }
+
+       /**
+        * Sets the Content-Type header to indicate the type of the response.
+        *
+        * @param response
+        *      the current we response
+        * @param encoding
+        *      the encoding to use
+        */
+       public abstract void setContentType(WebResponse response, String 
encoding);
+
+       /**
+        * Header container component partial page updates.
+        *
+        * @author Matej Knopp
+        */
+       private static class PartialHtmlHeaderContainer extends 
HtmlHeaderContainer
+       {
+               private static final long serialVersionUID = 1L;
+
+               private transient PartialPageUpdate update;
+
+               /**
+                * Constructor.
+                *
+                * @param update
+                *      the partial page update
+                */
+               public PartialHtmlHeaderContainer(final PartialPageUpdate 
update)
+               {
+                       super(HtmlHeaderSectionHandler.HEADER_ID);
+                       this.update = update;
+               }
+
+               /**
+                *
+                * @see 
org.apache.wicket.markup.html.internal.HtmlHeaderContainer#newHeaderResponse()
+                */
+               @Override
+               protected IHeaderResponse newHeaderResponse()
+               {
+                   if (update != null)
+            {
+                       return update.getHeaderResponse();
+            }
+                   
+                   return super.newHeaderResponse();
+               }
+               
+               @Override
+               protected void onDetach()
+               {
+                   super.onDetach();
+                   update = null;
+               }
+       }
+
+       /**
+        * Header response for partial updates.
+        *
+        * @author Matej Knopp
+        */
+       private class PartialHeaderResponse extends HeaderResponse
+       {
+               @Override
+               public void render(HeaderItem item)
+               {
+                       PriorityHeaderItem priorityHeaderItem = null;
+                       while (item instanceof IWrappedHeaderItem)
+                       {
+                               if (item instanceof PriorityHeaderItem)
+                               {
+                                       priorityHeaderItem = 
(PriorityHeaderItem) item;
+                               }
+                               item = ((IWrappedHeaderItem) item).getWrapped();
+                       }
+
+                       if (item instanceof OnLoadHeaderItem)
+                       {
+                               if (!wasItemRendered(item))
+                               {
+                                       
PartialPageUpdate.this.appendJavaScript(((OnLoadHeaderItem) 
item).getJavaScript());
+                                       markItemRendered(item);
+                               }
+                       }
+                       else if (item instanceof OnEventHeaderItem)
+                       {
+                               if (!wasItemRendered(item))
+                               {
+                                       
PartialPageUpdate.this.appendJavaScript(((OnEventHeaderItem) 
item).getCompleteJavaScript());
+                                       markItemRendered(item);
+                               }
+                       }
+                       else if (item instanceof OnDomReadyHeaderItem)
+                       {
+                               if (!wasItemRendered(item))
+                               {
+                                       if (priorityHeaderItem != null)
+                                       {
+                                               
PartialPageUpdate.this.domReadyJavaScripts.add(0, 
((OnDomReadyHeaderItem)item).getJavaScript());
+                                       }
+                                       else
+                                       {
+                                               
PartialPageUpdate.this.domReadyJavaScripts.add(((OnDomReadyHeaderItem)item).getJavaScript());
+                                       }
+                                       markItemRendered(item);
+                               }
+                       }
+                       else if (headerRendering)
+                       {
+                               super.render(item);
+                       }
+                       else
+                       {
+                               LOG.debug("Only methods that can be called on 
IHeaderResponse outside renderHead() are #render(OnLoadHeaderItem) and 
#render(OnDomReadyHeaderItem)");
+                       }
+               }
+
+               @Override
+               protected Response getRealResponse()
+               {
+                       return RequestCycle.get().getResponse();
+               }
+       }
+
+       /**
+        * Wrapper of a response that buffers its contents.
+        *
+        * @author Igor Vaynberg (ivaynberg)
+        * @author Sven Meier (svenmeier)
+        * 
+        * @see ResponseBuffer#getContents()
+        * @see ResponseBuffer#reset()
+        */
+       protected static final class ResponseBuffer extends WebResponse
+       {
+               private final AppendingStringBuffer buffer = new 
AppendingStringBuffer(256);
+
+               private final WebResponse originalResponse;
+
+               /**
+                * Constructor.
+                *
+                * @param originalResponse
+                *      the original request cycle response
+                */
+               private ResponseBuffer(WebResponse originalResponse)
+               {
+                       this.originalResponse = originalResponse;
+               }
+
+               /**
+                * @see 
org.apache.wicket.request.Response#encodeURL(CharSequence)
+                */
+               @Override
+               public String encodeURL(CharSequence url)
+               {
+                       return originalResponse.encodeURL(url);
+               }
+
+               /**
+                * @return contents of the response
+                */
+               public CharSequence getContents()
+               {
+                       return buffer;
+               }
+
+               /**
+                * @see org.apache.wicket.request.Response#write(CharSequence)
+                */
+               @Override
+               public void write(CharSequence cs)
+               {
+                       buffer.append(cs);
+               }
+
+               /**
+                * Resets the response to a clean state so it can be reused to 
save on garbage.
+                */
+               @Override
+               public void reset()
+               {
+                       buffer.clear();
+               }
+
+               @Override
+               public void write(byte[] array)
+               {
+                       throw new UnsupportedOperationException("Cannot write 
binary data.");
+               }
+
+               @Override
+               public void write(byte[] array, int offset, int length)
+               {
+                       throw new UnsupportedOperationException("Cannot write 
binary data.");
+               }
+
+               @Override
+               public Object getContainerResponse()
+               {
+                       return originalResponse.getContainerResponse();
+               }
+
+               @Override
+               public void addCookie(Cookie cookie)
+               {
+                       originalResponse.addCookie(cookie);
+               }
+
+               @Override
+               public void clearCookie(Cookie cookie)
+               {
+                       originalResponse.clearCookie(cookie);
+               }
+
+               @Override
+               public void setHeader(String name, String value)
+               {
+                       originalResponse.setHeader(name, value);
+               }
+
+               @Override
+               public void addHeader(String name, String value)
+               {
+                       originalResponse.addHeader(name, value);
+               }
+
+               @Override
+               public void setDateHeader(String name, Time date)
+               {
+                       originalResponse.setDateHeader(name, date);
+               }
+
+               @Override
+               public void setContentLength(long length)
+               {
+                       originalResponse.setContentLength(length);
+               }
+
+               @Override
+               public void setContentType(String mimeType)
+               {
+                       originalResponse.setContentType(mimeType);
+               }
+
+               @Override
+               public void setStatus(int sc)
+               {
+                       originalResponse.setStatus(sc);
+               }
+
+               @Override
+               public void sendError(int sc, String msg)
+               {
+                       originalResponse.sendError(sc, msg);
+               }
+
+               @Override
+               public String encodeRedirectURL(CharSequence url)
+               {
+                       return originalResponse.encodeRedirectURL(url);
+               }
+
+               @Override
+               public void sendRedirect(String url)
+               {
+                       originalResponse.sendRedirect(url);
+               }
+
+               @Override
+               public boolean isRedirect()
+               {
+                       return originalResponse.isRedirect();
+               }
+
+               @Override
+               public void flush()
+               {
+                       originalResponse.flush();
+               }
+       }
+
+       private void assertComponentsNotFrozen()
+       {
+               assertNotFrozen(componentsFrozen, Component.class);
+       }
+
+       private void assertNotFrozen(boolean frozen, Class<?> clazz)
+       {
+               if (frozen)
+               {
+                       throw new 
IllegalStateException(Classes.simpleName(clazz) + "s can no " +
+                                       " longer be added");
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
new file mode 100644
index 0000000..8021285
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
@@ -0,0 +1,222 @@
+/*
+ * 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.page;
+
+import java.util.Collection;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.util.string.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link PartialPageUpdate} that serializes itself to XML.
+ */
+public class XmlPartialPageUpdate extends PartialPageUpdate
+{
+       private static final Logger LOG = 
LoggerFactory.getLogger(XmlPartialPageUpdate.class);
+
+       /**
+        * The name of the root element in the produced XML document.
+        */
+       public static final String START_ROOT_ELEMENT = "<ajax-response>";
+       public static final String END_ROOT_ELEMENT = "</ajax-response>";
+
+       public XmlPartialPageUpdate(final Page page)
+       {
+               super(page);
+       }
+
+       @Override
+       public void setContentType(WebResponse response, String encoding)
+       {
+               response.setContentType("text/xml; charset=" + encoding);
+       }
+
+       @Override
+       protected void writeHeader(Response response, String encoding)
+       {
+               response.write("<?xml version=\"1.0\" encoding=\"");
+               response.write(encoding);
+               response.write("\"?>");
+               response.write(START_ROOT_ELEMENT);
+       }
+
+       @Override
+       protected void writeComponent(Response response, String markupId, 
Component component, String encoding)
+       {
+               if (component.getRenderBodyOnly() == true)
+               {
+                       throw new IllegalStateException(
+                                       "A partial update is not possible for a 
component that has renderBodyOnly enabled. Component: " +
+                                                       component.toString());
+               }
+
+               component.setOutputMarkupId(true);
+
+               // Initialize temporary variables
+               final Page page = component.findParent(Page.class);
+               if (page == null)
+               {
+                       // dont throw an exception but just ignore this 
component, somehow
+                       // it got removed from the page.
+                       LOG.warn("Component '{}' with markupid: '{}' not 
rendered because it was already removed from page",
+                                       component, markupId);
+                       return;
+               }
+
+               // substitute our encoding response for the old one so we can 
capture
+               // component's markup in a manner safe for transport inside 
CDATA block
+               Response oldResponse = 
RequestCycle.get().setResponse(bodyBuffer);
+
+               try
+               {
+                       bodyBuffer.reset();
+                       
+                       page.startComponentRender(component);
+
+                       try
+                       {
+                               component.prepareForRender();
+
+                               // render any associated headers of the 
component
+                               writeHeaderContribution(response, component);
+                       }
+                       catch (RuntimeException e)
+                       {
+                               try
+                               {
+                                       component.afterRender();
+                               }
+                               catch (RuntimeException e2)
+                               {
+                                       // ignore this one could be a result 
off.
+                               }
+                               bodyBuffer.reset();
+                               throw e;
+                       }
+
+                       try
+                       {
+                               component.render();
+                       }
+                       catch (RuntimeException e)
+                       {
+                               bodyBuffer.reset();
+                               throw e;
+                       }
+
+                       page.endComponentRender(component);
+               }
+               finally
+               {
+                       // Restore original response
+                       RequestCycle.get().setResponse(oldResponse);
+               }
+
+               response.write("<component id=\"");
+               response.write(markupId);
+               response.write("\" ><![CDATA[");
+               response.write(encode(bodyBuffer.getContents()));
+               response.write("]]></component>");
+
+               bodyBuffer.reset();
+       }
+
+       @Override
+       protected void writeFooter(Response response, String encoding)
+       {
+               response.write(END_ROOT_ELEMENT);
+       }
+
+       @Override
+       protected void writeHeaderContribution(Response response)
+       {
+               if (headerBuffer.getContents().length() != 0)
+               {
+                       response.write("<header-contribution>");
+
+                       // we need to write response as CDATA and parse it on 
client,
+                       // because konqueror crashes when there is a <script> 
element
+                       response.write("<![CDATA[<head 
xmlns:wicket=\"http://wicket.apache.org\";>");
+                       response.write(encode(headerBuffer.getContents()));
+                       response.write("</head>]]>");
+                       response.write("</header-contribution>");
+               }
+       }
+
+       @Override
+       protected void writeNormalEvaluations(final Response response, final 
Collection<CharSequence> scripts)
+       {
+               writeEvaluations(response, "evaluate", scripts);
+
+       }
+
+       @Override
+       protected void writePriorityEvaluations(Response response, 
Collection<CharSequence> scripts)
+       {
+               writeEvaluations(response, "priority-evaluate", scripts);
+       }
+
+       private void writeEvaluations(final Response response, String 
elementName, Collection<CharSequence> scripts)
+       {
+               if (scripts.size() > 0)
+               {
+                       StringBuilder combinedScript = new StringBuilder(1024);
+                       for (CharSequence script : scripts)
+                       {
+                               
combinedScript.append("(function(){").append(script).append("})();");
+                       }
+                       writeEvaluation(elementName, response, combinedScript);
+               }
+       }
+
+       /**
+       * @param invocation
+       *            type of invocation tag, usually {@literal evaluate} or
+       *            {@literal priority-evaluate}
+       * @param response
+       * @param js
+       */
+       private void writeEvaluation(final String invocation, final Response 
response, final CharSequence js)
+       {
+               response.write("<");
+               response.write(invocation);
+               response.write(">");
+
+               response.write("<![CDATA[");
+               response.write(encode(js));
+               response.write("]]>");
+
+               response.write("</");
+               response.write(invocation);
+               response.write(">");
+
+               bodyBuffer.reset();
+       }
+
+       protected CharSequence encode(CharSequence str)
+       {
+               return Strings.replaceAll(str, "]]>", "]]]]><![CDATA[>"); 
+       }
+
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/main/java/org/apache/wicket/response/filter/XmlCleaningResponseFilter.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/response/filter/XmlCleaningResponseFilter.java
 
b/wicket-core/src/main/java/org/apache/wicket/response/filter/XmlCleaningResponseFilter.java
index 8920ece..3b5df10 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/response/filter/XmlCleaningResponseFilter.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/response/filter/XmlCleaningResponseFilter.java
@@ -16,7 +16,7 @@
  */
 package org.apache.wicket.response.filter;
 
-import org.apache.wicket.ajax.XmlPartialPageUpdate;
+import org.apache.wicket.page.XmlPartialPageUpdate;
 import org.apache.wicket.util.string.AppendingStringBuffer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.html
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.html 
b/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.html
new file mode 100644
index 0000000..b701e5d
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.html
@@ -0,0 +1,19 @@
+<!--
+    ====================================================================
+    Licensed 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.
+-->
+<html xmlns:wicket>
+<body>
+       <span wicket:id="container"> two brackets ]] greater than > CDATA start 
]]> </span>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.java 
b/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.java
new file mode 100644
index 0000000..fc97fc4
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/page/PageForPartialUpdate.java
@@ -0,0 +1,40 @@
+/*
+ * 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.page;
+
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+
+/**
+ * 
+ */
+public class PageForPartialUpdate extends WebPage
+{
+       private static final long serialVersionUID = 1L;
+       
+       public WebMarkupContainer container;
+
+       /**
+        * Construct.
+        */
+       public PageForPartialUpdate()
+       {
+               container = new WebMarkupContainer("container");
+               container.setOutputMarkupId(true);
+               add(container);
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/test/java/org/apache/wicket/page/XmlPartialPageUpdateTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/page/XmlPartialPageUpdateTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/page/XmlPartialPageUpdateTest.java
new file mode 100644
index 0000000..e6704f7
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/page/XmlPartialPageUpdateTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.page;
+
+import org.apache.wicket.mock.MockWebResponse;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.Test;
+
+public class XmlPartialPageUpdateTest extends WicketTestCase {
+
+       @Test
+       public void encode() {
+               PageForPartialUpdate page = new PageForPartialUpdate();
+               
+               XmlPartialPageUpdate update = new XmlPartialPageUpdate(page);
+               
+               update.add(page.container, page.container.getMarkupId());
+               
+               MockWebResponse response = new MockWebResponse();
+               
+               update.writeTo(response, "UTF-8");
+               
+               assertEquals("<?xml version=\"1.0\" 
encoding=\"UTF-8\"?><ajax-response><component id=\"container1\" ><![CDATA[<span 
wicket:id=\"container\" id=\"container1\"> two brackets ]] greater than > CDATA 
start ]]]]><![CDATA[> </span>]]></component></ajax-response>", 
response.getTextResponse().toString());
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-core/src/test/java/org/apache/wicket/response/filter/XmlCleaningResponseFilterTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/response/filter/XmlCleaningResponseFilterTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/response/filter/XmlCleaningResponseFilterTest.java
index c255a59..9bc9e1a 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/response/filter/XmlCleaningResponseFilterTest.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/response/filter/XmlCleaningResponseFilterTest.java
@@ -16,8 +16,9 @@
  */
 package org.apache.wicket.response.filter;
 
-import static org.apache.wicket.ajax.XmlPartialPageUpdate.END_ROOT_ELEMENT;
-import static org.apache.wicket.ajax.XmlPartialPageUpdate.START_ROOT_ELEMENT;
+import static org.apache.wicket.page.XmlPartialPageUpdate.END_ROOT_ELEMENT;
+import static org.apache.wicket.page.XmlPartialPageUpdate.START_ROOT_ELEMENT;
+
 import org.apache.wicket.util.string.AppendingStringBuffer;
 import org.junit.Assert;
 import org.junit.Test;

http://git-wip-us.apache.org/repos/asf/wicket/blob/6391f2cf/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
index 5528f76..1deee5b 100644
--- 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
+++ 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
@@ -23,10 +23,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
-import org.apache.wicket.ajax.PartialPageUpdate;
-import org.apache.wicket.ajax.XmlPartialPageUpdate;
 import org.apache.wicket.core.request.handler.logger.PageLogData;
 import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.page.PartialPageUpdate;
+import org.apache.wicket.page.XmlPartialPageUpdate;
 import org.apache.wicket.request.ILogData;
 import org.apache.wicket.request.IRequestCycle;
 import org.apache.wicket.request.component.IRequestablePage;

Reply via email to