001 // Copyright Mar 18, 2006 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 package org.apache.tapestry.services.impl; 015 016 import org.apache.hivemind.Resource; 017 import org.apache.hivemind.util.Defense; 018 import org.apache.tapestry.*; 019 import org.apache.tapestry.asset.AssetFactory; 020 import org.apache.tapestry.engine.NullWriter; 021 import org.apache.tapestry.json.IJSONWriter; 022 import org.apache.tapestry.markup.MarkupWriterSource; 023 import org.apache.tapestry.services.RequestLocaleManager; 024 import org.apache.tapestry.services.ResponseBuilder; 025 import org.apache.tapestry.services.ServiceConstants; 026 import org.apache.tapestry.util.ContentType; 027 import org.apache.tapestry.util.PageRenderSupportImpl; 028 import org.apache.tapestry.web.WebRequest; 029 import org.apache.tapestry.web.WebResponse; 030 031 import java.io.IOException; 032 import java.io.PrintWriter; 033 import java.util.ArrayList; 034 import java.util.Iterator; 035 import java.util.List; 036 037 /** 038 * Class that implements JSON responses in tapestry. 039 * 040 * @see <a href="http://json.org">json.org</a> 041 * @author jkuhnert 042 */ 043 public class JSONResponseBuilder implements ResponseBuilder 044 { 045 /** Writer that creates JSON output response. */ 046 protected IJSONWriter _writer; 047 /** Passed in to bypass normal rendering. */ 048 protected IMarkupWriter _nullWriter = NullWriter.getSharedInstance(); 049 050 /** Parts that will be updated. */ 051 protected List _parts = new ArrayList(); 052 053 protected RequestLocaleManager _localeManager; 054 protected MarkupWriterSource _markupWriterSource; 055 056 private WebResponse _response; 057 058 private ContentType _contentType; 059 060 private final AssetFactory _assetFactory; 061 062 private final String _namespace; 063 064 private PageRenderSupportImpl _prs; 065 066 private IRequestCycle _cycle; 067 068 /** 069 * Creates a new response builder with the required services it needs 070 * to render the response when {@link #renderResponse(IRequestCycle)} is called. 071 * 072 * @param localeManager 073 * Used to set the locale on the response. 074 * @param markupWriterSource 075 * Creates IJSONWriter instance to be used. 076 * @param webResponse 077 * Web response for output stream. 078 */ 079 public JSONResponseBuilder(IRequestCycle cycle, RequestLocaleManager localeManager, 080 MarkupWriterSource markupWriterSource, 081 WebResponse webResponse, WebRequest request, AssetFactory assetFactory, String namespace) 082 { 083 Defense.notNull(cycle, "cycle"); 084 085 _cycle = cycle; 086 _localeManager = localeManager; 087 _markupWriterSource = markupWriterSource; 088 _response = webResponse; 089 090 // Used by PageRenderSupport 091 092 _assetFactory = assetFactory; 093 _namespace = namespace; 094 095 _prs = new PageRenderSupportImpl(_assetFactory, _namespace, this, cycle); 096 } 097 098 /** 099 * 100 * {@inheritDoc} 101 */ 102 public boolean isDynamic() 103 { 104 return true; 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 public void renderResponse(IRequestCycle cycle) 111 throws IOException 112 { 113 _localeManager.persistLocale(); 114 115 IPage page = cycle.getPage(); 116 117 _contentType = page.getResponseContentType(); 118 119 String encoding = _contentType.getParameter(ENCODING_KEY); 120 121 if (encoding == null) 122 { 123 encoding = cycle.getEngine().getOutputEncoding(); 124 125 _contentType.setParameter(ENCODING_KEY, encoding); 126 } 127 128 if (_writer == null) 129 { 130 parseParameters(cycle); 131 132 PrintWriter printWriter = _response.getPrintWriter(_contentType); 133 134 _writer = _markupWriterSource.newJSONWriter(printWriter, _contentType); 135 } 136 137 // render response 138 139 TapestryUtils.storePageRenderSupport(cycle, _prs); 140 141 cycle.renderPage(this); 142 143 TapestryUtils.removePageRenderSupport(cycle); 144 145 flush(); 146 147 _writer.close(); 148 } 149 150 public void flush() 151 throws IOException 152 { 153 // Important - causes any cookies stored to properly be written out before the 154 // rest of the response starts being written - see TAPESTRY-825 155 156 _writer.flush(); 157 } 158 159 /** 160 * Grabs the incoming parameters needed for json responses, most notable the 161 * {@link ServiceConstants#UPDATE_PARTS} parameter. 162 * 163 * @param cycle 164 * The request cycle to parse from 165 */ 166 protected void parseParameters(IRequestCycle cycle) 167 { 168 Object[] updateParts = cycle.getParameters(ServiceConstants.UPDATE_PARTS); 169 170 if (updateParts == null) 171 return; 172 173 for(int i = 0; i < updateParts.length; i++) 174 _parts.add(updateParts[i].toString()); 175 } 176 177 /** 178 * {@inheritDoc} 179 */ 180 public void render(IMarkupWriter writer, IRender render, IRequestCycle cycle) 181 { 182 if (IJSONRender.class.isInstance(render) 183 && IComponent.class.isInstance(render)) 184 { 185 IJSONRender json = (IJSONRender) render; 186 IComponent component = (IComponent) render; 187 188 if (!contains(component, component.peekClientId())) 189 { 190 render.render(_nullWriter, cycle); 191 return; 192 } 193 194 json.renderComponent(_writer, cycle); 195 } 196 197 render.render(_nullWriter, cycle); 198 } 199 200 /** 201 * {@inheritDoc} 202 */ 203 public void updateComponent(String id) 204 { 205 if (!_parts.contains(id)) 206 _parts.add(id); 207 } 208 209 /** 210 * Determines if the specified component is contained in the 211 * responses requested update parts. 212 * @param target 213 * The component to check for. 214 * @return True if the request should capture the components output. 215 */ 216 public boolean contains(IComponent target) 217 { 218 if (target == null) 219 return false; 220 221 String id = target.getClientId(); 222 223 return contains(target, id); 224 } 225 226 boolean contains(IComponent target, String id) 227 { 228 if (_parts.contains(id)) 229 return true; 230 231 Iterator it = _cycle.renderStackIterator(); 232 while (it.hasNext()) 233 { 234 IComponent comp = (IComponent)it.next(); 235 String compId = comp.getClientId(); 236 237 if (comp != target && _parts.contains(compId)) 238 return true; 239 } 240 241 return false; 242 } 243 244 /** 245 * {@inheritDoc} 246 */ 247 public boolean explicitlyContains(IComponent target) 248 { 249 if (target == null) 250 return false; 251 252 return _parts.contains(target.getId()); 253 } 254 255 /** 256 * {@inheritDoc} 257 */ 258 public IMarkupWriter getWriter() 259 { 260 return _nullWriter; 261 } 262 263 /** 264 * {@inheritDoc} 265 */ 266 public IMarkupWriter getWriter(String id, String type) 267 { 268 return _nullWriter; 269 } 270 271 /** 272 * {@inheritDoc} 273 */ 274 public boolean isBodyScriptAllowed(IComponent target) 275 { 276 return false; 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 public boolean isExternalScriptAllowed(IComponent target) 283 { 284 return false; 285 } 286 287 /** 288 * {@inheritDoc} 289 */ 290 public boolean isInitializationScriptAllowed(IComponent target) 291 { 292 return false; 293 } 294 295 /** 296 * {@inheritDoc} 297 */ 298 public boolean isImageInitializationAllowed(IComponent target) 299 { 300 return false; 301 } 302 303 /** 304 * {@inheritDoc} 305 */ 306 public String getPreloadedImageReference(IComponent target, IAsset source) 307 { 308 return _prs.getPreloadedImageReference(target, source); 309 } 310 311 /** 312 * {@inheritDoc} 313 */ 314 public String getPreloadedImageReference(IComponent target, String url) 315 { 316 return _prs.getPreloadedImageReference(target, url); 317 } 318 319 /** 320 * {@inheritDoc} 321 */ 322 public String getPreloadedImageReference(String url) 323 { 324 return _prs.getPreloadedImageReference(url); 325 } 326 327 /** 328 * {@inheritDoc} 329 */ 330 public void addBodyScript(IComponent target, String script) 331 { 332 _prs.addBodyScript(target, script); 333 } 334 335 /** 336 * {@inheritDoc} 337 */ 338 public void addBodyScript(String script) 339 { 340 _prs.addBodyScript(script); 341 } 342 343 /** 344 * {@inheritDoc} 345 */ 346 public void addExternalScript(IComponent target, Resource resource) 347 { 348 _prs.addExternalScript(target, resource); 349 } 350 351 /** 352 * {@inheritDoc} 353 */ 354 public void addExternalScript(Resource resource) 355 { 356 _prs.addExternalScript(resource); 357 } 358 359 /** 360 * {@inheritDoc} 361 */ 362 public void addInitializationScript(IComponent target, String script) 363 { 364 _prs.addInitializationScript(target, script); 365 } 366 367 /** 368 * {@inheritDoc} 369 */ 370 public void addInitializationScript(String script) 371 { 372 _prs.addInitializationScript(script); 373 } 374 375 public void addScriptAfterInitialization(IComponent target, String script) 376 { 377 _prs.addScriptAfterInitialization(target, script); 378 } 379 380 /** 381 * {@inheritDoc} 382 */ 383 public String getUniqueString(String baseValue) 384 { 385 return _prs.getUniqueString(baseValue); 386 } 387 388 /** 389 * {@inheritDoc} 390 */ 391 public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle) 392 { 393 _prs.writeBodyScript(writer, cycle); 394 } 395 396 /** 397 * {@inheritDoc} 398 */ 399 public void writeInitializationScript(IMarkupWriter writer) 400 { 401 _prs.writeInitializationScript(writer); 402 } 403 404 /** 405 * {@inheritDoc} 406 */ 407 public void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle) 408 { 409 // does nothing 410 } 411 412 /** 413 * {@inheritDoc} 414 */ 415 public void endBodyScript(IMarkupWriter writer, IRequestCycle cycle) 416 { 417 // does nothing 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 public void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle) 424 { 425 // does nothing 426 } 427 428 /** 429 * {@inheritDoc} 430 */ 431 public void writeExternalScript(IMarkupWriter normalWriter, String url, IRequestCycle cycle) 432 { 433 // does nothing 434 } 435 436 /** 437 * {@inheritDoc} 438 */ 439 public void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle) 440 { 441 // does nothing 442 } 443 444 /** 445 * {@inheritDoc} 446 */ 447 public void writeInitializationScript(IMarkupWriter writer, String script) 448 { 449 // does nothing 450 } 451 452 /** 453 * This implementation does nothing. 454 * {@inheritDoc} 455 */ 456 public void addStatusMessage(IMarkupWriter normalWriter, String category, String text) 457 { 458 } 459 }