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