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.struts;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.portlet.LiferayPortletConfig;
020    import com.liferay.portal.kernel.portlet.LiferayPortletRequestDispatcher;
021    import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
022    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
023    import com.liferay.portal.kernel.util.CharPool;
024    import com.liferay.portal.kernel.util.JavaConstants;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.Validator;
027    import com.liferay.portal.kernel.util.WebKeys;
028    import com.liferay.portal.model.Portlet;
029    import com.liferay.portal.security.auth.PrincipalException;
030    import com.liferay.portal.service.PortletLocalServiceUtil;
031    import com.liferay.portal.util.PortalUtil;
032    import com.liferay.portal.util.PropsValues;
033    
034    import java.io.IOException;
035    
036    import java.lang.reflect.Constructor;
037    
038    import javax.portlet.ActionRequest;
039    import javax.portlet.ActionResponse;
040    import javax.portlet.EventRequest;
041    import javax.portlet.EventResponse;
042    import javax.portlet.PortletContext;
043    import javax.portlet.PortletException;
044    import javax.portlet.PortletRequest;
045    import javax.portlet.PortletResponse;
046    import javax.portlet.RenderRequest;
047    import javax.portlet.RenderResponse;
048    import javax.portlet.ResourceRequest;
049    import javax.portlet.ResourceResponse;
050    
051    import javax.servlet.ServletException;
052    import javax.servlet.http.HttpServletRequest;
053    import javax.servlet.http.HttpServletResponse;
054    
055    import org.apache.struts.Globals;
056    import org.apache.struts.action.Action;
057    import org.apache.struts.action.ActionErrors;
058    import org.apache.struts.action.ActionForm;
059    import org.apache.struts.action.ActionForward;
060    import org.apache.struts.action.ActionMapping;
061    import org.apache.struts.action.ActionServlet;
062    import org.apache.struts.config.ActionConfig;
063    import org.apache.struts.config.ForwardConfig;
064    import org.apache.struts.config.ModuleConfig;
065    import org.apache.struts.tiles.TilesRequestProcessor;
066    import org.apache.struts.util.MessageResources;
067    
068    /**
069     * @author Brian Wing Shun Chan
070     * @author Raymond Aug??
071     */
072    public class PortletRequestProcessor extends TilesRequestProcessor {
073    
074            public static PortletRequestProcessor getInstance(
075                            ActionServlet servlet, ModuleConfig moduleConfig)
076                    throws ServletException {
077    
078                    try {
079                            String className = PropsValues.STRUTS_PORTLET_REQUEST_PROCESSOR;
080    
081                            Class<?> clazz = Class.forName(className);
082    
083                            Constructor<?> constructor = clazz.getConstructor(
084                                    ActionServlet.class, ModuleConfig.class);
085    
086                            PortletRequestProcessor portletReqProcessor =
087                                    (PortletRequestProcessor)constructor.newInstance(
088                                            servlet, moduleConfig);
089    
090                            return portletReqProcessor;
091                    }
092                    catch (Exception e) {
093                            _log.error(e);
094    
095                            return new PortletRequestProcessor(servlet, moduleConfig);
096                    }
097            }
098    
099            public PortletRequestProcessor(
100                            ActionServlet actionServlet, ModuleConfig moduleConfig)
101                    throws ServletException {
102    
103                    init(actionServlet, moduleConfig);
104            }
105    
106            public void process(
107                            ActionRequest actionRequest, ActionResponse actionResponse,
108                            String path)
109                    throws IOException, ServletException {
110    
111                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
112                            actionRequest);
113                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
114                            actionResponse);
115    
116                    ActionMapping actionMapping = processMapping(request, response, path);
117    
118                    if (actionMapping == null) {
119                            return;
120                    }
121    
122                    if (!processRoles(request, response, actionMapping, true)) {
123                            return;
124                    }
125    
126                    ActionForm actionForm = processActionForm(
127                            request, response, actionMapping);
128    
129                    processPopulate(request, response, actionForm, actionMapping);
130    
131                    if (!processValidateAction(
132                                    request, response, actionForm, actionMapping)) {
133    
134                            return;
135                    }
136    
137                    PortletAction portletAction = (PortletAction)processActionCreate(
138                            request, response, actionMapping);
139    
140                    if (portletAction == null) {
141                            return;
142                    }
143    
144                    LiferayPortletConfig liferayPortletConfig =
145                            (LiferayPortletConfig)actionRequest.getAttribute(
146                                    JavaConstants.JAVAX_PORTLET_CONFIG);
147    
148                    try {
149                            if (portletAction.isCheckMethodOnProcessAction()) {
150                                    if (!PortalUtil.isMethodPost(actionRequest)) {
151                                            String currentURL = PortalUtil.getCurrentURL(actionRequest);
152    
153                                            if (_log.isWarnEnabled()) {
154                                                    _log.warn(
155                                                            "This URL can only be invoked using POST: " +
156                                                                    currentURL);
157                                            }
158    
159                                            throw new PrincipalException.MustBeInvokedUsingPost(
160                                                    currentURL);
161                                    }
162                            }
163    
164                            portletAction.processAction(
165                                    actionMapping, actionForm, liferayPortletConfig, actionRequest,
166                                    actionResponse);
167                    }
168                    catch (Exception e) {
169                            String exceptionId =
170                                    WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
171                                            liferayPortletConfig.getPortletId();
172    
173                            actionRequest.setAttribute(exceptionId, e);
174                    }
175    
176                    String forward = (String)actionRequest.getAttribute(
177                            PortletAction.getForwardKey(actionRequest));
178    
179                    if (forward == null) {
180                            return;
181                    }
182    
183                    String queryString = StringPool.BLANK;
184    
185                    int pos = forward.indexOf(CharPool.QUESTION);
186    
187                    if (pos != -1) {
188                            queryString = forward.substring(pos + 1);
189                            forward = forward.substring(0, pos);
190                    }
191    
192                    ActionForward actionForward = actionMapping.findForward(forward);
193    
194                    if ((actionForward != null) && actionForward.getRedirect()) {
195                            String forwardPath = actionForward.getPath();
196    
197                            if (forwardPath.startsWith(StringPool.SLASH)) {
198                                    LiferayPortletResponse liferayPortletResponse =
199                                            PortalUtil.getLiferayPortletResponse(actionResponse);
200    
201                                    LiferayPortletURL forwardURL =
202                                            (LiferayPortletURL)liferayPortletResponse.createRenderURL();
203    
204                                    forwardURL.setParameter("struts_action", forwardPath);
205    
206                                    StrutsURLEncoder.setParameters(forwardURL, queryString);
207    
208                                    forwardPath = forwardURL.toString();
209                            }
210    
211                            actionResponse.sendRedirect(forwardPath);
212                    }
213            }
214    
215            public void process(EventRequest eventRequest, EventResponse eventResponse)
216                    throws IOException, ServletException {
217    
218                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
219                            eventRequest);
220                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
221                            eventResponse);
222    
223                    process(request, response);
224            }
225    
226            public void process(
227                            RenderRequest renderRequest, RenderResponse renderResponse)
228                    throws IOException, ServletException {
229    
230                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
231                            renderRequest);
232                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
233                            renderResponse);
234    
235                    process(request, response);
236            }
237    
238            public void process(
239                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
240                    throws IOException, ServletException {
241    
242                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
243                            resourceRequest);
244                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
245                            resourceResponse);
246    
247                    process(request, response);
248            }
249    
250            @Override
251            public ActionMapping processMapping(
252                    HttpServletRequest request, HttpServletResponse response, String path) {
253    
254                    if (path == null) {
255                            return null;
256                    }
257    
258                    ActionMapping actionMapping = null;
259    
260                    long companyId = PortalUtil.getCompanyId(request);
261    
262                    LiferayPortletConfig liferayPortletConfig =
263                            (LiferayPortletConfig)request.getAttribute(
264                                    JavaConstants.JAVAX_PORTLET_CONFIG);
265    
266                    try {
267                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
268                                    companyId, liferayPortletConfig.getPortletId());
269    
270                            if (StrutsActionRegistryUtil.getAction(path) != null) {
271                                    actionMapping = (ActionMapping)moduleConfig.findActionConfig(
272                                            path);
273    
274                                    if (actionMapping == null) {
275                                            actionMapping = new ActionMapping();
276    
277                                            actionMapping.setModuleConfig(moduleConfig);
278                                            actionMapping.setPath(path);
279    
280                                            request.setAttribute(Globals.MAPPING_KEY, actionMapping);
281                                    }
282                            }
283                            else if (moduleConfig.findActionConfig(path) != null) {
284                                    actionMapping = super.processMapping(request, response, path);
285                            }
286                            else if (Validator.isNotNull(portlet.getParentStrutsPath())) {
287                                    int pos = path.indexOf(StringPool.SLASH, 1);
288    
289                                    String parentPath =
290                                            StringPool.SLASH + portlet.getParentStrutsPath() +
291                                                    path.substring(pos);
292    
293                                    if (StrutsActionRegistryUtil.getAction(parentPath) != null) {
294                                            actionMapping =
295                                                    (ActionMapping)moduleConfig.findActionConfig(
296                                                            parentPath);
297    
298                                            if (actionMapping == null) {
299                                                    actionMapping = new ActionMapping();
300    
301                                                    actionMapping.setModuleConfig(moduleConfig);
302                                                    actionMapping.setPath(parentPath);
303    
304                                                    request.setAttribute(
305                                                            Globals.MAPPING_KEY, actionMapping);
306                                            }
307                                    }
308                                    else if (moduleConfig.findActionConfig(parentPath) != null) {
309                                            actionMapping = super.processMapping(
310                                                    request, response, parentPath);
311                                    }
312                            }
313                    }
314                    catch (Exception e) {
315                    }
316    
317                    if (actionMapping == null) {
318                            MessageResources messageResources = getInternal();
319    
320                            String msg = messageResources.getMessage("processInvalid");
321    
322                            _log.error("User ID " + request.getRemoteUser());
323                            _log.error("Current URL " + PortalUtil.getCurrentURL(request));
324                            _log.error("Referer " + request.getHeader("Referer"));
325                            _log.error("Remote address " + request.getRemoteAddr());
326    
327                            _log.error(msg + " " + path);
328                    }
329    
330                    return actionMapping;
331            }
332    
333            @Override
334            protected void doForward(
335                            String uri, HttpServletRequest request,
336                            HttpServletResponse response)
337                    throws IOException, ServletException {
338    
339                    doInclude(uri, request, response);
340            }
341    
342            @Override
343            protected void doInclude(
344                            String uri, HttpServletRequest request,
345                            HttpServletResponse response)
346                    throws IOException, ServletException {
347    
348                    LiferayPortletConfig liferayPortletConfig =
349                            (LiferayPortletConfig)request.getAttribute(
350                                    JavaConstants.JAVAX_PORTLET_CONFIG);
351    
352                    PortletContext portletContext =
353                            liferayPortletConfig.getPortletContext();
354    
355                    PortletRequest portletRequest = (PortletRequest)request.getAttribute(
356                            JavaConstants.JAVAX_PORTLET_REQUEST);
357    
358                    PortletResponse portletResponse = (PortletResponse)request.getAttribute(
359                            JavaConstants.JAVAX_PORTLET_RESPONSE);
360    
361                    LiferayPortletRequestDispatcher liferayPortletRequestDispatcher =
362                            (LiferayPortletRequestDispatcher)
363                                    portletContext.getRequestDispatcher(
364                                            StrutsUtil.TEXT_HTML_DIR + uri);
365    
366                    try {
367                            if (liferayPortletRequestDispatcher == null) {
368                                    _log.error(uri + " is not a valid include");
369                            }
370                            else {
371                                    liferayPortletRequestDispatcher.include(
372                                            portletRequest, portletResponse, true);
373                            }
374                    }
375                    catch (PortletException pe) {
376                            Throwable cause = pe.getCause();
377    
378                            if (cause instanceof ServletException) {
379                                    throw (ServletException)cause;
380                            }
381                            else {
382                                    _log.error(cause, cause);
383                            }
384                    }
385            }
386    
387            @Override
388            protected Action processActionCreate(
389                            HttpServletRequest request, HttpServletResponse response,
390                            ActionMapping actionMapping)
391                    throws IOException {
392    
393                    PortletActionAdapter portletActionAdapter =
394                            (PortletActionAdapter)StrutsActionRegistryUtil.getAction(
395                                    actionMapping.getPath());
396    
397                    if (portletActionAdapter != null) {
398                            ActionConfig actionConfig = moduleConfig.findActionConfig(
399                                    actionMapping.getPath());
400    
401                            if (actionConfig != null) {
402                                    PortletAction originalPortletAction =
403                                            (PortletAction)super.processActionCreate(
404                                                    request, response, actionMapping);
405    
406                                    portletActionAdapter.setOriginalPortletAction(
407                                            originalPortletAction);
408                            }
409    
410                            return portletActionAdapter;
411                    }
412    
413                    return super.processActionCreate(request, response, actionMapping);
414            }
415    
416            @Override
417            protected ActionForm processActionForm(
418                    HttpServletRequest request, HttpServletResponse response,
419                    ActionMapping actionMapping) {
420    
421                    ActionForm actionForm = super.processActionForm(
422                            request, response, actionMapping);
423    
424                    if (actionForm instanceof InitializableActionForm) {
425                            InitializableActionForm initializableActionForm =
426                                    (InitializableActionForm)actionForm;
427    
428                            initializableActionForm.init(request, response, actionMapping);
429                    }
430    
431                    return actionForm;
432            }
433    
434            @Override
435            protected ActionForward processActionPerform(
436                            HttpServletRequest request, HttpServletResponse response,
437                            Action action, ActionForm actionForm, ActionMapping actionMapping)
438                    throws IOException, ServletException {
439    
440                    LiferayPortletConfig liferayPortletConfig =
441                            (LiferayPortletConfig)request.getAttribute(
442                                    JavaConstants.JAVAX_PORTLET_CONFIG);
443    
444                    String exceptionId =
445                            WebKeys.PORTLET_STRUTS_EXCEPTION + StringPool.PERIOD +
446                                    liferayPortletConfig.getPortletId();
447    
448                    Exception e = (Exception)request.getAttribute(exceptionId);
449    
450                    if (e != null) {
451                            return processException(
452                                    request, response, e, actionForm, actionMapping);
453                    }
454                    else {
455                            return super.processActionPerform(
456                                    request, response, action, actionForm, actionMapping);
457                    }
458            }
459    
460            @Override
461            protected void processForwardConfig(
462                            HttpServletRequest request, HttpServletResponse response,
463                            ForwardConfig forward)
464                    throws IOException, ServletException {
465    
466                    if (forward == null) {
467                            _log.error("Forward does not exist");
468                    }
469                    else {
470    
471                            // Don't render a null path. This is useful if you're sending a file
472                            // in an exclusive window state.
473    
474                            if (forward.getPath().equals(ActionConstants.COMMON_NULL)) {
475                                    return;
476                            }
477                    }
478    
479                    super.processForwardConfig(request, response, forward);
480            }
481    
482            @Override
483            protected HttpServletRequest processMultipart(HttpServletRequest request) {
484    
485                    // Disable Struts from automatically wrapping a multipart request
486    
487                    return request;
488            }
489    
490            @Override
491            protected String processPath(
492                    HttpServletRequest request, HttpServletResponse response) {
493    
494                    String path = request.getParameter("struts_action");
495    
496                    if (_log.isDebugEnabled()) {
497                            _log.debug("Getting request parameter path " + path);
498                    }
499    
500                    if (Validator.isNull(path)) {
501                            if (_log.isDebugEnabled()) {
502                                    _log.debug("Getting request attribute path " + path);
503                            }
504    
505                            path = (String)request.getAttribute(WebKeys.PORTLET_STRUTS_ACTION);
506                    }
507    
508                    if (path == null) {
509                            LiferayPortletConfig liferayPortletConfig =
510                                    (LiferayPortletConfig)request.getAttribute(
511                                            JavaConstants.JAVAX_PORTLET_CONFIG);
512    
513                            _log.error(
514                                    liferayPortletConfig.getPortletName() +
515                                            " does not have any paths specified");
516                    }
517                    else {
518                            if (_log.isDebugEnabled()) {
519                                    _log.debug("Processing path " + path);
520                            }
521    
522                            request.removeAttribute(WebKeys.PORTLET_STRUTS_ACTION);
523                    }
524    
525                    return path;
526            }
527    
528            @Override
529            protected boolean processRoles(
530                            HttpServletRequest request, HttpServletResponse response,
531                            ActionMapping actionMapping)
532                    throws IOException, ServletException {
533    
534                    return processRoles(request, response, actionMapping, false);
535            }
536    
537            protected boolean processRoles(
538                            HttpServletRequest request, HttpServletResponse response,
539                            ActionMapping actionMapping, boolean action)
540                    throws IOException, ServletException {
541    
542                    long companyId = PortalUtil.getCompanyId(request);
543    
544                    String path = actionMapping.getPath();
545    
546                    try {
547                            LiferayPortletConfig liferayPortletConfig =
548                                    (LiferayPortletConfig)request.getAttribute(
549                                            JavaConstants.JAVAX_PORTLET_CONFIG);
550    
551                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
552                                    companyId, liferayPortletConfig.getPortletId());
553    
554                            if (portlet == null) {
555                                    return false;
556                            }
557    
558                            String strutsPath = path.substring(
559                                    1, path.lastIndexOf(CharPool.SLASH));
560    
561                            if (!strutsPath.equals(portlet.getStrutsPath()) &&
562                                    !strutsPath.equals(portlet.getParentStrutsPath())) {
563    
564                                    throw new PrincipalException.MustBePortletStrutsPath(
565                                            strutsPath, portlet.getPortletId());
566                            }
567                            else if (!portlet.isActive()) {
568                                    ForwardConfig forwardConfig = actionMapping.findForward(
569                                            _PATH_PORTAL_PORTLET_INACTIVE);
570    
571                                    if (!action) {
572                                            processForwardConfig(request, response, forwardConfig);
573                                    }
574    
575                                    return false;
576                            }
577                    }
578                    catch (Exception e) {
579                            if (_log.isWarnEnabled()) {
580                                    _log.warn(e.getMessage());
581                            }
582    
583                            ForwardConfig forwardConfig = actionMapping.findForward(
584                                    _PATH_PORTAL_PORTLET_ACCESS_DENIED);
585    
586                            if (!action) {
587                                    processForwardConfig(request, response, forwardConfig);
588                            }
589    
590                            return false;
591                    }
592    
593                    return true;
594            }
595    
596            protected boolean processValidateAction(
597                    HttpServletRequest request, HttpServletResponse response,
598                    ActionForm actionForm, ActionMapping actionMapping) {
599    
600                    if (actionForm == null) {
601                            return true;
602                    }
603    
604                    if (request.getAttribute(Globals.CANCEL_KEY) != null) {
605                            return true;
606                    }
607    
608                    if (!actionMapping.getValidate()) {
609                            return true;
610                    }
611    
612                    ActionErrors errors = actionForm.validate(actionMapping, request);
613    
614                    if ((errors == null) || errors.isEmpty()) {
615                            return true;
616                    }
617    
618                    if (actionForm.getMultipartRequestHandler() != null) {
619                            actionForm.getMultipartRequestHandler().rollback();
620                    }
621    
622                    String input = actionMapping.getInput();
623    
624                    if (input == null) {
625                            _log.error("Validation failed but no input form is available");
626    
627                            return false;
628                    }
629    
630                    request.setAttribute(Globals.ERROR_KEY, errors);
631    
632                    // Struts normally calls internalModuleRelativeForward which breaks if
633                    // called inside processAction
634    
635                    request.setAttribute(PortletAction.getForwardKey(request), input);
636    
637                    return false;
638            }
639    
640            private static final String _PATH_PORTAL_PORTLET_ACCESS_DENIED =
641                    "/portal/portlet_access_denied";
642    
643            private static final String _PATH_PORTAL_PORTLET_INACTIVE =
644                    "/portal/portlet_inactive";
645    
646            private static final Log _log = LogFactoryUtil.getLog(
647                    PortletRequestProcessor.class);
648    
649    }