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