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