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.portlet;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
023    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
024    import com.liferay.portal.kernel.servlet.URLEncoder;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.ParamUtil;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.model.Layout;
030    import com.liferay.portal.model.LayoutConstants;
031    import com.liferay.portal.model.Portlet;
032    import com.liferay.portal.model.PortletApp;
033    import com.liferay.portal.model.PortletURLListener;
034    import com.liferay.portal.security.lang.DoPrivilegedUtil;
035    import com.liferay.portal.service.LayoutLocalServiceUtil;
036    import com.liferay.portal.service.PortletLocalServiceUtil;
037    import com.liferay.portal.struts.StrutsActionPortletURL;
038    import com.liferay.portal.theme.ThemeDisplay;
039    import com.liferay.portal.util.PortalUtil;
040    import com.liferay.portal.util.PropsValues;
041    import com.liferay.portal.util.WebKeys;
042    
043    import java.io.Writer;
044    
045    import java.lang.reflect.Constructor;
046    
047    import java.security.PrivilegedAction;
048    
049    import java.util.ArrayList;
050    import java.util.LinkedHashMap;
051    import java.util.List;
052    import java.util.Map;
053    import java.util.Set;
054    import java.util.concurrent.ConcurrentHashMap;
055    
056    import javax.portlet.MimeResponse;
057    import javax.portlet.PortletException;
058    import javax.portlet.PortletModeException;
059    import javax.portlet.PortletPreferences;
060    import javax.portlet.PortletRequest;
061    import javax.portlet.PortletResponse;
062    import javax.portlet.PortletURL;
063    import javax.portlet.PortletURLGenerationListener;
064    import javax.portlet.ResourceURL;
065    import javax.portlet.WindowStateException;
066    import javax.portlet.filter.PortletResponseWrapper;
067    
068    import javax.servlet.http.Cookie;
069    import javax.servlet.http.HttpServletRequest;
070    import javax.servlet.http.HttpServletResponse;
071    
072    import javax.xml.parsers.DocumentBuilder;
073    import javax.xml.parsers.DocumentBuilderFactory;
074    import javax.xml.parsers.ParserConfigurationException;
075    import javax.xml.transform.OutputKeys;
076    import javax.xml.transform.Transformer;
077    import javax.xml.transform.TransformerFactory;
078    import javax.xml.transform.dom.DOMSource;
079    import javax.xml.transform.stream.StreamResult;
080    
081    import org.w3c.dom.DOMException;
082    import org.w3c.dom.Document;
083    import org.w3c.dom.Element;
084    
085    /**
086     * @author Brian Wing Shun Chan
087     */
088    public abstract class PortletResponseImpl implements LiferayPortletResponse {
089    
090            public static PortletResponseImpl getPortletResponseImpl(
091                    PortletResponse portletResponse) {
092    
093                    while (!(portletResponse instanceof PortletResponseImpl)) {
094                            if (portletResponse instanceof PortletResponseWrapper) {
095                                    PortletResponseWrapper portletResponseWrapper =
096                                            (PortletResponseWrapper)portletResponse;
097    
098                                    portletResponse = portletResponseWrapper.getResponse();
099                            }
100                            else {
101                                    throw new RuntimeException(
102                                            "Unable to unwrap the portlet response from " +
103                                                    portletResponse.getClass());
104                            }
105                    }
106    
107                    return (PortletResponseImpl)portletResponse;
108            }
109    
110            @Override
111            public void addDateHeader(String name, long date) {
112                    if (Validator.isNull(name)) {
113                            throw new IllegalArgumentException();
114                    }
115    
116                    Long[] values = (Long[])_headers.get(name);
117    
118                    if (values == null) {
119                            setDateHeader(name, date);
120                    }
121                    else {
122                            values = ArrayUtil.append(values, new Long(date));
123    
124                            _headers.put(name, values);
125                    }
126            }
127    
128            @Override
129            public void addHeader(String name, String value) {
130                    if (Validator.isNull(name)) {
131                            throw new IllegalArgumentException();
132                    }
133    
134                    String[] values = (String[])_headers.get(name);
135    
136                    if (values == null) {
137                            setHeader(name, value);
138                    }
139                    else {
140                            values = ArrayUtil.append(values, value);
141    
142                            _headers.put(name, values);
143                    }
144            }
145    
146            @Override
147            public void addIntHeader(String name, int value) {
148                    if (Validator.isNull(name)) {
149                            throw new IllegalArgumentException();
150                    }
151    
152                    Integer[] values = (Integer[])_headers.get(name);
153    
154                    if (values == null) {
155                            setIntHeader(name, value);
156                    }
157                    else {
158                            values = ArrayUtil.append(values, new Integer(value));
159    
160                            _headers.put(name, values);
161                    }
162            }
163    
164            @Override
165            public void addProperty(Cookie cookie) {
166                    if (cookie == null) {
167                            throw new IllegalArgumentException();
168                    }
169    
170                    Cookie[] cookies = (Cookie[])_headers.get("cookies");
171    
172                    if (cookies == null) {
173                            _headers.put("cookies", new Cookie[] {cookie});
174                    }
175                    else {
176                            cookies = ArrayUtil.append(cookies, cookie);
177    
178                            _headers.put("cookies", cookies);
179                    }
180            }
181    
182            @Override
183            public void addProperty(String key, Element element) {
184                    if (key == null) {
185                            throw new IllegalArgumentException();
186                    }
187    
188                    if (key.equalsIgnoreCase(MimeResponse.MARKUP_HEAD_ELEMENT)) {
189                            List<Element> values = _markupHeadElements.get(key);
190    
191                            if (values != null) {
192                                    if (element != null) {
193                                            values.add(element);
194                                    }
195                                    else {
196                                            _markupHeadElements.remove(key);
197                                    }
198                            }
199                            else {
200                                    if (element != null) {
201                                            values = new ArrayList<Element>();
202    
203                                            values.add(element);
204    
205                                            _markupHeadElements.put(key, values);
206                                    }
207                            }
208                    }
209            }
210    
211            @Override
212            public void addProperty(String key, String value) {
213                    if (Validator.isNull(key)) {
214                            throw new IllegalArgumentException();
215                    }
216    
217                    addHeader(key, value);
218            }
219    
220            @Override
221            public PortletURL createActionURL() {
222                    return createActionURL(_portletName);
223            }
224    
225            @Override
226            public LiferayPortletURL createActionURL(String portletName) {
227                    return createLiferayPortletURL(
228                            portletName, PortletRequest.ACTION_PHASE);
229            }
230    
231            @Override
232            public Element createElement(String tagName) throws DOMException {
233                    if (_document == null) {
234                            try {
235                                    DocumentBuilderFactory documentBuilderFactory =
236                                            DocumentBuilderFactory.newInstance();
237    
238                                    DocumentBuilder documentBuilder =
239                                            documentBuilderFactory.newDocumentBuilder();
240    
241                                    _document = documentBuilder.newDocument();
242                            }
243                            catch (ParserConfigurationException pce) {
244                                    throw new DOMException(
245                                            DOMException.INVALID_STATE_ERR, pce.getMessage());
246                            }
247                    }
248    
249                    return _document.createElement(tagName);
250            }
251    
252            @Override
253            public LiferayPortletURL createLiferayPortletURL(
254                    long plid, String portletName, String lifecycle) {
255    
256                    return createLiferayPortletURL(plid, portletName, lifecycle, true);
257            }
258    
259            @Override
260            public LiferayPortletURL createLiferayPortletURL(
261                    long plid, String portletName, String lifecycle,
262                    boolean includeLinkToLayoutUuid) {
263    
264                    return DoPrivilegedUtil.wrap(
265                            new LiferayPortletURLPrivilegedAction(
266                                    plid, portletName, lifecycle, includeLinkToLayoutUuid));
267            }
268    
269            @Override
270            public LiferayPortletURL createLiferayPortletURL(String lifecycle) {
271                    return createLiferayPortletURL(_portletName, lifecycle);
272            }
273    
274            @Override
275            public LiferayPortletURL createLiferayPortletURL(
276                    String portletName, String lifecycle) {
277    
278                    return createLiferayPortletURL(_plid, portletName, lifecycle);
279            }
280    
281            @Override
282            public PortletURL createRenderURL() {
283                    return createRenderURL(_portletName);
284            }
285    
286            @Override
287            public LiferayPortletURL createRenderURL(String portletName) {
288                    return createLiferayPortletURL(
289                            portletName, PortletRequest.RENDER_PHASE);
290            }
291    
292            @Override
293            public ResourceURL createResourceURL() {
294                    return createResourceURL(_portletName);
295            }
296    
297            @Override
298            public LiferayPortletURL createResourceURL(String portletName) {
299                    return createLiferayPortletURL(
300                            portletName, PortletRequest.RESOURCE_PHASE);
301            }
302    
303            @Override
304            public String encodeURL(String path) {
305                    if ((path == null) ||
306                            (!path.startsWith("#") && !path.startsWith("/") &&
307                             !path.contains("://"))) {
308    
309                            // Allow '#' as well to workaround a bug in Oracle ADF 10.1.3
310    
311                            throw new IllegalArgumentException(
312                                    "URL path must start with a '/' or include '://'");
313                    }
314    
315                    if (_urlEncoder != null) {
316                            return _urlEncoder.encodeURL(_response, path);
317                    }
318                    else {
319                            return path;
320                    }
321            }
322    
323            public long getCompanyId() {
324                    return _companyId;
325            }
326    
327            public HttpServletRequest getHttpServletRequest() {
328                    return _portletRequestImpl.getHttpServletRequest();
329            }
330    
331            @Override
332            public HttpServletResponse getHttpServletResponse() {
333                    return _response;
334            }
335    
336            public abstract String getLifecycle();
337    
338            @Override
339            public String getNamespace() {
340                    if (_wsrp) {
341                            return "wsrp_rewrite_";
342                    }
343    
344                    if (_namespace == null) {
345                            _namespace = PortalUtil.getPortletNamespace(_portletName);
346                    }
347    
348                    return _namespace;
349            }
350    
351            public long getPlid() {
352                    return _plid;
353            }
354    
355            public Portlet getPortlet() {
356                    if (_portlet == null) {
357                            try {
358                                    _portlet = PortletLocalServiceUtil.getPortletById(
359                                            _companyId, _portletName);
360                            }
361                            catch (Exception e) {
362                                    _log.error(e);
363                            }
364                    }
365    
366                    return _portlet;
367            }
368    
369            public String getPortletName() {
370                    return _portletName;
371            }
372    
373            public PortletRequestImpl getPortletRequest() {
374                    return _portletRequestImpl;
375            }
376    
377            @Override
378            public Map<String, String[]> getProperties() {
379                    Map<String, String[]> properties =
380                            new LinkedHashMap<String, String[]>();
381    
382                    for (Map.Entry<String, Object> entry : _headers.entrySet()) {
383                            String name = entry.getKey();
384                            Object[] values = (Object[])entry.getValue();
385    
386                            String[] valuesString = new String[values.length];
387    
388                            for (int i = 0; i < values.length; i++) {
389                                    valuesString[i] = values[i].toString();
390                            }
391    
392                            properties.put(name, valuesString);
393                    }
394    
395                    return properties;
396            }
397    
398            public URLEncoder getUrlEncoder() {
399                    return _urlEncoder;
400            }
401    
402            @Override
403            public void setDateHeader(String name, long date) {
404                    if (Validator.isNull(name)) {
405                            throw new IllegalArgumentException();
406                    }
407    
408                    if (date <= 0) {
409                            _headers.remove(name);
410                    }
411                    else {
412                            _headers.put(name, new Long[] {new Long(date)});
413                    }
414            }
415    
416            @Override
417            public void setHeader(String name, String value) {
418                    if (Validator.isNull(name)) {
419                            throw new IllegalArgumentException();
420                    }
421    
422                    if (Validator.isNull(value)) {
423                            _headers.remove(name);
424                    }
425                    else {
426                            _headers.put(name, new String[] {value});
427                    }
428            }
429    
430            @Override
431            public void setIntHeader(String name, int value) {
432                    if (Validator.isNull(name)) {
433                            throw new IllegalArgumentException();
434                    }
435    
436                    if (value <= 0) {
437                            _headers.remove(name);
438                    }
439                    else {
440                            _headers.put(name, new Integer[] {new Integer(value)});
441                    }
442            }
443    
444            public void setPlid(long plid) {
445                    _plid = plid;
446    
447                    if (_plid <= 0) {
448                            Layout layout = (Layout)_portletRequestImpl.getAttribute(
449                                    WebKeys.LAYOUT);
450    
451                            if (layout != null) {
452                                    _plid = layout.getPlid();
453                            }
454                    }
455            }
456    
457            @Override
458            public void setProperty(String key, String value) {
459                    if (key == null) {
460                            throw new IllegalArgumentException();
461                    }
462    
463                    setHeader(key, value);
464            }
465    
466            public void setURLEncoder(URLEncoder urlEncoder) {
467                    _urlEncoder = urlEncoder;
468            }
469    
470            public void transferHeaders(HttpServletResponse response) {
471                    for (Map.Entry<String, Object> entry : _headers.entrySet()) {
472                            String name = entry.getKey();
473                            Object values = entry.getValue();
474    
475                            if (values instanceof Integer[]) {
476                                    Integer[] intValues = (Integer[])values;
477    
478                                    for (int value : intValues) {
479                                            if (response.containsHeader(name)) {
480                                                    response.addIntHeader(name, value);
481                                            }
482                                            else {
483                                                    response.setIntHeader(name, value);
484                                            }
485                                    }
486                            }
487                            else if (values instanceof Long[]) {
488                                    Long[] dateValues = (Long[])values;
489    
490                                    for (long value : dateValues) {
491                                            if (response.containsHeader(name)) {
492                                                    response.addDateHeader(name, value);
493                                            }
494                                            else {
495                                                    response.setDateHeader(name, value);
496                                            }
497                                    }
498                            }
499                            else if (values instanceof String[]) {
500                                    String[] stringValues = (String[])values;
501    
502                                    for (String value : stringValues) {
503                                            if (response.containsHeader(name)) {
504                                                    response.addHeader(name, value);
505                                            }
506                                            else {
507                                                    response.setHeader(name, value);
508                                            }
509                                    }
510                            }
511                            else if (values instanceof Cookie[]) {
512                                    Cookie[] cookies = (Cookie[])values;
513    
514                                    for (Cookie cookie : cookies) {
515                                            response.addCookie(cookie);
516                                    }
517                            }
518                    }
519            }
520    
521            @Override
522            public void transferMarkupHeadElements() {
523                    List<Element> elements = _markupHeadElements.get(
524                            MimeResponse.MARKUP_HEAD_ELEMENT);
525    
526                    if ((elements == null) || elements.isEmpty()) {
527                            return;
528                    }
529    
530                    HttpServletRequest request = getHttpServletRequest();
531    
532                    List<String> markupHeadElements = (List<String>)request.getAttribute(
533                            MimeResponse.MARKUP_HEAD_ELEMENT);
534    
535                    if (markupHeadElements == null) {
536                            markupHeadElements = new ArrayList<String>();
537    
538                            request.setAttribute(
539                                    MimeResponse.MARKUP_HEAD_ELEMENT, markupHeadElements);
540                    }
541    
542                    for (Element element : elements) {
543                            try {
544                                    Writer writer = new UnsyncStringWriter();
545    
546                                    TransformerFactory transformerFactory =
547                                            TransformerFactory.newInstance();
548    
549                                    Transformer transformer = transformerFactory.newTransformer();
550    
551                                    transformer.setOutputProperty(
552                                            OutputKeys.OMIT_XML_DECLARATION, "yes");
553    
554                                    transformer.transform(
555                                            new DOMSource(element), new StreamResult(writer));
556    
557                                    markupHeadElements.add(writer.toString());
558                            }
559                            catch (Exception e) {
560                                    if (_log.isWarnEnabled()) {
561                                            _log.warn(e, e);
562                                    }
563                            }
564                    }
565            }
566    
567            protected LiferayPortletURL doCreateLiferayPortletURL(
568                    long plid, String portletName, String lifecycle,
569                    boolean includeLinkToLayoutUuid) {
570    
571                    try {
572                            Layout layout = (Layout)_portletRequestImpl.getAttribute(
573                                    WebKeys.LAYOUT);
574    
575                            if (layout == null) {
576                                    ThemeDisplay themeDisplay =
577                                            (ThemeDisplay)_portletRequestImpl.getAttribute(
578                                                    WebKeys.THEME_DISPLAY);
579    
580                                    if (themeDisplay != null) {
581                                            layout = themeDisplay.getLayout();
582                                    }
583                            }
584    
585                            if (_portletSetup == null) {
586                                    _portletSetup =
587                                            PortletPreferencesFactoryUtil.getStrictLayoutPortletSetup(
588                                                    layout, _portletName);
589                            }
590    
591                            String linkToLayoutUuid = GetterUtil.getString(
592                                    _portletSetup.getValue("portletSetupLinkToLayoutUuid", null));
593    
594                            if (PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals(
595                                            "render") &&
596                                    !PortletRequest.RENDER_PHASE.equals(lifecycle)) {
597    
598                                    includeLinkToLayoutUuid = false;
599                            }
600    
601                            if (Validator.isNotNull(linkToLayoutUuid) &&
602                                    includeLinkToLayoutUuid) {
603    
604                                    try {
605                                            Layout linkedLayout =
606                                                    LayoutLocalServiceUtil.getLayoutByUuidAndGroupId(
607                                                            linkToLayoutUuid, layout.getGroupId(),
608                                                            layout.isPrivateLayout());
609    
610                                            plid = linkedLayout.getPlid();
611                                    }
612                                    catch (PortalException pe) {
613                                    }
614                            }
615                    }
616                    catch (SystemException se) {
617                            if (_log.isWarnEnabled()) {
618                                    _log.warn(se);
619                            }
620                    }
621    
622                    if (plid == LayoutConstants.DEFAULT_PLID) {
623                            plid = _plid;
624                    }
625    
626                    PortletURLImpl portletURLImpl = null;
627    
628                    Portlet portlet = getPortlet();
629    
630                    String portletURLClass = portlet.getPortletURLClass();
631    
632                    if (portlet.getPortletId().equals(portletName) &&
633                            Validator.isNotNull(portletURLClass)) {
634    
635                            if (portletURLClass.equals(
636                                            StrutsActionPortletURL.class.getName())) {
637    
638                                    portletURLImpl = new StrutsActionPortletURL(
639                                            this, plid, lifecycle);
640                            }
641                            else {
642                                    try {
643                                            Constructor<? extends PortletURLImpl> constructor =
644                                                    _constructors.get(portletURLClass);
645    
646                                            if (constructor == null) {
647                                                    Class<?> portletURLClassObj = Class.forName(
648                                                            portletURLClass);
649    
650                                                    constructor = (Constructor<? extends PortletURLImpl>)
651                                                            portletURLClassObj.getConstructor(
652                                                                    new Class[] {
653                                                                            PortletResponseImpl.class, long.class,
654                                                                            String.class
655                                                                    });
656    
657                                                    _constructors.put(portletURLClass, constructor);
658                                            }
659    
660                                            portletURLImpl = constructor.newInstance(
661                                                    new Object[] {this, plid, lifecycle});
662                                    }
663                                    catch (Exception e) {
664                                            _log.error(e);
665                                    }
666                            }
667                    }
668    
669                    if (portletURLImpl == null) {
670                            portletURLImpl = new PortletURLImpl(
671                                    _portletRequestImpl, portletName, plid, lifecycle);
672                    }
673    
674                    PortletApp portletApp = portlet.getPortletApp();
675    
676                    Set<PortletURLListener> portletURLListeners =
677                            portletApp.getPortletURLListeners();
678    
679                    for (PortletURLListener portletURLListener : portletURLListeners) {
680                            try {
681                                    PortletURLGenerationListener portletURLGenerationListener =
682                                            PortletURLListenerFactory.create(portletURLListener);
683    
684                                    if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
685                                            portletURLGenerationListener.filterActionURL(
686                                                    portletURLImpl);
687                                    }
688                                    else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
689                                            portletURLGenerationListener.filterRenderURL(
690                                                    portletURLImpl);
691                                    }
692                                    else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
693                                            portletURLGenerationListener.filterResourceURL(
694                                                    portletURLImpl);
695                                    }
696                            }
697                            catch (PortletException pe) {
698                                    _log.error(pe, pe);
699                            }
700                    }
701    
702                    try {
703                            portletURLImpl.setWindowState(_portletRequestImpl.getWindowState());
704                    }
705                    catch (WindowStateException wse) {
706                            _log.error(wse.getMessage());
707                    }
708    
709                    try {
710                            portletURLImpl.setPortletMode(_portletRequestImpl.getPortletMode());
711                    }
712                    catch (PortletModeException pme) {
713                            _log.error(pme.getMessage());
714                    }
715    
716                    if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
717                            portletURLImpl.setCopyCurrentRenderParameters(true);
718                    }
719    
720                    return portletURLImpl;
721            }
722    
723            protected void init(
724                    PortletRequestImpl portletRequestImpl, HttpServletResponse response,
725                    String portletName, long companyId, long plid) {
726    
727                    _portletRequestImpl = portletRequestImpl;
728                    _response = response;
729                    _portletName = portletName;
730                    _companyId = companyId;
731                    _wsrp = ParamUtil.getBoolean(getHttpServletRequest(), "wsrp");
732    
733                    setPlid(plid);
734            }
735    
736            private static Log _log = LogFactoryUtil.getLog(PortletResponseImpl.class);
737    
738            private long _companyId;
739            private Map<String, Constructor<? extends PortletURLImpl>> _constructors =
740                    new ConcurrentHashMap<String, Constructor<? extends PortletURLImpl>>();
741            private Document _document;
742            private Map<String, Object> _headers = new LinkedHashMap<String, Object>();
743            private Map<String, List<Element>> _markupHeadElements =
744                    new LinkedHashMap<String, List<Element>>();
745            private String _namespace;
746            private long _plid;
747            private Portlet _portlet;
748            private String _portletName;
749            private PortletRequestImpl _portletRequestImpl;
750            private PortletPreferences _portletSetup;
751            private HttpServletResponse _response;
752            private URLEncoder _urlEncoder;
753            private boolean _wsrp;
754    
755            private class LiferayPortletURLPrivilegedAction
756                    implements PrivilegedAction<LiferayPortletURL> {
757    
758                    public LiferayPortletURLPrivilegedAction(
759                            long plid, String portletName, String lifecycle,
760                            boolean includeLinkToLayoutUuid) {
761    
762                            _plid = plid;
763                            _portletName = portletName;
764                            _lifecycle = lifecycle;
765                            _includeLinkToLayoutUuid = includeLinkToLayoutUuid;
766                    }
767    
768                    @Override
769                    public LiferayPortletURL run() {
770                            return doCreateLiferayPortletURL(
771                                    _plid, _portletName, _lifecycle, _includeLinkToLayoutUuid);
772                    }
773    
774                    private boolean _includeLinkToLayoutUuid;
775                    private String _lifecycle;
776                    private long _plid;
777                    private String _portletName;
778    
779            }
780    
781    }