/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.portlet; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Portlet; import com.liferay.portal.kernel.model.PortletApp; import com.liferay.portal.kernel.service.PortletLocalServiceUtil; import com.liferay.portal.kernel.servlet.BrowserSnifferUtil; import com.liferay.portal.kernel.servlet.ServletResponseUtil; import com.liferay.portal.kernel.servlet.SessionErrors; import com.liferay.portal.kernel.servlet.SessionMessages; import com.liferay.portal.kernel.theme.ThemeDisplay; import com.liferay.portal.kernel.util.ContentTypes; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.MimeResponse; import javax.portlet.PortletContext; import javax.portlet.PortletException; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; import javax.portlet.WindowState; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Brian Wing Shun Chan */ public class LiferayPortlet extends GenericPortlet { @Override public void init() throws PortletException { super.init(); addProcessActionSuccessMessage = GetterUtil.getBoolean( getInitParameter("add-process-action-success-action"), true); alwaysSendRedirect = GetterUtil.getBoolean( getInitParameter("always-send-redirect")); } @Override public void processAction( ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException { try { if (!isProcessActionRequest(actionRequest)) { return; } if (!callActionMethod(actionRequest, actionResponse)) { return; } if (!SessionErrors.isEmpty(actionRequest)) { return; } String portletId = PortalUtil.getPortletId(actionRequest); if (!SessionMessages.contains( actionRequest, portletId.concat( SessionMessages.KEY_SUFFIX_FORCE_SEND_REDIRECT))) { if (isEmptySessionMessages(actionRequest) || isAlwaysSendRedirect()) { sendRedirect(actionRequest, actionResponse); } } if (isAddSuccessMessage(actionRequest)) { addSuccessMessage(actionRequest, actionResponse); } } catch (PortletException pe) { Throwable cause = pe.getCause(); if (isSessionErrorException(cause)) { SessionErrors.add(actionRequest, cause.getClass(), cause); } else { throw pe; } } } @Override public void serveResource( ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws IOException, PortletException { if (!isProcessResourceRequest(resourceRequest)) { return; } if (!callResourceMethod(resourceRequest, resourceResponse)) { return; } if (!SessionErrors.isEmpty(resourceRequest)) { return; } if (!SessionMessages.isEmpty(resourceRequest)) { return; } super.serveResource(resourceRequest, resourceResponse); } protected void addSuccessMessage( ActionRequest actionRequest, ActionResponse actionResponse) { if (!addProcessActionSuccessMessage) { return; } String successMessage = ParamUtil.getString( actionRequest, "successMessage"); SessionMessages.add(actionRequest, "requestProcessed", successMessage); } protected boolean callActionMethod( ActionRequest actionRequest, ActionResponse actionResponse) throws PortletException { String actionName = ParamUtil.getString( actionRequest, ActionRequest.ACTION_NAME); if (Validator.isNull(actionName) || actionName.equals("callActionMethod") || actionName.equals("processAction")) { return false; } try { Method method = getActionMethod(actionName); method.invoke(this, actionRequest, actionResponse); return true; } catch (NoSuchMethodException nsme) { try { super.processAction(actionRequest, actionResponse); return true; } catch (Exception e) { throw new PortletException(e); } } catch (InvocationTargetException ite) { Throwable cause = ite.getCause(); if (cause != null) { throw new PortletException(cause); } else { throw new PortletException(ite); } } catch (Exception e) { throw new PortletException(e); } } protected boolean callResourceMethod( ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws PortletException { String actionName = ParamUtil.getString( resourceRequest, ActionRequest.ACTION_NAME); if (Validator.isNull(actionName) || actionName.equals("callResourceMethod") || actionName.equals("serveResource")) { return false; } try { Method method = getResourceMethod(actionName); method.invoke(this, resourceRequest, resourceResponse); return true; } catch (NoSuchMethodException nsme) { try { super.serveResource(resourceRequest, resourceResponse); return true; } catch (Exception e) { throw new PortletException(e); } } catch (InvocationTargetException ite) { Throwable cause = ite.getCause(); if (cause != null) { throw new PortletException(cause); } else { throw new PortletException(ite); } } catch (Exception e) { throw new PortletException(e); } } protected void checkPath(String path) throws PortletException { if (Validator.isNotNull(path) && !isValidPath(path)) { throw new PortletException( "Path " + path + " is not accessible by this portlet"); } } @SuppressWarnings("unused") protected void doAbout( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doAbout method not implemented"); } @SuppressWarnings("unused") protected void doConfig( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doConfig method not implemented"); } @Override protected void doDispatch( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { if (!isProcessRenderRequest(renderRequest)) { renderRequest.setAttribute(WebKeys.PORTLET_DECORATE, Boolean.FALSE); return; } WindowState windowState = renderRequest.getWindowState(); if (windowState.equals(WindowState.MINIMIZED)) { return; } PortletMode portletMode = renderRequest.getPortletMode(); if (portletMode.equals(PortletMode.VIEW)) { doView(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.ABOUT)) { doAbout(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.CONFIG)) { doConfig(renderRequest, renderResponse); } else if (portletMode.equals(PortletMode.EDIT)) { doEdit(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.EDIT_DEFAULTS)) { doEditDefaults(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.EDIT_GUEST)) { doEditGuest(renderRequest, renderResponse); } else if (portletMode.equals(PortletMode.HELP)) { doHelp(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.PREVIEW)) { doPreview(renderRequest, renderResponse); } else if (portletMode.equals(LiferayPortletMode.PRINT)) { doPrint(renderRequest, renderResponse); } else { throw new PortletException(portletMode.toString()); } } @SuppressWarnings("unused") protected void doEditDefaults( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doEditDefaults method not implemented"); } @SuppressWarnings("unused") protected void doEditGuest( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doEditGuest method not implemented"); } @SuppressWarnings("unused") protected void doPreview( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doPreview method not implemented"); } @SuppressWarnings("unused") protected void doPrint( RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { throw new PortletException("doPrint method not implemented"); } protected Method getActionMethod(String actionName) throws NoSuchMethodException { Method method = _actionMethods.get(actionName); if (method != null) { return method; } Class<?> clazz = getClass(); method = clazz.getMethod( actionName, ActionRequest.class, ActionResponse.class); _actionMethods.put(actionName, method); return method; } protected String getJSONContentType(PortletRequest portletRequest) { HttpServletRequest request = PortalUtil.getHttpServletRequest( portletRequest); if (BrowserSnifferUtil.isIe(request)) { return ContentTypes.TEXT_HTML; } return ContentTypes.APPLICATION_JSON; } protected Set<String> getPaths(String path, String extension) { Set<String> paths = new HashSet<>(); PortletContext portletContext = getPortletContext(); Set<String> childPaths = portletContext.getResourcePaths(path); if (childPaths == null) { return paths; } for (String childPath : childPaths) { if (childPath.endsWith(StringPool.SLASH)) { paths.addAll(getPaths(childPath, extension)); } else if (childPath.endsWith(extension)) { paths.add(childPath); } } return paths; } protected String getRedirect( ActionRequest actionRequest, ActionResponse actionResponse) { String redirect = (String)actionRequest.getAttribute(WebKeys.REDIRECT); if (Validator.isBlank(redirect)) { redirect = ParamUtil.getString(actionRequest, "redirect"); if (!Validator.isBlank(redirect)) { redirect = PortalUtil.escapeRedirect(redirect); } } return redirect; } protected Method getResourceMethod(String actionName) throws NoSuchMethodException { Method method = _resourceMethods.get(actionName); if (method != null) { return method; } Class<?> clazz = getClass(); method = clazz.getMethod( actionName, ResourceRequest.class, ResourceResponse.class); _resourceMethods.put(actionName, method); return method; } protected ServletContext getServletContext() { LiferayPortletConfig liferayPortletConfig = (LiferayPortletConfig)getPortletConfig(); Portlet portlet = liferayPortletConfig.getPortlet(); PortletApp portletApp = portlet.getPortletApp(); return portletApp.getServletContext(); } @Override protected String getTitle(RenderRequest renderRequest) { try { return PortalUtil.getPortletTitle(renderRequest); } catch (Exception e) { return super.getTitle(renderRequest); } } protected void initValidPaths(String rootPath, String extension) { if (rootPath.equals(StringPool.SLASH)) { PortletContext portletContext = getPortletContext(); PortletApp portletApp = PortletLocalServiceUtil.getPortletApp( portletContext.getPortletContextName()); if (!portletApp.isWARFile()) { _log.error( "Disabling paths for portlet " + getPortletName() + " because root path is configured to have access to " + "all portal paths"); validPaths = new HashSet<>(); return; } } validPaths = getPaths(rootPath, extension); validPaths.addAll( getPaths(_PATH_META_INF_RESOURCES.concat(rootPath), extension)); Collections.addAll( validPaths, StringUtil.split(getInitParameter("valid-paths"))); } protected boolean isAddSuccessMessage(ActionRequest actionRequest) { if (!addProcessActionSuccessMessage) { return false; } String portletId = PortalUtil.getPortletId(actionRequest); if (SessionMessages.contains( actionRequest, portletId.concat( SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_SUCCESS_MESSAGE))) { return false; } if (SessionMessages.isEmpty(actionRequest)) { return true; } int sessionMessagesSize = SessionMessages.size(actionRequest); for (String suffix : _IGNORED_SESSION_MESSAGE_SUFFIXES) { if (SessionMessages.contains( actionRequest, portletId.concat(suffix))) { sessionMessagesSize--; } } if (sessionMessagesSize == 0) { return true; } return false; } protected boolean isAlwaysSendRedirect() { return alwaysSendRedirect; } protected boolean isEmptySessionMessages(ActionRequest actionRequest) { if (SessionMessages.isEmpty(actionRequest)) { return true; } int sessionMessagesSize = SessionMessages.size(actionRequest); String portletId = PortalUtil.getPortletId(actionRequest); for (String suffix : _IGNORED_SESSION_MESSAGE_SUFFIXES) { if (SessionMessages.contains( actionRequest, portletId.concat(suffix))) { sessionMessagesSize--; } } if (sessionMessagesSize == 0) { return true; } return false; } protected boolean isProcessActionRequest(ActionRequest actionRequest) { return isProcessPortletRequest(actionRequest); } protected boolean isProcessPortletRequest(PortletRequest portletRequest) { return _PROCESS_PORTLET_REQUEST; } protected boolean isProcessRenderRequest(RenderRequest renderRequest) { return isProcessPortletRequest(renderRequest); } protected boolean isProcessResourceRequest( ResourceRequest resourceRequest) { return isProcessPortletRequest(resourceRequest); } protected boolean isSessionErrorException(Throwable cause) { if (_log.isDebugEnabled()) { _log.debug(cause, cause); } if (cause instanceof PortalException) { return true; } return false; } protected boolean isValidPath(String path) { if (validPaths.contains(path) || validPaths.contains(_PATH_META_INF_RESOURCES.concat(path))) { return true; } return false; } protected void sendRedirect( ActionRequest actionRequest, ActionResponse actionResponse) throws IOException { String redirect = getRedirect(actionRequest, actionResponse); if (Validator.isNotNull(redirect)) { actionResponse.sendRedirect(redirect); } } protected String translate(PortletRequest portletRequest, String key) { ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute( WebKeys.THEME_DISPLAY); ResourceBundle resourceBundle = getResourceBundle( themeDisplay.getLocale()); return LanguageUtil.get(resourceBundle, key); } protected String translate( PortletRequest portletRequest, String key, Object... arguments) { ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute( WebKeys.THEME_DISPLAY); ResourceBundle resourceBundle = getResourceBundle( themeDisplay.getLocale()); return LanguageUtil.format(resourceBundle, key, arguments); } protected void writeJSON( PortletRequest portletRequest, ActionResponse actionResponse, Object jsonObj) throws IOException { HttpServletResponse response = PortalUtil.getHttpServletResponse( actionResponse); response.setContentType(getJSONContentType(portletRequest)); ServletResponseUtil.write(response, jsonObj.toString()); response.flushBuffer(); } protected void writeJSON( PortletRequest portletRequest, MimeResponse mimeResponse, Object jsonObj) throws IOException { mimeResponse.setContentType(getJSONContentType(portletRequest)); PortletResponseUtil.write(mimeResponse, jsonObj.toString()); mimeResponse.flushBuffer(); } protected boolean addProcessActionSuccessMessage; protected boolean alwaysSendRedirect; protected Set<String> validPaths; private static final String[] _IGNORED_SESSION_MESSAGE_SUFFIXES = { SessionMessages.KEY_SUFFIX_DELETE_SUCCESS_DATA, SessionMessages.KEY_SUFFIX_FORCE_SEND_REDIRECT, SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_ERROR_MESSAGE, SessionMessages.KEY_SUFFIX_HIDE_DEFAULT_SUCCESS_MESSAGE, SessionMessages.KEY_SUFFIX_REFRESH_PORTLET }; private static final String _PATH_META_INF_RESOURCES = "/META-INF/resources"; private static final boolean _PROCESS_PORTLET_REQUEST = true; private static final Log _log = LogFactoryUtil.getLog(LiferayPortlet.class); private final Map<String, Method> _actionMethods = new ConcurrentHashMap<>(); private final Map<String, Method> _resourceMethods = new ConcurrentHashMap<>(); }