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