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 096 /** 097 * 098 * {@inheritDoc} 099 */ 100 public boolean isDynamic() 101 { 102 return true; 103 } 104 105 /** 106 * {@inheritDoc} 107 */ 108 public void renderResponse(IRequestCycle cycle) 109 throws IOException 110 { 111 _localeManager.persistLocale(); 112 113 IPage page = cycle.getPage(); 114 115 _contentType = page.getResponseContentType(); 116 117 String encoding = _contentType.getParameter(ENCODING_KEY); 118 119 if (encoding == null) 120 { 121 encoding = cycle.getEngine().getOutputEncoding(); 122 123 _contentType.setParameter(ENCODING_KEY, encoding); 124 } 125 126 if (_writer == null) { 127 128 parseParameters(cycle); 129 130 PrintWriter printWriter = _response.getPrintWriter(_contentType); 131 132 _writer = _markupWriterSource.newJSONWriter(printWriter, _contentType); 133 } 134 135 // render response 136 137 _prs = new PageRenderSupportImpl(_assetFactory, _namespace, cycle.getPage().getLocation(), this); 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 /** 376 * {@inheritDoc} 377 */ 378 public String getUniqueString(String baseValue) 379 { 380 return _prs.getUniqueString(baseValue); 381 } 382 383 /** 384 * {@inheritDoc} 385 */ 386 public void writeBodyScript(IMarkupWriter writer, IRequestCycle cycle) 387 { 388 _prs.writeBodyScript(writer, cycle); 389 } 390 391 /** 392 * {@inheritDoc} 393 */ 394 public void writeInitializationScript(IMarkupWriter writer) 395 { 396 _prs.writeInitializationScript(writer); 397 } 398 399 /** 400 * {@inheritDoc} 401 */ 402 public void beginBodyScript(IMarkupWriter writer, IRequestCycle cycle) 403 { 404 // does nothing 405 } 406 407 /** 408 * {@inheritDoc} 409 */ 410 public void endBodyScript(IMarkupWriter writer, IRequestCycle cycle) 411 { 412 // does nothing 413 } 414 415 /** 416 * {@inheritDoc} 417 */ 418 public void writeBodyScript(IMarkupWriter writer, String script, IRequestCycle cycle) 419 { 420 // does nothing 421 } 422 423 /** 424 * {@inheritDoc} 425 */ 426 public void writeExternalScript(IMarkupWriter normalWriter, String url, IRequestCycle cycle) 427 { 428 // does nothing 429 } 430 431 /** 432 * {@inheritDoc} 433 */ 434 public void writeImageInitializations(IMarkupWriter writer, String script, String preloadName, IRequestCycle cycle) 435 { 436 // does nothing 437 } 438 439 /** 440 * {@inheritDoc} 441 */ 442 public void writeInitializationScript(IMarkupWriter writer, String script) 443 { 444 // does nothing 445 } 446 447 /** 448 * This implementation does nothing. 449 * {@inheritDoc} 450 */ 451 public void addStatusMessage(IMarkupWriter normalWriter, String category, String text) 452 { 453 } 454 }