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.getName(),
204                            "ActionCommand");
205                    _mvcRenderCommandCache = new MVCCommandCache(
206                            MVCRenderCommand.EMPTY,
207                            getInitParameter("mvc-render-command-package-prefix"),
208                            getPortletName(), MVCRenderCommand.class.getName(),
209                            "RenderCommand");
210                    _mvcResourceCommandCache = new MVCCommandCache(
211                            MVCResourceCommand.EMPTY,
212                            getInitParameter("mvc-resource-command-package-prefix"),
213                            getPortletName(), MVCResourceCommand.class.getName(),
214                            "ResourceCommand");
215            }
216    
217            /**
218             * @deprecated As of 7.0.0, with no direct replacement
219             */
220            @Deprecated
221            public void invokeTaglibDiscussion(
222                            ActionRequest actionRequest, ActionResponse actionResponse)
223                    throws Exception {
224    
225                    PortletConfig portletConfig = getPortletConfig();
226    
227                    PortalUtil.invokeTaglibDiscussion(
228                            portletConfig, actionRequest, actionResponse);
229            }
230    
231            /**
232             * @deprecated As of 7.0.0, with no direct replacement
233             */
234            @Deprecated
235            public void invokeTaglibDiscussionPagination(
236                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
237                    throws IOException, PortletException {
238    
239                    PortletConfig portletConfig = getPortletConfig();
240    
241                    PortalUtil.invokeTaglibDiscussionPagination(
242                            portletConfig, resourceRequest, resourceResponse);
243            }
244    
245            @Override
246            public void processAction(
247                            ActionRequest actionRequest, ActionResponse actionResponse)
248                    throws IOException, PortletException {
249    
250                    super.processAction(actionRequest, actionResponse);
251    
252                    if (copyRequestParameters) {
253                            PortalUtil.copyRequestParameters(actionRequest, actionResponse);
254                    }
255            }
256    
257            @Override
258            public void render(
259                            RenderRequest renderRequest, RenderResponse renderResponse)
260                    throws IOException, PortletException {
261    
262                    invokeHideDefaultSuccessMessage(renderRequest);
263    
264                    String mvcRenderCommandName = ParamUtil.getString(
265                            renderRequest, "mvcRenderCommandName", "/");
266    
267                    String mvcPath = ParamUtil.getString(renderRequest, "mvcPath");
268    
269                    if (!mvcRenderCommandName.equals("/") || Validator.isNull(mvcPath)) {
270                            MVCRenderCommand mvcRenderCommand =
271                                    (MVCRenderCommand)_mvcRenderCommandCache.getMVCCommand(
272                                            mvcRenderCommandName);
273    
274                            mvcPath = null;
275    
276                            if (mvcRenderCommand != MVCRenderCommand.EMPTY) {
277                                    mvcPath = mvcRenderCommand.render(
278                                            renderRequest, renderResponse);
279                            }
280    
281                            if (MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH.equals(
282                                            mvcPath)) {
283    
284                                    return;
285                            }
286    
287                            renderRequest.setAttribute(
288                                    getMVCPathAttributeName(renderResponse.getNamespace()),
289                                    mvcPath);
290                    }
291    
292                    super.render(renderRequest, renderResponse);
293            }
294    
295            @Override
296            public void serveResource(
297                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
298                    throws IOException, PortletException {
299    
300                    invokeHideDefaultSuccessMessage(resourceRequest);
301    
302                    String path = getPath(resourceRequest, resourceResponse);
303    
304                    if (path != null) {
305                            include(
306                                    path, resourceRequest, resourceResponse,
307                                    PortletRequest.RESOURCE_PHASE);
308                    }
309    
310                    boolean invokeTaglibDiscussion = GetterUtil.getBoolean(
311                            resourceRequest.getParameter("invokeTaglibDiscussion"));
312    
313                    if (invokeTaglibDiscussion) {
314                            invokeTaglibDiscussionPagination(resourceRequest, resourceResponse);
315                    }
316                    else {
317                            super.serveResource(resourceRequest, resourceResponse);
318                    }
319            }
320    
321            @Override
322            protected boolean callActionMethod(
323                            ActionRequest actionRequest, ActionResponse actionResponse)
324                    throws PortletException {
325    
326                    try {
327                            checkPermissions(actionRequest);
328                    }
329                    catch (Exception e) {
330                            throw new PortletException(e);
331                    }
332    
333                    String[] actionNames = ParamUtil.getParameterValues(
334                            actionRequest, ActionRequest.ACTION_NAME);
335    
336                    String actionName = StringUtil.merge(actionNames);
337    
338                    if (!actionName.contains(StringPool.COMMA)) {
339                            MVCActionCommand mvcActionCommand =
340                                    (MVCActionCommand)_mvcActionCommandCache.getMVCCommand(
341                                            actionName);
342    
343                            if (mvcActionCommand != MVCActionCommand.EMPTY) {
344                                    if (mvcActionCommand instanceof FormMVCActionCommand) {
345                                            FormMVCActionCommand formMVCActionCommand =
346                                                    (FormMVCActionCommand)mvcActionCommand;
347    
348                                            if (!formMVCActionCommand.validateForm(
349                                                            actionRequest, actionResponse)) {
350    
351                                                    return false;
352                                            }
353                                    }
354    
355                                    return mvcActionCommand.processAction(
356                                            actionRequest, actionResponse);
357                            }
358                    }
359                    else {
360                            List<MVCActionCommand> mvcActionCommands =
361                                    (List<MVCActionCommand>)_mvcActionCommandCache.getMVCCommands(
362                                            actionName);
363    
364                            if (!mvcActionCommands.isEmpty()) {
365                                    boolean valid = true;
366    
367                                    for (MVCActionCommand mvcActionCommand : mvcActionCommands) {
368                                            if (mvcActionCommand instanceof FormMVCActionCommand) {
369                                                    FormMVCActionCommand formMVCActionCommand =
370                                                            (FormMVCActionCommand)mvcActionCommand;
371    
372                                                    valid &= formMVCActionCommand.validateForm(
373                                                            actionRequest, actionResponse);
374                                            }
375                                    }
376    
377                                    if (!valid) {
378                                            return false;
379                                    }
380    
381                                    for (MVCActionCommand mvcActionCommand : mvcActionCommands) {
382                                            if (!mvcActionCommand.processAction(
383                                                            actionRequest, actionResponse)) {
384    
385                                                    return false;
386                                            }
387                                    }
388    
389                                    return true;
390                            }
391                    }
392    
393                    return super.callActionMethod(actionRequest, actionResponse);
394            }
395    
396            @Override
397            protected boolean callResourceMethod(
398                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
399                    throws PortletException {
400    
401                    try {
402                            checkPermissions(resourceRequest);
403                    }
404                    catch (Exception e) {
405                            throw new PortletException(e);
406                    }
407    
408                    String resourceID = GetterUtil.getString(
409                            resourceRequest.getResourceID());
410    
411                    if (!resourceID.contains(StringPool.COMMA)) {
412                            MVCResourceCommand mvcResourceCommand =
413                                    (MVCResourceCommand)_mvcResourceCommandCache.getMVCCommand(
414                                            resourceID);
415    
416                            if (mvcResourceCommand != MVCResourceCommand.EMPTY) {
417                                    return mvcResourceCommand.serveResource(
418                                            resourceRequest, resourceResponse);
419                            }
420                    }
421                    else {
422                            List<MVCResourceCommand> mvcResourceCommands =
423                                    (List<MVCResourceCommand>)
424                                            _mvcResourceCommandCache.getMVCCommands(resourceID);
425    
426                            if (!mvcResourceCommands.isEmpty()) {
427                                    for (MVCResourceCommand mvcResourceCommand :
428                                                    mvcResourceCommands) {
429    
430                                            if (!mvcResourceCommand.serveResource(
431                                                            resourceRequest, resourceResponse)) {
432    
433                                                    return false;
434                                            }
435                                    }
436    
437                                    return true;
438                            }
439                    }
440    
441                    return super.callResourceMethod(resourceRequest, resourceResponse);
442            }
443    
444            protected void checkPath(String path) throws PortletException {
445                    if (Validator.isNotNull(path) &&
446                            (!path.startsWith(templatePath) ||
447                             !PortalUtil.isValidResourceId(path) ||
448                             !Validator.isFilePath(path, false))) {
449    
450                            throw new PortletException(
451                                    "Path " + path + " is not accessible by this portlet");
452                    }
453            }
454    
455            protected void checkPermissions(PortletRequest portletRequest)
456                    throws Exception {
457            }
458    
459            @Override
460            protected void doDispatch(
461                            RenderRequest renderRequest, RenderResponse renderResponse)
462                    throws IOException, PortletException {
463    
464                    String path = getPath(renderRequest, renderResponse);
465    
466                    if (path != null) {
467                            if (!isProcessRenderRequest(renderRequest)) {
468                                    renderRequest.setAttribute(
469                                            WebKeys.PORTLET_DECORATE, Boolean.FALSE);
470    
471                                    return;
472                            }
473    
474                            WindowState windowState = renderRequest.getWindowState();
475    
476                            if (windowState.equals(WindowState.MINIMIZED)) {
477                                    return;
478                            }
479    
480                            include(path, renderRequest, renderResponse);
481                    }
482                    else {
483                            super.doDispatch(renderRequest, renderResponse);
484                    }
485            }
486    
487            protected String getMVCPathAttributeName(String namespace) {
488                    return namespace.concat(StringPool.PERIOD).concat(
489                            MVCRenderConstants.MVC_PATH_REQUEST_ATTRIBUTE_NAME);
490            }
491    
492            protected String getPath(
493                    PortletRequest portletRequest, PortletResponse portletResponse) {
494    
495                    String mvcPath = portletRequest.getParameter("mvcPath");
496    
497                    if (mvcPath == null) {
498                            mvcPath = (String)portletRequest.getAttribute(
499                                    getMVCPathAttributeName(portletResponse.getNamespace()));
500                    }
501    
502                    // Check deprecated parameter
503    
504                    if (mvcPath == null) {
505                            mvcPath = portletRequest.getParameter("jspPage");
506                    }
507    
508                    return mvcPath;
509            }
510    
511            protected void hideDefaultErrorMessage(PortletRequest portletRequest) {
512                    SessionMessages.add(
513                            portletRequest,
514                            PortalUtil.getPortletId(portletRequest) +
515                                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_ERROR_MESSAGE);
516            }
517    
518            protected void hideDefaultSuccessMessage(PortletRequest portletRequest) {
519                    SessionMessages.add(
520                            portletRequest,
521                            PortalUtil.getPortletId(portletRequest) +
522                                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_SUCCESS_MESSAGE);
523            }
524    
525            protected void include(
526                            String path, ActionRequest actionRequest,
527                            ActionResponse actionResponse)
528                    throws IOException, PortletException {
529    
530                    include(
531                            path, actionRequest, actionResponse, PortletRequest.ACTION_PHASE);
532            }
533    
534            protected void include(
535                            String path, EventRequest eventRequest, EventResponse eventResponse)
536                    throws IOException, PortletException {
537    
538                    include(path, eventRequest, eventResponse, PortletRequest.EVENT_PHASE);
539            }
540    
541            protected void include(
542                            String path, PortletRequest portletRequest,
543                            PortletResponse portletResponse, String lifecycle)
544                    throws IOException, PortletException {
545    
546                    HttpServletRequest httpServletRequest =
547                            PortalUtil.getHttpServletRequest(portletRequest);
548    
549                    PortletContext portletContext =
550                            (PortletContext)httpServletRequest.getAttribute(
551                                    MVCRenderConstants.
552                                            PORTLET_CONTEXT_OVERRIDE_REQUEST_ATTIBUTE_NAME_PREFIX +
553                                                    path);
554    
555                    if (portletContext == null) {
556                            portletContext = getPortletContext();
557                    }
558    
559                    PortletRequestDispatcher portletRequestDispatcher =
560                            portletContext.getRequestDispatcher(path);
561    
562                    if (portletRequestDispatcher == null) {
563                            _log.error(path + " is not a valid include");
564                    }
565                    else {
566                            checkPath(path);
567    
568                            portletRequestDispatcher.include(portletRequest, portletResponse);
569                    }
570    
571                    if (clearRequestParameters) {
572                            if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
573                                    portletResponse.setProperty(
574                                            "clear-request-parameters", Boolean.TRUE.toString());
575                            }
576                    }
577            }
578    
579            protected void include(
580                            String path, RenderRequest renderRequest,
581                            RenderResponse renderResponse)
582                    throws IOException, PortletException {
583    
584                    include(
585                            path, renderRequest, renderResponse, PortletRequest.RENDER_PHASE);
586            }
587    
588            protected void include(
589                            String path, ResourceRequest resourceRequest,
590                            ResourceResponse resourceResponse)
591                    throws IOException, PortletException {
592    
593                    include(
594                            path, resourceRequest, resourceResponse,
595                            PortletRequest.RESOURCE_PHASE);
596            }
597    
598            protected void invokeHideDefaultSuccessMessage(
599                    PortletRequest portletRequest) {
600    
601                    boolean hideDefaultSuccessMessage = ParamUtil.getBoolean(
602                            portletRequest, "hideDefaultSuccessMessage");
603    
604                    if (hideDefaultSuccessMessage) {
605                            hideDefaultSuccessMessage(portletRequest);
606                    }
607            }
608    
609            protected String aboutTemplate;
610            protected boolean clearRequestParameters;
611            protected String configTemplate;
612            protected boolean copyRequestParameters;
613            protected String editDefaultsTemplate;
614            protected String editGuestTemplate;
615            protected String editTemplate;
616            protected String helpTemplate;
617            protected String previewTemplate;
618            protected String printTemplate;
619            protected String templatePath;
620            protected String viewTemplate;
621    
622            private String _getInitParameter(String name) {
623                    String value = getInitParameter(name);
624    
625                    if (value != null) {
626                            return value;
627                    }
628    
629                    // Check deprecated parameter
630    
631                    if (name.equals("template-path")) {
632                            return getInitParameter("jsp-path");
633                    }
634                    else if (name.endsWith("-template")) {
635                            name = name.substring(0, name.length() - 9) + "-jsp";
636    
637                            return getInitParameter(name);
638                    }
639    
640                    return null;
641            }
642    
643            private static final Log _log = LogFactoryUtil.getLog(MVCPortlet.class);
644    
645            private MVCCommandCache _mvcActionCommandCache;
646            private MVCCommandCache _mvcRenderCommandCache;
647            private MVCCommandCache _mvcResourceCommandCache;
648    
649    }