001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.portlet.bridges.mvc;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.portlet.LiferayPortlet;
020    import com.liferay.portal.kernel.servlet.SessionMessages;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.ParamUtil;
023    import com.liferay.portal.kernel.util.PortalUtil;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.StringUtil;
026    import com.liferay.portal.kernel.util.Validator;
027    import com.liferay.portal.kernel.util.WebKeys;
028    
029    import java.io.IOException;
030    
031    import java.util.List;
032    
033    import javax.portlet.ActionRequest;
034    import javax.portlet.ActionResponse;
035    import javax.portlet.EventRequest;
036    import javax.portlet.EventResponse;
037    import javax.portlet.PortletConfig;
038    import javax.portlet.PortletContext;
039    import javax.portlet.PortletException;
040    import javax.portlet.PortletPreferences;
041    import javax.portlet.PortletRequest;
042    import javax.portlet.PortletRequestDispatcher;
043    import javax.portlet.PortletResponse;
044    import javax.portlet.RenderRequest;
045    import javax.portlet.RenderResponse;
046    import javax.portlet.ResourceRequest;
047    import javax.portlet.ResourceResponse;
048    import javax.portlet.WindowState;
049    
050    import javax.servlet.http.HttpServletRequest;
051    
052    /**
053     * @author Brian Wing Shun Chan
054     * @author Raymond Aug??
055     */
056    public class MVCPortlet extends LiferayPortlet {
057    
058            @Override
059            public void destroy() {
060                    super.destroy();
061    
062                    _mvcActionCommandCache.close();
063                    _mvcRenderCommandCache.close();
064                    _mvcResourceCommandCache.close();
065            }
066    
067            @Override
068            public void doAbout(
069                            RenderRequest renderRequest, RenderResponse renderResponse)
070                    throws IOException, PortletException {
071    
072                    include(aboutTemplate, renderRequest, renderResponse);
073            }
074    
075            @Override
076            public void doConfig(
077                            RenderRequest renderRequest, RenderResponse renderResponse)
078                    throws IOException, PortletException {
079    
080                    include(configTemplate, renderRequest, renderResponse);
081            }
082    
083            @Override
084            public void doEdit(
085                            RenderRequest renderRequest, RenderResponse renderResponse)
086                    throws IOException, PortletException {
087    
088                    PortletPreferences portletPreferences = renderRequest.getPreferences();
089    
090                    if (portletPreferences == null) {
091                            super.doEdit(renderRequest, renderResponse);
092                    }
093                    else {
094                            include(editTemplate, renderRequest, renderResponse);
095                    }
096            }
097    
098            @Override
099            public void doEditDefaults(
100                            RenderRequest renderRequest, RenderResponse renderResponse)
101                    throws IOException, PortletException {
102    
103                    PortletPreferences portletPreferences = renderRequest.getPreferences();
104    
105                    if (portletPreferences == null) {
106                            super.doEdit(renderRequest, renderResponse);
107                    }
108                    else {
109                            include(editDefaultsTemplate, renderRequest, renderResponse);
110                    }
111            }
112    
113            @Override
114            public void doEditGuest(
115                            RenderRequest renderRequest, RenderResponse renderResponse)
116                    throws IOException, PortletException {
117    
118                    PortletPreferences portletPreferences = renderRequest.getPreferences();
119    
120                    if (portletPreferences == null) {
121                            super.doEdit(renderRequest, renderResponse);
122                    }
123                    else {
124                            include(editGuestTemplate, renderRequest, renderResponse);
125                    }
126            }
127    
128            @Override
129            public void doHelp(
130                            RenderRequest renderRequest, RenderResponse renderResponse)
131                    throws IOException, PortletException {
132    
133                    include(helpTemplate, renderRequest, renderResponse);
134            }
135    
136            @Override
137            public void doPreview(
138                            RenderRequest renderRequest, RenderResponse renderResponse)
139                    throws IOException, PortletException {
140    
141                    include(previewTemplate, renderRequest, renderResponse);
142            }
143    
144            @Override
145            public void doPrint(
146                            RenderRequest renderRequest, RenderResponse renderResponse)
147                    throws IOException, PortletException {
148    
149                    include(printTemplate, renderRequest, renderResponse);
150            }
151    
152            @Override
153            public void doView(
154                            RenderRequest renderRequest, RenderResponse renderResponse)
155                    throws IOException, PortletException {
156    
157                    include(viewTemplate, renderRequest, renderResponse);
158            }
159    
160            @Override
161            public void init() throws PortletException {
162                    super.init();
163    
164                    templatePath = _getInitParameter("template-path");
165    
166                    if (Validator.isNull(templatePath)) {
167                            templatePath = StringPool.SLASH;
168                    }
169                    else if (templatePath.contains(StringPool.BACK_SLASH) ||
170                                     templatePath.contains(StringPool.DOUBLE_SLASH) ||
171                                     templatePath.contains(StringPool.PERIOD) ||
172                                     templatePath.contains(StringPool.SPACE)) {
173    
174                            throw new PortletException(
175                                    "template-path " + templatePath + " has invalid characters");
176                    }
177                    else if (!templatePath.startsWith(StringPool.SLASH) ||
178                                     !templatePath.endsWith(StringPool.SLASH)) {
179    
180                            throw new PortletException(
181                                    "template-path " + templatePath +
182                                            " must start and end with a /");
183                    }
184    
185                    aboutTemplate = _getInitParameter("about-template");
186                    configTemplate = _getInitParameter("config-template");
187                    editTemplate = _getInitParameter("edit-template");
188                    editDefaultsTemplate = _getInitParameter("edit-defaults-template");
189                    editGuestTemplate = _getInitParameter("edit-guest-template");
190                    helpTemplate = _getInitParameter("help-template");
191                    previewTemplate = _getInitParameter("preview-template");
192                    printTemplate = _getInitParameter("print-template");
193                    viewTemplate = _getInitParameter("view-template");
194    
195                    clearRequestParameters = GetterUtil.getBoolean(
196                            getInitParameter("clear-request-parameters"));
197                    copyRequestParameters = GetterUtil.getBoolean(
198                            getInitParameter("copy-request-parameters"), true);
199    
200                    _mvcActionCommandCache = new MVCCommandCache(
201                            MVCActionCommand.EMPTY,
202                            getInitParameter("mvc-action-command-package-prefix"),
203                            getPortletName(), MVCActionCommand.class, "ActionCommand");
204                    _mvcRenderCommandCache = new MVCCommandCache(
205                            MVCRenderCommand.EMPTY,
206                            getInitParameter("mvc-render-command-package-prefix"),
207                            getPortletName(), MVCRenderCommand.class, "RenderCommand");
208                    _mvcResourceCommandCache = new MVCCommandCache(
209                            MVCResourceCommand.EMPTY,
210                            getInitParameter("mvc-resource-command-package-prefix"),
211                            getPortletName(), MVCResourceCommand.class, "ResourceCommand");
212    
213                    initValidPaths(templatePath, ".jsp");
214            }
215    
216            /**
217             * @deprecated As of 7.0.0, with no direct replacement
218             */
219            @Deprecated
220            public void invokeTaglibDiscussion(
221                            ActionRequest actionRequest, ActionResponse actionResponse)
222                    throws Exception {
223    
224                    PortletConfig portletConfig = getPortletConfig();
225    
226                    PortalUtil.invokeTaglibDiscussion(
227                            portletConfig, actionRequest, actionResponse);
228            }
229    
230            /**
231             * @deprecated As of 7.0.0, with no direct replacement
232             */
233            @Deprecated
234            public void invokeTaglibDiscussionPagination(
235                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
236                    throws IOException, PortletException {
237    
238                    PortletConfig portletConfig = getPortletConfig();
239    
240                    PortalUtil.invokeTaglibDiscussionPagination(
241                            portletConfig, resourceRequest, resourceResponse);
242            }
243    
244            @Override
245            public void processAction(
246                            ActionRequest actionRequest, ActionResponse actionResponse)
247                    throws IOException, PortletException {
248    
249                    super.processAction(actionRequest, actionResponse);
250    
251                    if (copyRequestParameters) {
252                            PortalUtil.copyRequestParameters(actionRequest, actionResponse);
253                    }
254            }
255    
256            @Override
257            public void render(
258                            RenderRequest renderRequest, RenderResponse renderResponse)
259                    throws IOException, PortletException {
260    
261                    invokeHideDefaultSuccessMessage(renderRequest);
262    
263                    String mvcRenderCommandName = ParamUtil.getString(
264                            renderRequest, "mvcRenderCommandName", "/");
265    
266                    String mvcPath = ParamUtil.getString(renderRequest, "mvcPath");
267    
268                    if (!mvcRenderCommandName.equals("/") || Validator.isNull(mvcPath)) {
269                            MVCRenderCommand mvcRenderCommand =
270                                    (MVCRenderCommand)_mvcRenderCommandCache.getMVCCommand(
271                                            mvcRenderCommandName);
272    
273                            mvcPath = null;
274    
275                            if (mvcRenderCommand != MVCRenderCommand.EMPTY) {
276                                    mvcPath = mvcRenderCommand.render(
277                                            renderRequest, renderResponse);
278                            }
279    
280                            if (MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH.equals(
281                                            mvcPath)) {
282    
283                                    return;
284                            }
285    
286                            renderRequest.setAttribute(
287                                    getMVCPathAttributeName(renderResponse.getNamespace()),
288                                    mvcPath);
289                    }
290    
291                    super.render(renderRequest, renderResponse);
292            }
293    
294            @Override
295            public void serveResource(
296                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
297                    throws IOException, PortletException {
298    
299                    invokeHideDefaultSuccessMessage(resourceRequest);
300    
301                    String path = getPath(resourceRequest, resourceResponse);
302    
303                    if (path != null) {
304                            include(
305                                    path, resourceRequest, resourceResponse,
306                                    PortletRequest.RESOURCE_PHASE);
307                    }
308    
309                    boolean invokeTaglibDiscussion = GetterUtil.getBoolean(
310                            resourceRequest.getParameter("invokeTaglibDiscussion"));
311    
312                    if (invokeTaglibDiscussion) {
313                            invokeTaglibDiscussionPagination(resourceRequest, resourceResponse);
314                    }
315                    else {
316                            super.serveResource(resourceRequest, resourceResponse);
317                    }
318            }
319    
320            @Override
321            protected boolean callActionMethod(
322                            ActionRequest actionRequest, ActionResponse actionResponse)
323                    throws PortletException {
324    
325                    try {
326                            checkPermissions(actionRequest);
327                    }
328                    catch (Exception e) {
329                            throw new PortletException(e);
330                    }
331    
332                    String[] actionNames = ParamUtil.getParameterValues(
333                            actionRequest, ActionRequest.ACTION_NAME);
334    
335                    String actionName = StringUtil.merge(actionNames);
336    
337                    if (!actionName.contains(StringPool.COMMA)) {
338                            MVCActionCommand mvcActionCommand =
339                                    (MVCActionCommand)_mvcActionCommandCache.getMVCCommand(
340                                            actionName);
341    
342                            if (mvcActionCommand != MVCActionCommand.EMPTY) {
343                                    if (mvcActionCommand instanceof FormMVCActionCommand) {
344                                            FormMVCActionCommand formMVCActionCommand =
345                                                    (FormMVCActionCommand)mvcActionCommand;
346    
347                                            if (!formMVCActionCommand.validateForm(
348                                                            actionRequest, actionResponse)) {
349    
350                                                    return false;
351                                            }
352                                    }
353    
354                                    return mvcActionCommand.processAction(
355                                            actionRequest, actionResponse);
356                            }
357                    }
358                    else {
359                            List<MVCActionCommand> mvcActionCommands =
360                                    (List<MVCActionCommand>)_mvcActionCommandCache.getMVCCommands(
361                                            actionName);
362    
363                            if (!mvcActionCommands.isEmpty()) {
364                                    boolean valid = true;
365    
366                                    for (MVCActionCommand mvcActionCommand : mvcActionCommands) {
367                                            if (mvcActionCommand instanceof FormMVCActionCommand) {
368                                                    FormMVCActionCommand formMVCActionCommand =
369                                                            (FormMVCActionCommand)mvcActionCommand;
370    
371                                                    valid &= formMVCActionCommand.validateForm(
372                                                            actionRequest, actionResponse);
373                                            }
374                                    }
375    
376                                    if (!valid) {
377                                            return false;
378                                    }
379    
380                                    for (MVCActionCommand mvcActionCommand : mvcActionCommands) {
381                                            if (!mvcActionCommand.processAction(
382                                                            actionRequest, actionResponse)) {
383    
384                                                    return false;
385                                            }
386                                    }
387    
388                                    return true;
389                            }
390                    }
391    
392                    return super.callActionMethod(actionRequest, actionResponse);
393            }
394    
395            @Override
396            protected boolean callResourceMethod(
397                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
398                    throws PortletException {
399    
400                    try {
401                            checkPermissions(resourceRequest);
402                    }
403                    catch (Exception e) {
404                            throw new PortletException(e);
405                    }
406    
407                    String resourceID = GetterUtil.getString(
408                            resourceRequest.getResourceID());
409    
410                    if (!resourceID.contains(StringPool.COMMA)) {
411                            MVCResourceCommand mvcResourceCommand =
412                                    (MVCResourceCommand)_mvcResourceCommandCache.getMVCCommand(
413                                            resourceID);
414    
415                            if (mvcResourceCommand != MVCResourceCommand.EMPTY) {
416                                    return mvcResourceCommand.serveResource(
417                                            resourceRequest, resourceResponse);
418                            }
419                    }
420                    else {
421                            List<MVCResourceCommand> mvcResourceCommands =
422                                    (List<MVCResourceCommand>)
423                                            _mvcResourceCommandCache.getMVCCommands(resourceID);
424    
425                            if (!mvcResourceCommands.isEmpty()) {
426                                    for (MVCResourceCommand mvcResourceCommand :
427                                                    mvcResourceCommands) {
428    
429                                            if (!mvcResourceCommand.serveResource(
430                                                            resourceRequest, resourceResponse)) {
431    
432                                                    return false;
433                                            }
434                                    }
435    
436                                    return true;
437                            }
438                    }
439    
440                    return super.callResourceMethod(resourceRequest, resourceResponse);
441            }
442    
443            protected void checkPermissions(PortletRequest portletRequest)
444                    throws Exception {
445            }
446    
447            @Override
448            protected void doDispatch(
449                            RenderRequest renderRequest, RenderResponse renderResponse)
450                    throws IOException, PortletException {
451    
452                    String path = getPath(renderRequest, renderResponse);
453    
454                    if (path != null) {
455                            if (!isProcessRenderRequest(renderRequest)) {
456                                    renderRequest.setAttribute(
457                                            WebKeys.PORTLET_DECORATE, Boolean.FALSE);
458    
459                                    return;
460                            }
461    
462                            WindowState windowState = renderRequest.getWindowState();
463    
464                            if (windowState.equals(WindowState.MINIMIZED)) {
465                                    return;
466                            }
467    
468                            include(path, renderRequest, renderResponse);
469                    }
470                    else {
471                            super.doDispatch(renderRequest, renderResponse);
472                    }
473            }
474    
475            protected String getMVCPathAttributeName(String namespace) {
476                    return namespace.concat(StringPool.PERIOD).concat(
477                            MVCRenderConstants.MVC_PATH_REQUEST_ATTRIBUTE_NAME);
478            }
479    
480            protected String getPath(
481                    PortletRequest portletRequest, PortletResponse portletResponse) {
482    
483                    String mvcPath = portletRequest.getParameter("mvcPath");
484    
485                    if (mvcPath == null) {
486                            mvcPath = (String)portletRequest.getAttribute(
487                                    getMVCPathAttributeName(portletResponse.getNamespace()));
488                    }
489    
490                    // Check deprecated parameter
491    
492                    if (mvcPath == null) {
493                            mvcPath = portletRequest.getParameter("jspPage");
494                    }
495    
496                    return mvcPath;
497            }
498    
499            protected void hideDefaultErrorMessage(PortletRequest portletRequest) {
500                    SessionMessages.add(
501                            portletRequest,
502                            PortalUtil.getPortletId(portletRequest) +
503                                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_ERROR_MESSAGE);
504            }
505    
506            protected void hideDefaultSuccessMessage(PortletRequest portletRequest) {
507                    SessionMessages.add(
508                            portletRequest,
509                            PortalUtil.getPortletId(portletRequest) +
510                                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_SUCCESS_MESSAGE);
511            }
512    
513            protected void include(
514                            String path, ActionRequest actionRequest,
515                            ActionResponse actionResponse)
516                    throws IOException, PortletException {
517    
518                    include(
519                            path, actionRequest, actionResponse, PortletRequest.ACTION_PHASE);
520            }
521    
522            protected void include(
523                            String path, EventRequest eventRequest, EventResponse eventResponse)
524                    throws IOException, PortletException {
525    
526                    include(path, eventRequest, eventResponse, PortletRequest.EVENT_PHASE);
527            }
528    
529            protected void include(
530                            String path, PortletRequest portletRequest,
531                            PortletResponse portletResponse, String lifecycle)
532                    throws IOException, PortletException {
533    
534                    HttpServletRequest httpServletRequest =
535                            PortalUtil.getHttpServletRequest(portletRequest);
536    
537                    PortletContext portletContext =
538                            (PortletContext)httpServletRequest.getAttribute(
539                                    MVCRenderConstants.
540                                            PORTLET_CONTEXT_OVERRIDE_REQUEST_ATTIBUTE_NAME_PREFIX +
541                                                    path);
542    
543                    if (portletContext == null) {
544                            portletContext = getPortletContext();
545                    }
546    
547                    PortletRequestDispatcher portletRequestDispatcher =
548                            portletContext.getRequestDispatcher(path);
549    
550                    if (portletRequestDispatcher == null) {
551                            _log.error(path + " is not a valid include");
552                    }
553                    else {
554                            checkPath(path);
555    
556                            portletRequestDispatcher.include(portletRequest, portletResponse);
557                    }
558    
559                    if (clearRequestParameters) {
560                            if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
561                                    portletResponse.setProperty(
562                                            "clear-request-parameters", Boolean.TRUE.toString());
563                            }
564                    }
565            }
566    
567            protected void include(
568                            String path, RenderRequest renderRequest,
569                            RenderResponse renderResponse)
570                    throws IOException, PortletException {
571    
572                    include(
573                            path, renderRequest, renderResponse, PortletRequest.RENDER_PHASE);
574            }
575    
576            protected void include(
577                            String path, ResourceRequest resourceRequest,
578                            ResourceResponse resourceResponse)
579                    throws IOException, PortletException {
580    
581                    include(
582                            path, resourceRequest, resourceResponse,
583                            PortletRequest.RESOURCE_PHASE);
584            }
585    
586            protected void invokeHideDefaultSuccessMessage(
587                    PortletRequest portletRequest) {
588    
589                    boolean hideDefaultSuccessMessage = ParamUtil.getBoolean(
590                            portletRequest, "hideDefaultSuccessMessage");
591    
592                    if (hideDefaultSuccessMessage) {
593                            hideDefaultSuccessMessage(portletRequest);
594                    }
595            }
596    
597            protected String aboutTemplate;
598            protected boolean clearRequestParameters;
599            protected String configTemplate;
600            protected boolean copyRequestParameters;
601            protected String editDefaultsTemplate;
602            protected String editGuestTemplate;
603            protected String editTemplate;
604            protected String helpTemplate;
605            protected String previewTemplate;
606            protected String printTemplate;
607            protected String templatePath;
608            protected String viewTemplate;
609    
610            private String _getInitParameter(String name) {
611                    String value = getInitParameter(name);
612    
613                    if (value != null) {
614                            return value;
615                    }
616    
617                    // Check deprecated parameter
618    
619                    if (name.equals("template-path")) {
620                            return getInitParameter("jsp-path");
621                    }
622                    else if (name.endsWith("-template")) {
623                            name = name.substring(0, name.length() - 9) + "-jsp";
624    
625                            return getInitParameter(name);
626                    }
627    
628                    return null;
629            }
630    
631            private static final Log _log = LogFactoryUtil.getLog(MVCPortlet.class);
632    
633            private MVCCommandCache _mvcActionCommandCache;
634            private MVCCommandCache _mvcRenderCommandCache;
635            private MVCCommandCache _mvcResourceCommandCache;
636    
637    }