001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011 012package org.apache.tapestry5.internal.services.ajax; 013 014import org.apache.tapestry5.ClientBodyElement; 015import org.apache.tapestry5.MarkupWriter; 016import org.apache.tapestry5.internal.InternalConstants; 017import org.apache.tapestry5.internal.services.PageRenderQueue; 018import org.apache.tapestry5.internal.services.PageSource; 019import org.apache.tapestry5.internal.services.RequestPageCache; 020import org.apache.tapestry5.internal.structure.Page; 021import org.apache.tapestry5.ioc.internal.util.InternalUtils; 022import org.apache.tapestry5.ioc.services.TypeCoercer; 023import org.apache.tapestry5.json.JSONObject; 024import org.apache.tapestry5.runtime.RenderCommand; 025import org.apache.tapestry5.services.PartialMarkupRenderer; 026import org.apache.tapestry5.services.PartialMarkupRendererFilter; 027import org.apache.tapestry5.services.Request; 028import org.apache.tapestry5.services.ajax.AjaxResponseRenderer; 029import org.apache.tapestry5.services.ajax.JSONCallback; 030import org.apache.tapestry5.services.ajax.JavaScriptCallback; 031import org.apache.tapestry5.services.javascript.JavaScriptSupport; 032 033 034public class AjaxResponseRendererImpl implements AjaxResponseRenderer 035{ 036 private final PageRenderQueue queue; 037 038 private final AjaxFormUpdateController ajaxFormUpdateController; 039 040 private final TypeCoercer typeCoercer; 041 042 private final JavaScriptSupport javaScriptSupport; 043 044 private final RequestPageCache requestPageCache; 045 046 private final Request request; 047 048 public AjaxResponseRendererImpl(PageRenderQueue queue, AjaxFormUpdateController ajaxFormUpdateController, TypeCoercer typeCoercer, JavaScriptSupport javaScriptSupport, RequestPageCache requestPageCache, Request request) 049 { 050 this.queue = queue; 051 this.ajaxFormUpdateController = ajaxFormUpdateController; 052 this.typeCoercer = typeCoercer; 053 this.javaScriptSupport = javaScriptSupport; 054 this.requestPageCache = requestPageCache; 055 this.request = request; 056 } 057 058 public AjaxResponseRenderer addRender(String clientId, Object renderer) 059 { 060 assert InternalUtils.isNonBlank(clientId); 061 assert renderer != null; 062 063 RenderCommand command = typeCoercer.coerce(renderer, RenderCommand.class); 064 065 addFilter(new SingleZonePartialRendererFilter(clientId, command, queue, ajaxFormUpdateController)); 066 067 return this; 068 } 069 070 public AjaxResponseRenderer addRender(ClientBodyElement zone) 071 { 072 assert zone != null; 073 074 final String clientId = zone.getClientId(); 075 076 if (clientId == null) 077 { 078 throw new IllegalArgumentException( 079 "Attempt to render a ClientBodyElement, probably a Zone, with a null clientId. " 080 + "You can solve this by using the id parameter."); 081 } 082 083 addRender(clientId, zone.getBody()); 084 085 return this; 086 } 087 088 public AjaxResponseRenderer addCallback(final JavaScriptCallback callback) 089 { 090 assert callback != null; 091 092 addFilter(new PartialMarkupRendererFilter() 093 { 094 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 095 { 096 renderer.renderMarkup(writer, reply); 097 098 callback.run(javaScriptSupport); 099 } 100 }); 101 102 return this; 103 } 104 105 public AjaxResponseRenderer addCallback(final Runnable callback) 106 { 107 assert callback != null; 108 109 addFilter(new PartialMarkupRendererFilter() 110 { 111 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 112 { 113 renderer.renderMarkup(writer, reply); 114 115 callback.run(); 116 } 117 }); 118 119 120 return this; 121 } 122 123 private boolean isRedirect(JSONObject reply) 124 { 125 126 return reply.has(InternalConstants.PARTIAL_KEY) && 127 reply.in(InternalConstants.PARTIAL_KEY).has("redirectURL"); 128 } 129 130 public AjaxResponseRenderer addFilter(final PartialMarkupRendererFilter filter) 131 { 132 assert filter != null; 133 134 queue.addPartialMarkupRendererFilter(new PartialMarkupRendererFilter() 135 { 136 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer delete) 137 { 138 if (isRedirect(reply)) 139 { 140 // Bypass the callback. 141 delete.renderMarkup(writer, reply); 142 return; 143 } 144 145 filter.renderMarkup(writer, reply, delete); 146 } 147 }); 148 149 return this; 150 } 151 152 public AjaxResponseRenderer addCallback(final JSONCallback callback) 153 { 154 assert callback != null; 155 156 addFilter(new PartialMarkupRendererFilter() 157 { 158 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 159 { 160 renderer.renderMarkup(writer, reply); 161 162 callback.run(reply); 163 } 164 }); 165 166 return this; 167 } 168 169 @Override 170 public void setupPartial(String pageName) 171 { 172 Page page = requestPageCache.get(pageName); 173 174 queue.setRenderingPage(page); 175 176 request.setAttribute(InternalConstants.PAGE_NAME_ATTRIBUTE_NAME, page.getName()); 177 } 178}