1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.tomahawk.application.jsp;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.tiles.access.TilesAccess;
24 import org.apache.tiles.context.BasicAttributeContext;
25 import org.apache.tiles.context.TilesRequestContext;
26 import org.apache.tiles.definition.NoSuchDefinitionException;
27 import org.apache.tiles.factory.TilesContainerFactory;
28 import org.apache.tiles.impl.BasicTilesContainer;
29 import org.apache.tiles.preparer.NoSuchPreparerException;
30 import org.apache.tiles.preparer.ViewPreparer;
31 import org.apache.tiles.servlet.context.ServletTilesApplicationContext;
32 import org.apache.tiles.servlet.context.ServletTilesRequestContext;
33 import org.apache.tiles.AttributeContext;
34 import org.apache.tiles.Definition;
35 import org.apache.tiles.TilesContainer;
36 import org.apache.tiles.TilesException;
37 import org.apache.myfaces.shared_tomahawk.config.MyfacesConfig;
38 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
39 import org.apache.myfaces.shared_tomahawk.webapp.webxml.ServletMapping;
40 import org.apache.myfaces.shared_tomahawk.webapp.webxml.WebXml;
41
42 import javax.faces.application.StateManager;
43 import javax.faces.application.ViewHandler;
44 import javax.faces.context.ExternalContext;
45 import javax.faces.context.FacesContext;
46 import javax.faces.context.ResponseWriter;
47 import javax.faces.FacesException;
48 import javax.faces.FactoryFinder;
49 import javax.faces.component.UIViewRoot;
50 import javax.faces.render.RenderKit;
51 import javax.faces.render.RenderKitFactory;
52 import javax.servlet.http.HttpServletRequest;
53 import javax.servlet.http.HttpServletResponse;
54 import javax.servlet.http.HttpSession;
55 import javax.servlet.ServletResponse;
56 import java.io.IOException;
57 import java.io.StringWriter;
58 import java.io.Writer;
59 import java.util.List;
60 import java.util.Locale;
61 import java.util.StringTokenizer;
62
63
64
65
66
67
68
69
70
71 public class JspTilesTwoViewHandlerImpl
72 extends ViewHandler {
73 private ViewHandler _viewHandler;
74
75 public static final String FORM_STATE_MARKER = "<!--@@JSF_FORM_STATE_MARKER@@-->";
76 public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER.length();
77
78 private static final Log log = LogFactory.getLog(JspTilesTwoViewHandlerImpl.class);
79 private static final String TILES_DEF_EXT = ".tiles";
80 private String tilesExtension = TILES_DEF_EXT;
81 private static final String AFTER_VIEW_TAG_CONTENT_PARAM = JspTilesTwoViewHandlerImpl.class + ".AFTER_VIEW_TAG_CONTENT";
82
83 public JspTilesTwoViewHandlerImpl(ViewHandler viewHandler)
84 {
85 _viewHandler = viewHandler;
86 }
87
88 private void initContainer(ExternalContext context) {
89
90 if(TilesAccess.getContainer(context.getContext())==null) {
91 try
92 {
93 TilesContainerFactory factory = TilesContainerFactory.getFactory(context.getContext());
94 TilesContainer container = factory.createTilesContainer(context.getContext());
95 TilesAccess.setContainer(context.getContext(),container);
96 }
97 catch (Exception e)
98 {
99 throw new FacesException("Error reading tiles definitions : " + e.getMessage(), e);
100 }
101 }
102 }
103
104 public void renderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException, FacesException
105 {
106 if (viewToRender == null)
107 {
108 log.fatal("viewToRender must not be null");
109 throw new NullPointerException("viewToRender must not be null");
110 }
111
112 ExternalContext externalContext = facesContext.getExternalContext();
113
114 String viewId = deriveViewId(externalContext, viewToRender.getViewId());
115
116 if(viewId==null) {
117
118 _viewHandler.renderView(facesContext, viewToRender);
119 return;
120 }
121
122
123 initContainer(externalContext);
124
125 String tilesId = deriveTileFromViewId(viewId);
126
127 TilesContainer container = TilesAccess.getContainer(externalContext.getContext());
128
129 Object[] requestObjects = {externalContext.getRequest(), externalContext.getResponse()};
130
131 if(container.isValidDefinition(tilesId, requestObjects)) {
132
133
134 setViewId(viewToRender, viewId, facesContext);
135 renderTilesView(facesContext, requestObjects, container, viewToRender, viewId, tilesId);
136 } else {
137
138
139 _viewHandler.renderView(facesContext, viewToRender);
140 }
141 }
142
143 private void renderTilesView(FacesContext facesContext,
144 Object[] requestObjects, TilesContainer container,
145 UIViewRoot viewToRender, String viewId, String tilesId)
146 throws IOException
147 {
148 ExternalContext externalContext = facesContext.getExternalContext();
149 handleCharacterEncoding(viewId, externalContext, viewToRender);
150 container.startContext(requestObjects);
151 try
152 {
153
154 buildTilesViewLikeContainer(externalContext, container, tilesId,
155 requestObjects);
156 }
157 catch (TilesException e)
158 {
159 throw new FacesException(e);
160 }
161 finally
162 {
163 container.endContext(requestObjects);
164 }
165
166 handleCharacterEncodingPostDispatch(externalContext);
167
168
169 RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder
170 .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
171 RenderKit renderKit = renderFactory.getRenderKit(facesContext,
172 viewToRender.getRenderKitId());
173 ServletResponse response = (ServletResponse) requestObjects[1];
174 ResponseWriter responseWriter = facesContext.getResponseWriter();
175 if (responseWriter == null)
176 {
177 responseWriter = renderKit.createResponseWriter(response
178 .getWriter(), null, ((HttpServletRequest) externalContext
179 .getRequest()).getCharacterEncoding());
180 facesContext.setResponseWriter(responseWriter);
181 }
182
183 ResponseWriter oldResponseWriter = responseWriter;
184 StateMarkerAwareWriter stateAwareWriter = null;
185
186 StateManager stateManager = facesContext.getApplication()
187 .getStateManager();
188 if (stateManager.isSavingStateInClient(facesContext))
189 {
190 stateAwareWriter = new StateMarkerAwareWriter();
191
192
193
194
195 responseWriter = oldResponseWriter
196 .cloneWithWriter(stateAwareWriter);
197 facesContext.setResponseWriter(responseWriter);
198 }
199
200 actuallyRenderView(facesContext, viewToRender);
201
202
203
204
205 if (stateManager.isSavingStateInClient(facesContext))
206 {
207 stateAwareWriter.flushToWriter(response.getWriter());
208 }
209 else
210 {
211 stateManager.saveView(facesContext);
212 }
213
214
215
216 ViewResponseWrapper afterViewTagResponse = (ViewResponseWrapper) externalContext
217 .getRequestMap().get(AFTER_VIEW_TAG_CONTENT_PARAM);
218 externalContext.getRequestMap().remove(AFTER_VIEW_TAG_CONTENT_PARAM);
219 if (afterViewTagResponse != null)
220 {
221 afterViewTagResponse.flushToWriter(response.getWriter(),
222 facesContext.getExternalContext()
223 .getResponseCharacterEncoding());
224 }
225 response.flushBuffer();
226 }
227
228
229 private String deriveTileFromViewId(String viewId) {
230 String tilesId = viewId;
231 int idx = tilesId.lastIndexOf('.');
232 if (idx > 0)
233 {
234 tilesId = tilesId.substring(0, idx) + tilesExtension;
235 }
236 else
237 {
238 tilesId = tilesId + tilesExtension;
239 }
240 return tilesId;
241 }
242
243 private String deriveViewId(ExternalContext externalContext, String viewId) {
244 ServletMapping servletMapping = getServletMapping(externalContext);
245
246 String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
247 String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
248 if (servletMapping.isExtensionMapping())
249 {
250 if (!viewId.endsWith(suffix))
251 {
252 int dot = viewId.lastIndexOf('.');
253 if (dot == -1)
254 {
255 if (log.isTraceEnabled()) log.trace("Current viewId has no extension, appending default suffix " + suffix);
256 return viewId + suffix;
257 }
258 else
259 {
260 if (log.isTraceEnabled()) log.trace("Replacing extension of current viewId by suffix " + suffix);
261 return viewId.substring(0, dot) + suffix;
262 }
263 }
264
265
266 return viewId;
267 }
268 else if (!viewId.endsWith(suffix))
269 {
270
271 return null;
272 }
273 else {
274
275 return viewId;
276 }
277 }
278
279 private void handleCharacterEncodingPostDispatch(ExternalContext externalContext) {
280
281 if (externalContext.getRequest() instanceof HttpServletRequest) {
282 HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
283 HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
284 HttpSession session = request.getSession(false);
285
286 if (session != null) {
287 session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
288 }
289 }
290 }
291
292 private void handleCharacterEncoding(String viewId, ExternalContext externalContext, UIViewRoot viewToRender) {
293 if (log.isTraceEnabled()) log.trace("Dispatching to " + viewId);
294
295
296 if (externalContext.getResponse() instanceof ServletResponse) {
297 ServletResponse response = (ServletResponse) externalContext.getResponse();
298 response.setLocale(viewToRender.getLocale());
299 }
300 }
301
302 private void buildTilesViewLikeContainer(ExternalContext externalContext,
303 TilesContainer container, String definitionName,
304 Object... requestItems) throws TilesException
305 {
306 if (log.isDebugEnabled())
307 {
308 log.debug("Render request recieved for definition '"
309 + definitionName + "'");
310 }
311 TilesRequestContext tilesRequest = ((BasicTilesContainer) container)
312 .getContextFactory().createRequestContext(
313 container.getApplicationContext(), requestItems);
314 Definition definition = ((BasicTilesContainer) container)
315 .getDefinitionsFactory().getDefinition(definitionName,
316 tilesRequest);
317 if (definition == null)
318 {
319 if (log.isWarnEnabled())
320 {
321 String message = "Unable to find the definition '"
322 + definitionName + "'";
323 log.warn(message);
324 }
325 throw new NoSuchDefinitionException(definitionName);
326 }
327 if (!isPermitted(tilesRequest, definition.getRole()))
328 {
329 log.info("Access to definition '" + definitionName
330 + "' denied. User not in role '" + definition.getRole());
331 return;
332 }
333
334 AttributeContext originalContext = container
335 .getAttributeContext(requestItems);
336 BasicAttributeContext subContext = new BasicAttributeContext(
337 originalContext);
338 subContext.addMissing(definition.getAttributes());
339 BasicAttributeContext.pushContext(subContext, tilesRequest);
340
341 try
342 {
343 if (definition.getPreparer() != null)
344 {
345 prepare(container, tilesRequest, definition.getPreparer(), true);
346 }
347 String dispatchPath = definition.getTemplate();
348 if (log.isDebugEnabled())
349 {
350 log.debug("Dispatching to definition path '"
351 + definition.getTemplate() + " '");
352 }
353
354 if (!buildView(container, tilesRequest, externalContext,
355 dispatchPath))
356 {
357
358
359 return;
360 }
361
362
363 }
364 catch (TilesException e)
365 {
366 throw e;
367 }
368 catch (Exception e)
369 {
370 log.error("Error rendering tile", e);
371 throw new TilesException(e.getMessage(), e);
372 }
373 finally
374 {
375 BasicAttributeContext.popContext(tilesRequest);
376 }
377 }
378
379
380
381
382
383
384
385
386
387 private boolean isPermitted(TilesRequestContext request, String role) {
388 if (role == null) {
389 return true;
390 }
391 StringTokenizer st = new StringTokenizer(role, ",");
392 while (st.hasMoreTokens()) {
393 if (request.isUserInRole(st.nextToken())) {
394 return true;
395 }
396 }
397 return false;
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411 private void prepare(TilesContainer container, TilesRequestContext context, String preparerName, boolean ignoreMissing) throws TilesException {
412 if (log.isDebugEnabled()) {
413 log.debug("Prepare request received for '" + preparerName);
414 }
415 ViewPreparer preparer = ((BasicTilesContainer)container).getPreparerFactory().getPreparer(preparerName, context);
416 if (preparer == null && ignoreMissing) {
417 return;
418 }
419 if (preparer == null) {
420 throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
421 }
422 AttributeContext attributeContext = BasicAttributeContext.getContext(context);
423 preparer.execute(context, attributeContext);
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439 private boolean buildView(TilesContainer container,
440 TilesRequestContext tilesRequest, ExternalContext externalContext,
441 String viewId) throws IOException
442 {
443 HttpServletResponse response = (HttpServletResponse) tilesRequest
444 .getResponse();
445 ViewResponseWrapper wrappedResponse = new ViewResponseWrapper(response);
446 tilesRequest = new ServletTilesRequestContext(
447 ((ServletTilesApplicationContext) container
448 .getApplicationContext()).getServletContext(),
449 (HttpServletRequest) tilesRequest.getRequest(), wrappedResponse);
450 tilesRequest.dispatch(viewId);
451 tilesRequest = new ServletTilesRequestContext(
452 ((ServletTilesApplicationContext) container
453 .getApplicationContext()).getServletContext(),
454 (HttpServletRequest) tilesRequest.getRequest(), response);
455 boolean errorResponse = wrappedResponse.getStatus() < 200
456 || wrappedResponse.getStatus() > 299;
457 if (errorResponse)
458 {
459 wrappedResponse.flushToWrappedResponse();
460 return false;
461 }
462
463 externalContext.getRequestMap().put(AFTER_VIEW_TAG_CONTENT_PARAM,
464 wrappedResponse);
465 return true;
466 }
467
468
469
470
471 private void actuallyRenderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException {
472
473 ResponseWriter responseWriter = facesContext.getResponseWriter();
474
475
476 responseWriter.startDocument();
477
478 viewToRender.encodeAll(facesContext);
479
480 responseWriter.endDocument();
481 responseWriter.flush();
482 }
483
484 private void setViewId(UIViewRoot viewToRender, String viewId, FacesContext facesContext) {
485 viewToRender.setViewId(viewId);
486 if(facesContext.getViewRoot()!=null) {
487 facesContext.getViewRoot().setViewId(viewId);
488 }
489 }
490
491 private static ServletMapping getServletMapping(ExternalContext externalContext)
492 {
493 String servletPath = externalContext.getRequestServletPath();
494 String requestPathInfo = externalContext.getRequestPathInfo();
495
496 WebXml webxml = WebXml.getWebXml(externalContext);
497 List<?> mappings = webxml.getFacesServletMappings();
498
499 boolean isExtensionMapping = requestPathInfo == null;
500
501 for (int i = 0, size = mappings.size(); i < size; i++)
502 {
503 ServletMapping servletMapping = (ServletMapping) mappings.get(i);
504 if (servletMapping.isExtensionMapping() == isExtensionMapping)
505 {
506 String urlpattern = servletMapping.getUrlPattern();
507 if (isExtensionMapping)
508 {
509 String extension = urlpattern.substring(1, urlpattern.length());
510 if (servletPath.endsWith(extension))
511 {
512 return servletMapping;
513 }
514 }
515 else
516 {
517 urlpattern = urlpattern.substring(0, urlpattern.length() - 2);
518
519
520
521 if (servletPath.equals(urlpattern))
522 {
523 return servletMapping;
524 }
525 }
526 }
527 }
528 log.error("could not find pathMapping for servletPath = " + servletPath +
529 " requestPathInfo = " + requestPathInfo);
530 throw new IllegalArgumentException("could not find pathMapping for servletPath = " + servletPath +
531 " requestPathInfo = " + requestPathInfo);
532 }
533
534
535 public Locale calculateLocale(FacesContext context)
536 {
537 return _viewHandler.calculateLocale(context);
538 }
539
540 public String calculateRenderKitId(FacesContext context)
541 {
542 return _viewHandler.calculateRenderKitId(context);
543 }
544
545 public UIViewRoot createView(FacesContext context, String viewId)
546 {
547 return _viewHandler.createView(context, viewId);
548 }
549
550 public String getActionURL(FacesContext context, String viewId)
551 {
552 return _viewHandler.getActionURL(context, viewId);
553 }
554
555 public String getResourceURL(FacesContext context, String path)
556 {
557 return _viewHandler.getResourceURL(context, path);
558 }
559
560 public UIViewRoot restoreView(FacesContext context, String viewId)
561 {
562 return _viewHandler.restoreView(context, viewId);
563 }
564
565 public void writeState(FacesContext context) throws IOException
566 {
567 _viewHandler.writeState(context);
568 }
569
570
571
572
573 private static class StateMarkerAwareWriter extends Writer
574 {
575 private StringBuilder buf;
576
577 public StateMarkerAwareWriter()
578 {
579 this.buf = new StringBuilder();
580 }
581
582 @Override
583 public void close() throws IOException
584 {
585 }
586
587 @Override
588 public void flush() throws IOException
589 {
590 }
591
592 @Override
593 public void write(char[] cbuf, int off, int len) throws IOException
594 {
595 if ((off < 0) || (off > cbuf.length) || (len < 0)
596 || ((off + len) > cbuf.length) || ((off + len) < 0))
597 {
598 throw new IndexOutOfBoundsException();
599 }
600 else if (len == 0)
601 {
602 return;
603 }
604 buf.append(cbuf, off, len);
605 }
606
607 public StringBuilder getStringBuilder()
608 {
609 return buf;
610 }
611
612 public void flushToWriter(Writer writer) throws IOException
613 {
614 FacesContext facesContext = FacesContext.getCurrentInstance();
615 StateManager stateManager = facesContext.getApplication().getStateManager();
616
617 StringWriter stateWriter = new StringWriter();
618 ResponseWriter realWriter = facesContext.getResponseWriter();
619 facesContext.setResponseWriter(realWriter.cloneWithWriter(stateWriter));
620
621 Object serializedView = stateManager.saveView(facesContext);
622
623 stateManager.writeState(facesContext, serializedView);
624 facesContext.setResponseWriter(realWriter);
625
626 StringBuilder contentBuffer = getStringBuilder();
627 String state = stateWriter.getBuffer().toString();
628
629 ExternalContext extContext = facesContext.getExternalContext();
630 if (JavascriptUtils.isJavascriptAllowed(extContext) && MyfacesConfig.getCurrentInstance(extContext).isViewStateJavascript()) {
631
632 write(contentBuffer, 0, contentBuffer.length(), writer);
633 writer.write(state);
634 } else {
635
636 int lastFormMarkerPos = 0;
637 int formMarkerPos = 0;
638
639 while ((formMarkerPos = contentBuffer.indexOf(FORM_STATE_MARKER, formMarkerPos)) > -1)
640 {
641
642 write(contentBuffer, lastFormMarkerPos, formMarkerPos, writer);
643
644 writer.write(state);
645 formMarkerPos += FORM_STATE_MARKER_LEN;
646 lastFormMarkerPos = formMarkerPos;
647 }
648
649 if (lastFormMarkerPos < contentBuffer.length()) {
650 write(contentBuffer, lastFormMarkerPos, contentBuffer.length(), writer);
651 }
652 }
653
654 }
655
656
657
658
659
660
661
662
663
664
665
666 private void write(StringBuilder contentBuffer, int beginIndex, int endIndex, Writer writer) throws IOException {
667 int index = beginIndex;
668 int bufferSize = 2048;
669 char[] bufToWrite = new char[bufferSize];
670
671 while (index < endIndex)
672 {
673 int maxSize = Math.min(bufferSize, endIndex - index);
674
675 contentBuffer.getChars(index, index + maxSize, bufToWrite, 0);
676 writer.write(bufToWrite, 0, maxSize);
677
678 index += bufferSize;
679 }
680 }
681 }
682 }