/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portlet; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Layout; import com.liferay.portal.kernel.model.LayoutConstants; import com.liferay.portal.kernel.model.Portlet; import com.liferay.portal.kernel.model.PortletApp; import com.liferay.portal.kernel.model.PortletURLListener; import com.liferay.portal.kernel.portlet.LiferayPortletResponse; import com.liferay.portal.kernel.portlet.LiferayPortletURL; import com.liferay.portal.kernel.portlet.PortletPreferencesFactoryUtil; import com.liferay.portal.kernel.portlet.PortletURLFactoryUtil; import com.liferay.portal.kernel.security.xml.SecureXMLFactoryProviderUtil; import com.liferay.portal.kernel.service.LayoutLocalServiceUtil; import com.liferay.portal.kernel.servlet.URLEncoder; import com.liferay.portal.kernel.theme.ThemeDisplay; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.security.lang.DoPrivilegedUtil; import com.liferay.portal.struts.StrutsActionPortletURL; import com.liferay.portal.util.PropsValues; import java.io.Writer; import java.lang.reflect.Constructor; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.portlet.MimeResponse; import javax.portlet.PortletException; import javax.portlet.PortletModeException; import javax.portlet.PortletPreferences; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletURL; import javax.portlet.PortletURLGenerationListener; import javax.portlet.ResourceURL; import javax.portlet.WindowStateException; import javax.portlet.filter.PortletResponseWrapper; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * @author Brian Wing Shun Chan */ public abstract class PortletResponseImpl implements LiferayPortletResponse { public static PortletResponseImpl getPortletResponseImpl( PortletResponse portletResponse) { while (!(portletResponse instanceof PortletResponseImpl)) { if (portletResponse instanceof PortletResponseWrapper) { PortletResponseWrapper portletResponseWrapper = (PortletResponseWrapper)portletResponse; portletResponse = portletResponseWrapper.getResponse(); } else { throw new RuntimeException( "Unable to unwrap the portlet response from " + portletResponse.getClass()); } } return (PortletResponseImpl)portletResponse; } @Override public void addDateHeader(String name, long date) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } Long[] values = (Long[])_headers.get(name); if (values == null) { setDateHeader(name, date); } else { values = ArrayUtil.append(values, Long.valueOf(date)); _headers.put(name, values); } } @Override public void addHeader(String name, String value) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } String[] values = (String[])_headers.get(name); if (values == null) { setHeader(name, value); } else { values = ArrayUtil.append(values, value); _headers.put(name, values); } } @Override public void addIntHeader(String name, int value) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } Integer[] values = (Integer[])_headers.get(name); if (values == null) { setIntHeader(name, value); } else { values = ArrayUtil.append(values, Integer.valueOf(value)); _headers.put(name, values); } } @Override public void addProperty(Cookie cookie) { if (cookie == null) { throw new IllegalArgumentException(); } Cookie[] cookies = (Cookie[])_headers.get("cookies"); if (cookies == null) { _headers.put("cookies", new Cookie[] {cookie}); } else { cookies = ArrayUtil.append(cookies, cookie); _headers.put("cookies", cookies); } } @Override public void addProperty(String key, Element element) { if (key == null) { throw new IllegalArgumentException(); } if (StringUtil.equalsIgnoreCase( key, MimeResponse.MARKUP_HEAD_ELEMENT)) { List<Element> values = _markupHeadElements.get(key); if (values != null) { if (element != null) { values.add(element); } else { _markupHeadElements.remove(key); } } else { if (element != null) { values = new ArrayList<>(); values.add(element); _markupHeadElements.put(key, values); } } } } @Override public void addProperty(String key, String value) { if (Validator.isNull(key)) { throw new IllegalArgumentException(); } addHeader(key, value); } @Override public PortletURL createActionURL() { return createActionURL(portletName); } @Override public LiferayPortletURL createActionURL(String portletName) { return createLiferayPortletURL( portletName, PortletRequest.ACTION_PHASE); } @Override public Element createElement(String tagName) throws DOMException { if (_document == null) { try { DocumentBuilderFactory documentBuilderFactory = SecureXMLFactoryProviderUtil.newDocumentBuilderFactory(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); _document = documentBuilder.newDocument(); } catch (ParserConfigurationException pce) { throw new DOMException( DOMException.INVALID_STATE_ERR, pce.getMessage()); } } return _document.createElement(tagName); } @Override public LiferayPortletURL createLiferayPortletURL( long plid, String portletName, String lifecycle) { return createLiferayPortletURL(plid, portletName, lifecycle, true); } @Override public LiferayPortletURL createLiferayPortletURL( long plid, String portletName, String lifecycle, boolean includeLinkToLayoutUuid) { return DoPrivilegedUtil.wrap( new LiferayPortletURLPrivilegedAction( plid, portletName, lifecycle, includeLinkToLayoutUuid)); } @Override public LiferayPortletURL createLiferayPortletURL(String lifecycle) { return createLiferayPortletURL(portletName, lifecycle); } @Override public LiferayPortletURL createLiferayPortletURL( String portletName, String lifecycle) { return createLiferayPortletURL(_plid, portletName, lifecycle); } @Override public PortletURL createRenderURL() { return createRenderURL(portletName); } @Override public LiferayPortletURL createRenderURL(String portletName) { return createLiferayPortletURL( portletName, PortletRequest.RENDER_PHASE); } @Override public ResourceURL createResourceURL() { return createResourceURL(portletName); } @Override public LiferayPortletURL createResourceURL(String portletName) { return createLiferayPortletURL( portletName, PortletRequest.RESOURCE_PHASE); } @Override public String encodeURL(String path) { if ((path == null) || (!path.startsWith("#") && !path.startsWith("/") && !path.contains("://"))) { // Allow '#' as well to workaround a bug in Oracle ADF 10.1.3 throw new IllegalArgumentException( "URL path must start with a '/' or include '://'"); } if (_urlEncoder != null) { return _urlEncoder.encodeURL(response, path); } else { return path; } } public long getCompanyId() { return _companyId; } public HttpServletRequest getHttpServletRequest() { return portletRequestImpl.getHttpServletRequest(); } @Override public HttpServletResponse getHttpServletResponse() { return response; } public abstract String getLifecycle(); @Override public String getNamespace() { if (_wsrp) { return "wsrp_rewrite_"; } if (_namespace == null) { _namespace = PortalUtil.getPortletNamespace(portletName); } return _namespace; } public long getPlid() { return _plid; } @Override public Portlet getPortlet() { return _portlet; } public String getPortletName() { return portletName; } public PortletRequestImpl getPortletRequest() { return portletRequestImpl; } @Override public Map<String, String[]> getProperties() { Map<String, String[]> properties = new LinkedHashMap<>(); for (Map.Entry<String, Object> entry : _headers.entrySet()) { String name = entry.getKey(); Object[] values = (Object[])entry.getValue(); String[] valuesString = new String[values.length]; for (int i = 0; i < values.length; i++) { valuesString[i] = values[i].toString(); } properties.put(name, valuesString); } return properties; } public URLEncoder getUrlEncoder() { return _urlEncoder; } @Override public void setDateHeader(String name, long date) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } if (date <= 0) { _headers.remove(name); } else { _headers.put(name, new Long[] {Long.valueOf(date)}); } } @Override public void setHeader(String name, String value) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } if (Validator.isNull(value)) { _headers.remove(name); } else { _headers.put(name, new String[] {value}); } } @Override public void setIntHeader(String name, int value) { if (Validator.isNull(name)) { throw new IllegalArgumentException(); } if (value <= 0) { _headers.remove(name); } else { _headers.put(name, new Integer[] {Integer.valueOf(value)}); } } public void setPlid(long plid) { _plid = plid; if (_plid <= 0) { Layout layout = (Layout)portletRequestImpl.getAttribute( WebKeys.LAYOUT); if (layout != null) { _plid = layout.getPlid(); } } } @Override public void setProperty(String key, String value) { if (key == null) { throw new IllegalArgumentException(); } setHeader(key, value); } public void setURLEncoder(URLEncoder urlEncoder) { _urlEncoder = urlEncoder; } public void transferHeaders(HttpServletResponse response) { for (Map.Entry<String, Object> entry : _headers.entrySet()) { String name = entry.getKey(); Object values = entry.getValue(); if (values instanceof Integer[]) { Integer[] intValues = (Integer[])values; for (int value : intValues) { if (response.containsHeader(name)) { response.addIntHeader(name, value); } else { response.setIntHeader(name, value); } } } else if (values instanceof Long[]) { Long[] dateValues = (Long[])values; for (long value : dateValues) { if (response.containsHeader(name)) { response.addDateHeader(name, value); } else { response.setDateHeader(name, value); } } } else if (values instanceof String[]) { String[] stringValues = (String[])values; for (String value : stringValues) { if (response.containsHeader(name)) { response.addHeader(name, value); } else { response.setHeader(name, value); } } } else if (values instanceof Cookie[]) { Cookie[] cookies = (Cookie[])values; for (Cookie cookie : cookies) { response.addCookie(cookie); } } } } @Override public void transferMarkupHeadElements() { List<Element> elements = _markupHeadElements.get( MimeResponse.MARKUP_HEAD_ELEMENT); if ((elements == null) || elements.isEmpty()) { return; } HttpServletRequest request = getHttpServletRequest(); List<String> markupHeadElements = (List<String>)request.getAttribute( MimeResponse.MARKUP_HEAD_ELEMENT); if (markupHeadElements == null) { markupHeadElements = new ArrayList<>(); request.setAttribute( MimeResponse.MARKUP_HEAD_ELEMENT, markupHeadElements); } for (Element element : elements) { try { Writer writer = new UnsyncStringWriter(); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform( new DOMSource(element), new StreamResult(writer)); markupHeadElements.add(writer.toString()); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } } } protected LiferayPortletURL doCreateLiferayPortletURL( long plid, String portletName, String lifecycle, boolean includeLinkToLayoutUuid) { try { Layout layout = (Layout)portletRequestImpl.getAttribute( WebKeys.LAYOUT); ThemeDisplay themeDisplay = (ThemeDisplay)portletRequestImpl.getAttribute( WebKeys.THEME_DISPLAY); if ((layout == null) && (themeDisplay != null)) { layout = themeDisplay.getLayout(); } if (_portletSetup == null) { if (themeDisplay == null) { _portletSetup = PortletPreferencesFactoryUtil. getStrictLayoutPortletSetup( layout, this.portletName); } else { _portletSetup = themeDisplay.getStrictLayoutPortletSetup( layout, this.portletName); } } String linkToLayoutUuid = GetterUtil.getString( _portletSetup.getValue("portletSetupLinkToLayoutUuid", null)); if (PropsValues.PORTLET_CROSS_LAYOUT_INVOCATION_MODE.equals( "render") && !PortletRequest.RENDER_PHASE.equals(lifecycle)) { includeLinkToLayoutUuid = false; } if (Validator.isNotNull(linkToLayoutUuid) && includeLinkToLayoutUuid) { try { Layout linkedLayout = LayoutLocalServiceUtil.getLayoutByUuidAndGroupId( linkToLayoutUuid, layout.getGroupId(), layout.isPrivateLayout()); plid = linkedLayout.getPlid(); } catch (PortalException pe) { // LPS-52675 if (_log.isDebugEnabled()) { _log.debug(pe, pe); } } } } catch (SystemException se) { if (_log.isWarnEnabled()) { _log.warn(se); } } if (plid == LayoutConstants.DEFAULT_PLID) { plid = _plid; } LiferayPortletURL portletURL = null; Portlet portlet = getPortlet(); String portletURLClass = portlet.getPortletURLClass(); if (portlet.getPortletId().equals(portletName) && Validator.isNotNull(portletURLClass)) { if (portletURLClass.equals( StrutsActionPortletURL.class.getName())) { portletURL = new StrutsActionPortletURL(this, plid, lifecycle); } else { try { Constructor<? extends PortletURLImpl> constructor = _constructors.get(portletURLClass); if (constructor == null) { Class<?> portletURLClassObj = Class.forName( portletURLClass); constructor = (Constructor<? extends PortletURLImpl>) portletURLClassObj.getConstructor( new Class<?>[] { PortletResponseImpl.class, long.class, String.class }); _constructors.put(portletURLClass, constructor); } portletURL = constructor.newInstance( new Object[] {this, plid, lifecycle}); } catch (Exception e) { _log.error(e); } } } if (portletURL == null) { if (portletName.equals(portlet.getPortletId())) { portletURL = PortletURLFactoryUtil.create( portletRequestImpl, portlet, plid, lifecycle); } else { portletURL = PortletURLFactoryUtil.create( portletRequestImpl, portletName, plid, lifecycle); } } PortletApp portletApp = portlet.getPortletApp(); Set<PortletURLListener> portletURLListeners = portletApp.getPortletURLListeners(); for (PortletURLListener portletURLListener : portletURLListeners) { try { PortletURLGenerationListener portletURLGenerationListener = PortletURLListenerFactory.create(portletURLListener); if (lifecycle.equals(PortletRequest.ACTION_PHASE)) { portletURLGenerationListener.filterActionURL(portletURL); } else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) { portletURLGenerationListener.filterRenderURL(portletURL); } else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) { portletURLGenerationListener.filterResourceURL(portletURL); } } catch (PortletException pe) { _log.error(pe, pe); } } try { portletURL.setWindowState(portletRequestImpl.getWindowState()); } catch (WindowStateException wse) { _log.error(wse.getMessage()); } try { portletURL.setPortletMode(portletRequestImpl.getPortletMode()); } catch (PortletModeException pme) { _log.error(pme.getMessage()); } if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) { portletURL.setCopyCurrentRenderParameters(true); } return portletURL; } protected void init( PortletRequestImpl portletRequestImpl, HttpServletResponse response) { this.portletRequestImpl = portletRequestImpl; this.response = response; _portlet = portletRequestImpl.getPortlet(); portletName = _portlet.getPortletId(); _companyId = _portlet.getCompanyId(); _wsrp = ParamUtil.getBoolean( portletRequestImpl.getHttpServletRequest(), "wsrp"); setPlid(portletRequestImpl.getPlid()); } /** * @deprecated As of 7.0.0, replaced by {@link * #init(PortletRequestImpl, HttpServletResponse)} */ @Deprecated protected void init( PortletRequestImpl portletRequestImpl, HttpServletResponse response, String portletName, long companyId, long plid) { init(portletRequestImpl, response); } protected String portletName; protected PortletRequestImpl portletRequestImpl; protected HttpServletResponse response; private static final Log _log = LogFactoryUtil.getLog( PortletResponseImpl.class); private long _companyId; private final Map<String, Constructor<? extends PortletURLImpl>> _constructors = new ConcurrentHashMap<>(); private Document _document; private final Map<String, Object> _headers = new LinkedHashMap<>(); private final Map<String, List<Element>> _markupHeadElements = new LinkedHashMap<>(); private String _namespace; private long _plid; private Portlet _portlet; private PortletPreferences _portletSetup; private URLEncoder _urlEncoder; private boolean _wsrp; private class LiferayPortletURLPrivilegedAction implements PrivilegedAction<LiferayPortletURL> { public LiferayPortletURLPrivilegedAction( long plid, String portletName, String lifecycle, boolean includeLinkToLayoutUuid) { _plid = plid; _portletName = portletName; _lifecycle = lifecycle; _includeLinkToLayoutUuid = includeLinkToLayoutUuid; } @Override public LiferayPortletURL run() { return doCreateLiferayPortletURL( _plid, _portletName, _lifecycle, _includeLinkToLayoutUuid); } private final boolean _includeLinkToLayoutUuid; private final String _lifecycle; private long _plid; private final String _portletName; } }