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 }