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