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