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;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.language.LanguageUtil;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.model.Portlet;
022    import com.liferay.portal.kernel.model.PortletApp;
023    import com.liferay.portal.kernel.service.PortletLocalServiceUtil;
024    import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
025    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
026    import com.liferay.portal.kernel.servlet.SessionErrors;
027    import com.liferay.portal.kernel.servlet.SessionMessages;
028    import com.liferay.portal.kernel.theme.ThemeDisplay;
029    import com.liferay.portal.kernel.util.ContentTypes;
030    import com.liferay.portal.kernel.util.GetterUtil;
031    import com.liferay.portal.kernel.util.ParamUtil;
032    import com.liferay.portal.kernel.util.PortalUtil;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.kernel.util.WebKeys;
037    
038    import java.io.IOException;
039    
040    import java.lang.reflect.InvocationTargetException;
041    import java.lang.reflect.Method;
042    
043    import java.util.Arrays;
044    import java.util.HashSet;
045    import java.util.Map;
046    import java.util.ResourceBundle;
047    import java.util.Set;
048    import java.util.concurrent.ConcurrentHashMap;
049    
050    import javax.portlet.ActionRequest;
051    import javax.portlet.ActionResponse;
052    import javax.portlet.GenericPortlet;
053    import javax.portlet.MimeResponse;
054    import javax.portlet.PortletContext;
055    import javax.portlet.PortletException;
056    import javax.portlet.PortletMode;
057    import javax.portlet.PortletRequest;
058    import javax.portlet.RenderRequest;
059    import javax.portlet.RenderResponse;
060    import javax.portlet.ResourceRequest;
061    import javax.portlet.ResourceResponse;
062    import javax.portlet.WindowState;
063    
064    import javax.servlet.ServletContext;
065    import javax.servlet.http.HttpServletRequest;
066    import javax.servlet.http.HttpServletResponse;
067    
068    /**
069     * @author Brian Wing Shun Chan
070     */
071    public class LiferayPortlet extends GenericPortlet {
072    
073            @Override
074            public void init() throws PortletException {
075                    super.init();
076    
077                    addProcessActionSuccessMessage = GetterUtil.getBoolean(
078                            getInitParameter("add-process-action-success-action"), true);
079                    alwaysSendRedirect = GetterUtil.getBoolean(
080                            getInitParameter("always-send-redirect"));
081            }
082    
083            @Override
084            public void processAction(
085                            ActionRequest actionRequest, ActionResponse actionResponse)
086                    throws IOException, PortletException {
087    
088                    try {
089                            if (!isProcessActionRequest(actionRequest)) {
090                                    return;
091                            }
092    
093                            if (!callActionMethod(actionRequest, actionResponse)) {
094                                    return;
095                            }
096    
097                            if (!SessionErrors.isEmpty(actionRequest)) {
098                                    return;
099                            }
100    
101                            boolean emptySessionMessages = isEmptySessionMessages(
102                                    actionRequest);
103    
104                            if (emptySessionMessages) {
105                                    addSuccessMessage(actionRequest, actionResponse);
106                            }
107    
108                            if (!SessionMessages.contains(
109                                            actionRequest,
110                                            PortalUtil.getPortletId(actionRequest) +
111                                                    SessionMessages.KEY_SUFFIX_FORCE_SEND_REDIRECT)) {
112    
113                                    if (emptySessionMessages || isAlwaysSendRedirect()) {
114                                            sendRedirect(actionRequest, actionResponse);
115                                    }
116                            }
117                    }
118                    catch (PortletException pe) {
119                            Throwable cause = pe.getCause();
120    
121                            if (isSessionErrorException(cause)) {
122                                    SessionErrors.add(actionRequest, cause.getClass(), cause);
123                            }
124                            else {
125                                    throw pe;
126                            }
127                    }
128            }
129    
130            @Override
131            public void serveResource(
132                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
133                    throws IOException, PortletException {
134    
135                    if (!isProcessResourceRequest(resourceRequest)) {
136                            return;
137                    }
138    
139                    if (!callResourceMethod(resourceRequest, resourceResponse)) {
140                            return;
141                    }
142    
143                    if (!SessionErrors.isEmpty(resourceRequest)) {
144                            return;
145                    }
146    
147                    if (!SessionMessages.isEmpty(resourceRequest)) {
148                            return;
149                    }
150    
151                    super.serveResource(resourceRequest, resourceResponse);
152            }
153    
154            protected void addSuccessMessage(
155                    ActionRequest actionRequest, ActionResponse actionResponse) {
156    
157                    if (!addProcessActionSuccessMessage) {
158                            return;
159                    }
160    
161                    String successMessage = ParamUtil.getString(
162                            actionRequest, "successMessage");
163    
164                    SessionMessages.add(actionRequest, "requestProcessed", successMessage);
165            }
166    
167            protected boolean callActionMethod(
168                            ActionRequest actionRequest, ActionResponse actionResponse)
169                    throws PortletException {
170    
171                    String actionName = ParamUtil.getString(
172                            actionRequest, ActionRequest.ACTION_NAME);
173    
174                    if (Validator.isNull(actionName) ||
175                            actionName.equals("callActionMethod") ||
176                            actionName.equals("processAction")) {
177    
178                            return false;
179                    }
180    
181                    try {
182                            Method method = getActionMethod(actionName);
183    
184                            method.invoke(this, actionRequest, actionResponse);
185    
186                            return true;
187                    }
188                    catch (NoSuchMethodException nsme) {
189                            try {
190                                    super.processAction(actionRequest, actionResponse);
191    
192                                    return true;
193                            }
194                            catch (Exception e) {
195                                    throw new PortletException(e);
196                            }
197                    }
198                    catch (InvocationTargetException ite) {
199                            Throwable cause = ite.getCause();
200    
201                            if (cause != null) {
202                                    throw new PortletException(cause);
203                            }
204                            else {
205                                    throw new PortletException(ite);
206                            }
207                    }
208                    catch (Exception e) {
209                            throw new PortletException(e);
210                    }
211            }
212    
213            protected boolean callResourceMethod(
214                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
215                    throws PortletException {
216    
217                    String actionName = ParamUtil.getString(
218                            resourceRequest, ActionRequest.ACTION_NAME);
219    
220                    if (Validator.isNull(actionName) ||
221                            actionName.equals("callResourceMethod") ||
222                            actionName.equals("serveResource")) {
223    
224                            return false;
225                    }
226    
227                    try {
228                            Method method = getResourceMethod(actionName);
229    
230                            method.invoke(this, resourceRequest, resourceResponse);
231    
232                            return true;
233                    }
234                    catch (NoSuchMethodException nsme) {
235                            try {
236                                    super.serveResource(resourceRequest, resourceResponse);
237    
238                                    return true;
239                            }
240                            catch (Exception e) {
241                                    throw new PortletException(e);
242                            }
243                    }
244                    catch (InvocationTargetException ite) {
245                            Throwable cause = ite.getCause();
246    
247                            if (cause != null) {
248                                    throw new PortletException(cause);
249                            }
250                            else {
251                                    throw new PortletException(ite);
252                            }
253                    }
254                    catch (Exception e) {
255                            throw new PortletException(e);
256                    }
257            }
258    
259            protected void checkPath(String path) throws PortletException {
260                    if (Validator.isNotNull(path) && !isValidPath(path)) {
261                            throw new PortletException(
262                                    "Path " + path + " is not accessible by this portlet");
263                    }
264            }
265    
266            @SuppressWarnings("unused")
267            protected void doAbout(
268                            RenderRequest renderRequest, RenderResponse renderResponse)
269                    throws IOException, PortletException {
270    
271                    throw new PortletException("doAbout method not implemented");
272            }
273    
274            @SuppressWarnings("unused")
275            protected void doConfig(
276                            RenderRequest renderRequest, RenderResponse renderResponse)
277                    throws IOException, PortletException {
278    
279                    throw new PortletException("doConfig method not implemented");
280            }
281    
282            @Override
283            protected void doDispatch(
284                            RenderRequest renderRequest, RenderResponse renderResponse)
285                    throws IOException, PortletException {
286    
287                    if (!isProcessRenderRequest(renderRequest)) {
288                            renderRequest.setAttribute(WebKeys.PORTLET_DECORATE, Boolean.FALSE);
289    
290                            return;
291                    }
292    
293                    WindowState windowState = renderRequest.getWindowState();
294    
295                    if (windowState.equals(WindowState.MINIMIZED)) {
296                            return;
297                    }
298    
299                    PortletMode portletMode = renderRequest.getPortletMode();
300    
301                    if (portletMode.equals(PortletMode.VIEW)) {
302                            doView(renderRequest, renderResponse);
303                    }
304                    else if (portletMode.equals(LiferayPortletMode.ABOUT)) {
305                            doAbout(renderRequest, renderResponse);
306                    }
307                    else if (portletMode.equals(LiferayPortletMode.CONFIG)) {
308                            doConfig(renderRequest, renderResponse);
309                    }
310                    else if (portletMode.equals(PortletMode.EDIT)) {
311                            doEdit(renderRequest, renderResponse);
312                    }
313                    else if (portletMode.equals(LiferayPortletMode.EDIT_DEFAULTS)) {
314                            doEditDefaults(renderRequest, renderResponse);
315                    }
316                    else if (portletMode.equals(LiferayPortletMode.EDIT_GUEST)) {
317                            doEditGuest(renderRequest, renderResponse);
318                    }
319                    else if (portletMode.equals(PortletMode.HELP)) {
320                            doHelp(renderRequest, renderResponse);
321                    }
322                    else if (portletMode.equals(LiferayPortletMode.PREVIEW)) {
323                            doPreview(renderRequest, renderResponse);
324                    }
325                    else if (portletMode.equals(LiferayPortletMode.PRINT)) {
326                            doPrint(renderRequest, renderResponse);
327                    }
328                    else {
329                            throw new PortletException(portletMode.toString());
330                    }
331            }
332    
333            @SuppressWarnings("unused")
334            protected void doEditDefaults(
335                            RenderRequest renderRequest, RenderResponse renderResponse)
336                    throws IOException, PortletException {
337    
338                    throw new PortletException("doEditDefaults method not implemented");
339            }
340    
341            @SuppressWarnings("unused")
342            protected void doEditGuest(
343                            RenderRequest renderRequest, RenderResponse renderResponse)
344                    throws IOException, PortletException {
345    
346                    throw new PortletException("doEditGuest method not implemented");
347            }
348    
349            @SuppressWarnings("unused")
350            protected void doPreview(
351                            RenderRequest renderRequest, RenderResponse renderResponse)
352                    throws IOException, PortletException {
353    
354                    throw new PortletException("doPreview method not implemented");
355            }
356    
357            @SuppressWarnings("unused")
358            protected void doPrint(
359                            RenderRequest renderRequest, RenderResponse renderResponse)
360                    throws IOException, PortletException {
361    
362                    throw new PortletException("doPrint method not implemented");
363            }
364    
365            protected Method getActionMethod(String actionName)
366                    throws NoSuchMethodException {
367    
368                    Method method = _actionMethods.get(actionName);
369    
370                    if (method != null) {
371                            return method;
372                    }
373    
374                    Class<?> clazz = getClass();
375    
376                    method = clazz.getMethod(
377                            actionName, ActionRequest.class, ActionResponse.class);
378    
379                    _actionMethods.put(actionName, method);
380    
381                    return method;
382            }
383    
384            protected String getJSONContentType(PortletRequest portletRequest) {
385                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
386                            portletRequest);
387    
388                    if (BrowserSnifferUtil.isIe(request)) {
389                            return ContentTypes.TEXT_HTML;
390                    }
391    
392                    return ContentTypes.APPLICATION_JSON;
393            }
394    
395            protected Set<String> getPaths(String path, String extension) {
396                    Set<String> paths = new HashSet<>();
397    
398                    PortletContext portletContext = getPortletContext();
399    
400                    Set<String> childPaths = portletContext.getResourcePaths(path);
401    
402                    if (childPaths == null) {
403                            return paths;
404                    }
405    
406                    for (String childPath : childPaths) {
407                            if (childPath.endsWith(StringPool.SLASH)) {
408                                    paths.addAll(getPaths(childPath, extension));
409                            }
410                            else if (childPath.endsWith(extension)) {
411                                    paths.add(childPath);
412                            }
413                    }
414    
415                    return paths;
416            }
417    
418            protected String getRedirect(
419                    ActionRequest actionRequest, ActionResponse actionResponse) {
420    
421                    String redirect = (String)actionRequest.getAttribute(WebKeys.REDIRECT);
422    
423                    if (Validator.isBlank(redirect)) {
424                            redirect = ParamUtil.getString(actionRequest, "redirect");
425    
426                            if (!Validator.isBlank(redirect)) {
427                                    redirect = PortalUtil.escapeRedirect(redirect);
428                            }
429                    }
430    
431                    return redirect;
432            }
433    
434            protected Method getResourceMethod(String actionName)
435                    throws NoSuchMethodException {
436    
437                    Method method = _resourceMethods.get(actionName);
438    
439                    if (method != null) {
440                            return method;
441                    }
442    
443                    Class<?> clazz = getClass();
444    
445                    method = clazz.getMethod(
446                            actionName, ResourceRequest.class, ResourceResponse.class);
447    
448                    _resourceMethods.put(actionName, method);
449    
450                    return method;
451            }
452    
453            protected ServletContext getServletContext() {
454                    LiferayPortletConfig liferayPortletConfig =
455                            (LiferayPortletConfig)getPortletConfig();
456    
457                    Portlet portlet = liferayPortletConfig.getPortlet();
458    
459                    PortletApp portletApp = portlet.getPortletApp();
460    
461                    return portletApp.getServletContext();
462            }
463    
464            @Override
465            protected String getTitle(RenderRequest renderRequest) {
466                    try {
467                            return PortalUtil.getPortletTitle(renderRequest);
468                    }
469                    catch (Exception e) {
470                            return super.getTitle(renderRequest);
471                    }
472            }
473    
474            protected void initValidPaths(String rootPath, String extension) {
475                    if (rootPath.equals(StringPool.SLASH)) {
476                            PortletContext portletContext = getPortletContext();
477    
478                            PortletApp portletApp = PortletLocalServiceUtil.getPortletApp(
479                                    portletContext.getPortletContextName());
480    
481                            if (!portletApp.isWARFile()) {
482                                    _log.error(
483                                            "Disabling paths for portlet " + getPortletName() +
484                                                    " because root path is configured to have access to " +
485                                                            "all portal paths");
486    
487                                    validPaths = new HashSet<>();
488    
489                                    return;
490                            }
491                    }
492    
493                    validPaths = getPaths(rootPath, extension);
494    
495                    validPaths.addAll(
496                            getPaths(_PATH_META_INF_RESOURCES + rootPath, extension));
497    
498                    validPaths.addAll(
499                            Arrays.asList(StringUtil.split(getInitParameter("valid-paths"))));
500            }
501    
502            protected boolean isAlwaysSendRedirect() {
503                    return alwaysSendRedirect;
504            }
505    
506            protected boolean isEmptySessionMessages(ActionRequest actionRequest) {
507                    if (SessionMessages.isEmpty(actionRequest)) {
508                            return true;
509                    }
510    
511                    int sessionMessagesSize = SessionMessages.size(actionRequest);
512    
513                    String portletId = PortalUtil.getPortletId(actionRequest);
514    
515                    for (String suffix : _IGNORED_SESSION_MESSAGE_SUFFIXES) {
516                            if (SessionMessages.contains(actionRequest, portletId + suffix)) {
517                                    sessionMessagesSize--;
518                            }
519                    }
520    
521                    if (sessionMessagesSize == 0) {
522                            return true;
523                    }
524    
525                    return false;
526            }
527    
528            protected boolean isProcessActionRequest(ActionRequest actionRequest) {
529                    return isProcessPortletRequest(actionRequest);
530            }
531    
532            protected boolean isProcessPortletRequest(PortletRequest portletRequest) {
533                    return _PROCESS_PORTLET_REQUEST;
534            }
535    
536            protected boolean isProcessRenderRequest(RenderRequest renderRequest) {
537                    return isProcessPortletRequest(renderRequest);
538            }
539    
540            protected boolean isProcessResourceRequest(
541                    ResourceRequest resourceRequest) {
542    
543                    return isProcessPortletRequest(resourceRequest);
544            }
545    
546            protected boolean isSessionErrorException(Throwable cause) {
547                    if (_log.isDebugEnabled()) {
548                            _log.debug(cause, cause);
549                    }
550    
551                    if (cause instanceof PortalException) {
552                            return true;
553                    }
554    
555                    return false;
556            }
557    
558            protected boolean isValidPath(String path) {
559                    if (validPaths.contains(path) ||
560                            validPaths.contains(_PATH_META_INF_RESOURCES + path)) {
561    
562                            return true;
563                    }
564    
565                    return false;
566            }
567    
568            protected void sendRedirect(
569                            ActionRequest actionRequest, ActionResponse actionResponse)
570                    throws IOException {
571    
572                    String redirect = getRedirect(actionRequest, actionResponse);
573    
574                    if (Validator.isNotNull(redirect)) {
575                            actionResponse.sendRedirect(redirect);
576                    }
577            }
578    
579            protected String translate(PortletRequest portletRequest, String key) {
580                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
581                            WebKeys.THEME_DISPLAY);
582    
583                    ResourceBundle resourceBundle = getResourceBundle(
584                            themeDisplay.getLocale());
585    
586                    return LanguageUtil.get(resourceBundle, key);
587            }
588    
589            protected String translate(
590                    PortletRequest portletRequest, String key, Object... arguments) {
591    
592                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
593                            WebKeys.THEME_DISPLAY);
594    
595                    ResourceBundle resourceBundle = getResourceBundle(
596                            themeDisplay.getLocale());
597    
598                    return LanguageUtil.format(resourceBundle, key, arguments);
599            }
600    
601            protected void writeJSON(
602                            PortletRequest portletRequest, ActionResponse actionResponse,
603                            Object json)
604                    throws IOException {
605    
606                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
607                            actionResponse);
608    
609                    response.setContentType(getJSONContentType(portletRequest));
610    
611                    ServletResponseUtil.write(response, json.toString());
612    
613                    response.flushBuffer();
614            }
615    
616            protected void writeJSON(
617                            PortletRequest portletRequest, MimeResponse mimeResponse,
618                            Object json)
619                    throws IOException {
620    
621                    mimeResponse.setContentType(getJSONContentType(portletRequest));
622    
623                    PortletResponseUtil.write(mimeResponse, json.toString());
624    
625                    mimeResponse.flushBuffer();
626            }
627    
628            protected boolean addProcessActionSuccessMessage;
629            protected boolean alwaysSendRedirect;
630            protected Set<String> validPaths;
631    
632            private static final String[] _IGNORED_SESSION_MESSAGE_SUFFIXES = {
633                    SessionMessages.KEY_SUFFIX_DELETE_SUCCESS_DATA,
634                    SessionMessages.KEY_SUFFIX_FORCE_SEND_REDIRECT,
635                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_ERROR_MESSAGE,
636                    SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_SUCCESS_MESSAGE,
637                    SessionMessages.KEY_SUFFIX_REFRESH_PORTLET
638            };
639    
640            private static final String _PATH_META_INF_RESOURCES =
641                    "/META-INF/resources";
642    
643            private static final boolean _PROCESS_PORTLET_REQUEST = true;
644    
645            private static final Log _log = LogFactoryUtil.getLog(LiferayPortlet.class);
646    
647            private final Map<String, Method> _actionMethods =
648                    new ConcurrentHashMap<>();
649            private final Map<String, Method> _resourceMethods =
650                    new ConcurrentHashMap<>();
651    
652    }