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