001
014
015 package com.liferay.portal.layoutconfiguration.util;
016
017 import com.liferay.portal.kernel.executor.PortalExecutorManagerUtil;
018 import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
022 import com.liferay.portal.kernel.servlet.PipingServletResponse;
023 import com.liferay.portal.kernel.servlet.PluginContextListener;
024 import com.liferay.portal.kernel.servlet.ServletContextPool;
025 import com.liferay.portal.kernel.template.Template;
026 import com.liferay.portal.kernel.template.TemplateConstants;
027 import com.liferay.portal.kernel.template.TemplateContextType;
028 import com.liferay.portal.kernel.template.TemplateManagerUtil;
029 import com.liferay.portal.kernel.template.TemplateResource;
030 import com.liferay.portal.kernel.util.GetterUtil;
031 import com.liferay.portal.kernel.util.JavaConstants;
032 import com.liferay.portal.kernel.util.ObjectValuePair;
033 import com.liferay.portal.kernel.util.StringBundler;
034 import com.liferay.portal.kernel.util.StringPool;
035 import com.liferay.portal.kernel.util.StringUtil;
036 import com.liferay.portal.kernel.util.Validator;
037 import com.liferay.portal.layoutconfiguration.util.velocity.CustomizationSettingsProcessor;
038 import com.liferay.portal.layoutconfiguration.util.velocity.TemplateProcessor;
039 import com.liferay.portal.layoutconfiguration.util.xml.ActionURLLogic;
040 import com.liferay.portal.layoutconfiguration.util.xml.PortletLogic;
041 import com.liferay.portal.layoutconfiguration.util.xml.RenderURLLogic;
042 import com.liferay.portal.layoutconfiguration.util.xml.RuntimeLogic;
043 import com.liferay.portal.model.LayoutTemplate;
044 import com.liferay.portal.model.LayoutTemplateConstants;
045 import com.liferay.portal.model.Portlet;
046 import com.liferay.portal.model.PortletConstants;
047 import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
048 import com.liferay.portal.servlet.ThreadLocalFacadeServletRequestWrapperUtil;
049 import com.liferay.portal.util.ClassLoaderUtil;
050 import com.liferay.portal.util.PropsValues;
051 import com.liferay.portal.util.WebKeys;
052 import com.liferay.taglib.util.DummyVelocityTaglib;
053 import com.liferay.taglib.util.VelocityTaglib;
054 import com.liferay.taglib.util.VelocityTaglibImpl;
055
056 import java.io.Closeable;
057
058 import java.util.HashMap;
059 import java.util.List;
060 import java.util.Map;
061 import java.util.concurrent.Callable;
062 import java.util.concurrent.CancellationException;
063 import java.util.concurrent.ExecutionException;
064 import java.util.concurrent.ExecutorService;
065 import java.util.concurrent.Future;
066 import java.util.concurrent.FutureTask;
067 import java.util.concurrent.RejectedExecutionException;
068 import java.util.concurrent.TimeUnit;
069 import java.util.concurrent.TimeoutException;
070 import java.util.concurrent.locks.Lock;
071 import java.util.concurrent.locks.ReentrantLock;
072
073 import javax.portlet.PortletResponse;
074 import javax.portlet.RenderResponse;
075
076 import javax.servlet.ServletContext;
077 import javax.servlet.http.HttpServletRequest;
078 import javax.servlet.http.HttpServletResponse;
079 import javax.servlet.jsp.PageContext;
080
081 import org.apache.commons.lang.time.StopWatch;
082
083
088 @DoPrivileged
089 public class RuntimePageImpl implements RuntimePage {
090
091 public StringBundler getProcessedTemplate(
092 PageContext pageContext, String portletId,
093 TemplateResource templateResource)
094 throws Exception {
095
096 return doDispatch(pageContext, portletId, templateResource, true);
097 }
098
099 public void processCustomizationSettings(
100 PageContext pageContext, TemplateResource templateResource)
101 throws Exception {
102
103 doDispatch(pageContext, null, templateResource, false);
104 }
105
106 public void processTemplate(
107 PageContext pageContext, String portletId,
108 TemplateResource templateResource)
109 throws Exception {
110
111 StringBundler sb = doDispatch(
112 pageContext, portletId, templateResource, true);
113
114 sb.writeTo(pageContext.getOut());
115 }
116
117 public void processTemplate(
118 PageContext pageContext, TemplateResource templateResource)
119 throws Exception {
120
121 processTemplate(pageContext, null, templateResource);
122 }
123
124 public String processXML(
125 HttpServletRequest request, HttpServletResponse response,
126 String content)
127 throws Exception {
128
129 PortletResponse portletResponse = (PortletResponse)request.getAttribute(
130 JavaConstants.JAVAX_PORTLET_RESPONSE);
131
132 if (!(portletResponse instanceof RenderResponse)) {
133 throw new IllegalArgumentException(
134 "processXML can only be invoked in the render phase");
135 }
136
137 RenderResponse renderResponse = (RenderResponse)portletResponse;
138
139 RuntimeLogic portletLogic = new PortletLogic(request, response);
140 RuntimeLogic actionURLLogic = new ActionURLLogic(renderResponse);
141 RuntimeLogic renderURLLogic = new RenderURLLogic(renderResponse);
142
143 content = processXML(request, content, portletLogic);
144 content = processXML(request, content, actionURLLogic);
145 content = processXML(request, content, renderURLLogic);
146
147 return content;
148 }
149
150 public String processXML(
151 HttpServletRequest request, String content,
152 RuntimeLogic runtimeLogic)
153 throws Exception {
154
155 if (Validator.isNull(content)) {
156 return StringPool.BLANK;
157 }
158
159 int index = content.indexOf(runtimeLogic.getOpenTag());
160
161 if (index == -1) {
162 return content;
163 }
164
165 Portlet renderPortlet = (Portlet)request.getAttribute(
166 WebKeys.RENDER_PORTLET);
167
168 Boolean renderPortletResource = (Boolean)request.getAttribute(
169 WebKeys.RENDER_PORTLET_RESOURCE);
170
171 String outerPortletId = (String)request.getAttribute(
172 WebKeys.OUTER_PORTLET_ID);
173
174 if (outerPortletId == null) {
175 request.setAttribute(
176 WebKeys.OUTER_PORTLET_ID, renderPortlet.getPortletId());
177 }
178
179 try {
180 request.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
181
182 StringBundler sb = new StringBundler();
183
184 int x = 0;
185 int y = index;
186
187 while (y != -1) {
188 sb.append(content.substring(x, y));
189
190 int close1 = content.indexOf(runtimeLogic.getClose1Tag(), y);
191 int close2 = content.indexOf(runtimeLogic.getClose2Tag(), y);
192
193 if ((close2 == -1) || ((close1 != -1) && (close1 < close2))) {
194 x = close1 + runtimeLogic.getClose1Tag().length();
195 }
196 else {
197 x = close2 + runtimeLogic.getClose2Tag().length();
198 }
199
200 String runtimePortletTag = content.substring(y, x);
201
202 if ((renderPortlet != null) &&
203 runtimePortletTag.contains(renderPortlet.getPortletId())) {
204
205 return StringPool.BLANK;
206 }
207
208 sb.append(runtimeLogic.processXML(runtimePortletTag));
209
210 y = content.indexOf(runtimeLogic.getOpenTag(), x);
211 }
212
213 if (y == -1) {
214 sb.append(content.substring(x));
215 }
216
217 return sb.toString();
218 }
219 finally {
220 if (outerPortletId == null) {
221 request.removeAttribute(WebKeys.OUTER_PORTLET_ID);
222 }
223
224 request.setAttribute(WebKeys.RENDER_PORTLET, renderPortlet);
225
226 if (renderPortletResource == null) {
227 request.removeAttribute(WebKeys.RENDER_PORTLET_RESOURCE);
228 }
229 else {
230 request.setAttribute(
231 WebKeys.RENDER_PORTLET_RESOURCE, renderPortletResource);
232 }
233 }
234 }
235
236 protected StringBundler doDispatch(
237 PageContext pageContext, String portletId,
238 TemplateResource templateResource, boolean processTemplate)
239 throws Exception {
240
241 ClassLoader pluginClassLoader = null;
242
243 LayoutTemplate layoutTemplate = getLayoutTemplate(
244 templateResource.getTemplateId());
245
246 if (layoutTemplate != null) {
247 String pluginServletContextName = GetterUtil.getString(
248 layoutTemplate.getServletContextName());
249
250 ServletContext pluginServletContext = ServletContextPool.get(
251 pluginServletContextName);
252
253 if (pluginServletContext != null) {
254 pluginClassLoader =
255 (ClassLoader)pluginServletContext.getAttribute(
256 PluginContextListener.PLUGIN_CLASS_LOADER);
257 }
258 }
259
260 ClassLoader contextClassLoader =
261 ClassLoaderUtil.getContextClassLoader();
262
263 try {
264 if ((pluginClassLoader != null) &&
265 (pluginClassLoader != contextClassLoader)) {
266
267 ClassLoaderUtil.setContextClassLoader(pluginClassLoader);
268 }
269
270 if (processTemplate) {
271 return doProcessTemplate(
272 pageContext, portletId, templateResource,
273 TemplateContextType.STANDARD);
274 }
275 else {
276 doProcessCustomizationSettings(
277 pageContext, templateResource,
278 TemplateContextType.STANDARD);
279
280 return null;
281 }
282 }
283 finally {
284 if ((pluginClassLoader != null) &&
285 (pluginClassLoader != contextClassLoader)) {
286
287 ClassLoaderUtil.setContextClassLoader(contextClassLoader);
288 }
289 }
290 }
291
292 protected void doProcessCustomizationSettings(
293 PageContext pageContext, TemplateResource templateResource,
294 TemplateContextType templateContextType)
295 throws Exception {
296
297 HttpServletRequest request =
298 (HttpServletRequest)pageContext.getRequest();
299
300 CustomizationSettingsProcessor processor =
301 new CustomizationSettingsProcessor(pageContext);
302
303 Template template = TemplateManagerUtil.getTemplate(
304 TemplateConstants.LANG_TYPE_VM, templateResource,
305 templateContextType);
306
307 template.put("processor", processor);
308
309
310
311 template.prepare(request);
312
313
314
315 VelocityTaglib velocityTaglib = new DummyVelocityTaglib();
316
317 template.put("taglibLiferay", velocityTaglib);
318 template.put("theme", velocityTaglib);
319
320 try {
321 template.processTemplate(pageContext.getOut());
322 }
323 catch (Exception e) {
324 _log.error(e, e);
325
326 throw e;
327 }
328 }
329
330 protected StringBundler doProcessTemplate(
331 PageContext pageContext, String portletId,
332 TemplateResource templateResource,
333 TemplateContextType templateContextType)
334 throws Exception {
335
336 HttpServletRequest request =
337 (HttpServletRequest)pageContext.getRequest();
338 HttpServletResponse response =
339 (HttpServletResponse)pageContext.getResponse();
340
341 TemplateProcessor processor = new TemplateProcessor(
342 request, response, portletId);
343
344 Template template = TemplateManagerUtil.getTemplate(
345 TemplateConstants.LANG_TYPE_VM, templateResource,
346 templateContextType);
347
348 template.put("processor", processor);
349
350
351
352 template.prepare(request);
353
354
355
356 UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
357
358 VelocityTaglib velocityTaglib = new VelocityTaglibImpl(
359 pageContext.getServletContext(), request,
360 new PipingServletResponse(response, unsyncStringWriter),
361 pageContext, template);
362
363 template.put("taglibLiferay", velocityTaglib);
364 template.put("theme", velocityTaglib);
365
366 try {
367 template.processTemplate(unsyncStringWriter);
368 }
369 catch (Exception e) {
370 _log.error(e, e);
371
372 throw e;
373 }
374
375 boolean portletParallelRender = GetterUtil.getBoolean(
376 request.getAttribute(WebKeys.PORTLET_PARALLEL_RENDER));
377
378 Lock lock = null;
379
380 Map<String, StringBundler> contentsMap =
381 new HashMap<String, StringBundler>();
382
383 Map<Integer, List<PortletRenderer>> portletRenderersMap =
384 processor.getPortletRenderers();
385
386 for (Map.Entry<Integer, List<PortletRenderer>> entry :
387 portletRenderersMap.entrySet()) {
388
389 if (_log.isDebugEnabled()) {
390 _log.debug(
391 "Processing portlets with render weight " + entry.getKey());
392 }
393
394 List<PortletRenderer> portletRenderers = entry.getValue();
395
396 if (portletParallelRender && (portletRenderers.size() > 1)) {
397 StopWatch stopWatch = null;
398
399 if (_log.isDebugEnabled()) {
400 _log.debug("Start parallel rendering");
401
402 stopWatch = new StopWatch();
403
404 stopWatch.start();
405 }
406
407 if (lock == null) {
408 lock = new ReentrantLock();
409 }
410
411 request.setAttribute(
412 WebKeys.PARALLEL_RENDERING_MERGE_LOCK, lock);
413
414 ObjectValuePair<HttpServletRequest, Closeable> objectValuePair =
415 ThreadLocalFacadeServletRequestWrapperUtil.inject(request);
416
417 try {
418 parallelyRenderPortlets(
419 objectValuePair.getKey(), response, processor,
420 contentsMap, portletRenderers);
421 }
422 finally {
423 Closeable closeable = objectValuePair.getValue();
424
425 closeable.close();
426 }
427
428 request.removeAttribute(WebKeys.PARALLEL_RENDERING_MERGE_LOCK);
429
430 if (_log.isDebugEnabled()) {
431 _log.debug(
432 "Finished parallel rendering in " +
433 stopWatch.getTime() + " ms");
434 }
435 }
436 else {
437 StopWatch stopWatch = null;
438
439 if (_log.isDebugEnabled()) {
440 _log.debug("Start serial rendering");
441
442 stopWatch = new StopWatch();
443
444 stopWatch.start();
445 }
446
447 for (PortletRenderer portletRenderer : portletRenderers) {
448 Portlet portlet = portletRenderer.getPortlet();
449
450 contentsMap.put(
451 portlet.getPortletId(),
452 portletRenderer.render(request, response));
453
454 if (_log.isDebugEnabled()) {
455 _log.debug(
456 "Serially rendered portlet " +
457 portlet.getPortletId() + " in " +
458 stopWatch.getTime() + " ms");
459 }
460 }
461
462 if (_log.isDebugEnabled()) {
463 _log.debug(
464 "Finished serial rendering in " + stopWatch.getTime() +
465 " ms");
466 }
467 }
468 }
469
470 if (portletParallelRender && (_waitTime == Integer.MAX_VALUE)) {
471 _waitTime = PropsValues.LAYOUT_PARALLEL_RENDER_TIMEOUT;
472 }
473
474 StringBundler sb = StringUtil.replaceWithStringBundler(
475 unsyncStringWriter.toString(), "[$TEMPLATE_PORTLET_", "$]",
476 contentsMap);
477
478 return sb;
479 }
480
481 protected LayoutTemplate getLayoutTemplate(String velocityTemplateId) {
482 String separator = LayoutTemplateConstants.CUSTOM_SEPARATOR;
483 boolean standard = false;
484
485 if (velocityTemplateId.contains(
486 LayoutTemplateConstants.STANDARD_SEPARATOR)) {
487
488 separator = LayoutTemplateConstants.STANDARD_SEPARATOR;
489 standard = true;
490 }
491
492 String layoutTemplateId = null;
493
494 String themeId = null;
495
496 int pos = velocityTemplateId.indexOf(separator);
497
498 if (pos != -1) {
499 layoutTemplateId = velocityTemplateId.substring(
500 pos + separator.length());
501
502 themeId = velocityTemplateId.substring(0, pos);
503 }
504
505 pos = layoutTemplateId.indexOf(PortletConstants.INSTANCE_SEPARATOR);
506
507 if (pos != -1) {
508 layoutTemplateId = layoutTemplateId.substring(
509 pos + PortletConstants.INSTANCE_SEPARATOR.length() + 1);
510
511 pos = layoutTemplateId.indexOf(StringPool.UNDERLINE);
512
513 layoutTemplateId = layoutTemplateId.substring(pos + 1);
514 }
515
516 return LayoutTemplateLocalServiceUtil.getLayoutTemplate(
517 layoutTemplateId, standard, themeId);
518 }
519
520 protected void parallelyRenderPortlets(
521 HttpServletRequest request, HttpServletResponse response,
522 TemplateProcessor processor, Map<String, StringBundler> contentsMap,
523 List<PortletRenderer> portletRenderers)
524 throws Exception {
525
526 ExecutorService executorService =
527 PortalExecutorManagerUtil.getPortalExecutor(
528 RuntimePageImpl.class.getName());
529
530 Map<Future<StringBundler>, PortletRenderer> futures =
531 new HashMap<Future<StringBundler>, PortletRenderer>(
532 portletRenderers.size());
533
534 for (PortletRenderer portletRenderer : portletRenderers) {
535 if (_log.isDebugEnabled()) {
536 Portlet portlet = portletRenderer.getPortlet();
537
538 _log.debug(
539 "Submit portlet " + portlet.getPortletId() +
540 " for parallel rendering");
541 }
542
543 Callable<StringBundler> renderCallable =
544 portletRenderer.getCallable(request, response);
545
546 Future<StringBundler> future = null;
547
548 try {
549 future = executorService.submit(renderCallable);
550 }
551 catch (RejectedExecutionException ree) {
552
553
554
555
556
557
558
559
560 future = new FutureTask<StringBundler>(renderCallable);
561
562
563
564 future.cancel(true);
565 }
566
567 futures.put(future, portletRenderer);
568 }
569
570 long waitTime = _waitTime;
571
572 for (Map.Entry<Future<StringBundler>, PortletRenderer> entry :
573 futures.entrySet()) {
574
575 Future<StringBundler> future = entry.getKey();
576 PortletRenderer portletRenderer = entry.getValue();
577
578 Portlet portlet = portletRenderer.getPortlet();
579
580 if (future.isCancelled()) {
581 if (_log.isDebugEnabled()) {
582 _log.debug(
583 "Reject portlet " + portlet.getPortletId() +
584 " for parallel rendering");
585 }
586 }
587 else if ((waitTime > 0) || future.isDone()) {
588 try {
589 long startTime = System.currentTimeMillis();
590
591 StringBundler sb = future.get(
592 waitTime, TimeUnit.MILLISECONDS);
593
594 long duration = System.currentTimeMillis() - startTime;
595
596 waitTime -= duration;
597
598 contentsMap.put(portlet.getPortletId(), sb);
599
600 portletRenderer.finishParallelRender();
601
602 if (_log.isDebugEnabled()) {
603 _log.debug(
604 "Parallely rendered portlet " +
605 portlet.getPortletId() + " in " + duration +
606 " ms");
607 }
608
609 continue;
610 }
611 catch (ExecutionException ee) {
612 throw ee;
613 }
614 catch (InterruptedException ie) {
615
616
617
618
619 waitTime = -1;
620 }
621 catch (TimeoutException te) {
622
623
624
625
626 waitTime = -1;
627 }
628 catch (CancellationException ce) {
629
630
631
632
633 if (_log.isDebugEnabled()) {
634 _log.debug(
635 "Asynchronized cancellation detected that should " +
636 "only be caused by a concurrent shutdown of " +
637 "the thread pool",
638 ce);
639 }
640
641 return;
642 }
643
644
645
646 future.cancel(true);
647 }
648
649 StringBundler sb = null;
650
651 if (processor.isPortletAjaxRender() && portlet.isAjaxable()) {
652 if (_log.isDebugEnabled()) {
653 _log.debug(
654 "Fall back to ajax rendering of portlet " +
655 portlet.getPortletId());
656 }
657
658 sb = portletRenderer.renderAjax(request, response);
659 }
660 else {
661 if (_log.isDebugEnabled()) {
662 if (processor.isPortletAjaxRender()) {
663 _log.debug(
664 "Fall back to an error message for portlet " +
665 portlet.getPortletId() +
666 " since it is not ajaxable");
667 }
668 else {
669 _log.debug(
670 "Fall back to an error message for portlet " +
671 portlet.getPortletId() +
672 " since ajax rendering is disabled");
673 }
674 }
675
676 sb = portletRenderer.renderError(request, response);
677 }
678
679 contentsMap.put(portlet.getPortletId(), sb);
680
681 portletRenderer.finishParallelRender();
682 }
683 }
684
685 private static Log _log = LogFactoryUtil.getLog(RuntimePageImpl.class);
686
687 private int _waitTime = Integer.MAX_VALUE;
688
689 }