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