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