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