All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.portlet.internal.HeaderResponseImpl Maven / Gradle / Ivy

There is a newer version: 7.4.3.112-ga112
Show newest version
/**
 * 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.internal;

import com.liferay.petra.io.unsync.UnsyncByteArrayOutputStream;
import com.liferay.petra.io.unsync.UnsyncPrintWriter;
import com.liferay.petra.io.unsync.UnsyncStringWriter;
import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Portlet;
import com.liferay.portal.kernel.model.portlet.PortletDependency;
import com.liferay.portal.kernel.model.portlet.PortletDependencyFactoryUtil;
import com.liferay.portal.kernel.portlet.LiferayHeaderResponse;
import com.liferay.portal.kernel.servlet.taglib.util.OutputData;
import com.liferay.portal.kernel.theme.PortletDisplay;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.util.GetterUtil;
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.xml.StAXReaderUtil;

import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.portlet.HeaderRequest;
import javax.portlet.PortletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

/**
 * @author Neil Griffin
 */
public class HeaderResponseImpl
	extends MimeResponseImpl implements LiferayHeaderResponse {

	@Override
	public void addDependency(String name, String scope, String version) {
		addDependency(name, scope, version, null);
	}

	@Override
	public void addDependency(
		String name, String scope, String version, String xml) {

		if (Validator.isNull(name)) {
			throw new IllegalArgumentException();
		}

		if ("PortletHub".equals(name) && "javax.portlet".equals(scope)) {
			return;
		}

		if (xml != null) {
			xml = StringUtil.trim(xml);

			if (xml.isEmpty()) {
				return;
			}

			xml = _addClosingTags(xml);

			List parsedElements = _parseElements(xml);

			if (parsedElements.isEmpty()) {
				return;
			}

			if (parsedElements.size() > 1) {
				throw new IllegalArgumentException(
					"More than one element in markup: " + xml);
			}

			ParsedElement parsedElement = parsedElements.get(0);

			if (!parsedElement.isValid()) {
				throw new IllegalArgumentException(
					"Invalid dependency: " + xml);
			}
		}

		PortletDependency portletDependency =
			PortletDependencyFactoryUtil.createPortletDependency(
				name, scope, version, xml, portletRequestImpl);

		if (PortletDependency.Type.OTHER == portletDependency.getType()) {
			if (xml != null) {
				throw new IllegalArgumentException(
					"Invalid dependency markup: " + xml);
			}

			_log.error("Unable to add dependency: " + portletDependency);

			return;
		}

		Portlet portlet = getPortlet();

		if (((PortletDependency.Type.CSS == portletDependency.getType()) &&
			 portlet.isPortletDependencyCssEnabled()) ||
			((PortletDependency.Type.JAVASCRIPT ==
				portletDependency.getType()) &&
			 portlet.isPortletDependencyJavaScriptEnabled()) ||
			(PortletDependency.Type.OTHER == portletDependency.getType())) {

			_addDependencyToHead(name, scope, version, null, portletDependency);
		}
	}

	@Override
	public void flushBuffer() {
		if (_printWriter != null) {
			_printWriter.flush();
		}

		_calledFlushBuffer = true;
	}

	@Override
	public int getBufferSize() {
		return _bufferSize;
	}

	@Override
	public String getLifecycle() {
		return PortletRequest.HEADER_PHASE;
	}

	@Override
	public OutputStream getPortletOutputStream() {
		if (_calledGetWriter) {
			throw new IllegalStateException(
				"Unable to obtain OutputStream because Writer is already in " +
					"use");
		}

		if (_portletOutputStream == null) {
			_portletOutputStream = new HeaderOutputStream();
		}

		_calledGetPortletOutputStream = true;

		return _portletOutputStream;
	}

	@Override
	public PrintWriter getWriter() {
		if (_calledGetPortletOutputStream) {
			throw new IllegalStateException(
				"Unable to obtain Writer because OutputStream is already in " +
					"use");
		}

		if (_printWriter == null) {
			_printWriter = new HeaderPrintWriter(new UnsyncStringWriter());
		}

		_calledGetWriter = true;

		return _printWriter;
	}

	public void init(
		PortletRequestImpl portletRequestImpl, HttpServletResponse response,
		List portletDependencies) {

		super.init(portletRequestImpl, response);

		if (portletDependencies != null) {
			for (PortletDependency portletDependency : portletDependencies) {
				addDependency(
					portletDependency.getName(), portletDependency.getScope(),
					portletDependency.getVersion());
			}
		}
	}

	@Override
	public boolean isCalledFlushBuffer() {
		return _calledFlushBuffer;
	}

	@Override
	public boolean isCalledGetPortletOutputStream() {
		return _calledGetPortletOutputStream;
	}

	@Override
	public boolean isCalledGetWriter() {
		return _calledGetWriter;
	}

	@Override
	public boolean isCommitted() {
		if (_calledFlushBuffer || super.isCommitted()) {
			return true;
		}

		return false;
	}

	@Override
	public void reset() {
		if (_calledFlushBuffer) {
			throw new IllegalStateException(
				"Unable to reset a buffer that has been flushed");
		}

		_portletOutputStream = null;
		_printWriter = null;
	}

	@Override
	public void resetBuffer() {
		reset();
	}

	@Override
	public void setBufferSize(int bufferSize) {
		if (_calledFlushBuffer) {
			throw new IllegalStateException(
				"Unable to set buffer size because buffer has been flushed");
		}

		_bufferSize = bufferSize;
	}

	@Override
	public void setContentType(String contentType) {
		if ((_printWriter != null) || (_portletOutputStream != null)) {
			return;
		}

		super.setContentType(contentType);
	}

	@Override
	public void setTitle(String title) {

		// See LEP-2188

		ThemeDisplay themeDisplay =
			(ThemeDisplay)portletRequestImpl.getAttribute(
				WebKeys.THEME_DISPLAY);

		PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();

		portletDisplay.setTitle(title);
	}

	@Override
	public void writeToHead() {
		if (_portletOutputStream != null) {
			_addXMLToHead(
				portletName, getNamespace(), _portletOutputStream.toString());
		}
		else if (_printWriter != null) {
			_addXMLToHead(portletName, getNamespace(), _printWriter.toString());
		}
	}

	private static String _addClosingTags(String xml) {
		xml = _addClosingTags(xml, "");
		xml = _addClosingTags(xml, "");
		xml = _addClosingTags(xml, "");
		xml = _addClosingTags(xml, "");

		return xml;
	}

	private static String _addClosingTags(
		String xml, String opening, String closing) {

		StringBundler sb = null;

		int fromIndex = 0;
		int index = 0;
		int sbIndex = 0;

		while ((index = xml.indexOf(opening, fromIndex)) != -1) {
			int openingEnd = xml.indexOf(CharPool.GREATER_THAN, index);

			if (openingEnd == -1) {
				break;
			}

			fromIndex = openingEnd + 1;

			if (xml.regionMatches(fromIndex, closing, 0, closing.length())) {
				fromIndex += closing.length();
			}
			else {
				if (sb == null) {
					sb = new StringBundler();
				}

				sb.append(xml.substring(sbIndex, fromIndex));

				sbIndex = fromIndex;

				sb.append(closing);
			}
		}

		if (sb == null) {
			return xml;
		}

		sb.append(xml.substring(sbIndex));

		return sb.toString();
	}

	private void _addDependencyToHead(
		String name, String scope, String version, ParsedElement parsedElement,
		PortletDependency portletDependency) {

		if (Validator.isNull(scope)) {
			scope = StringPool.BLANK;
		}

		SemVer semVer = SemVer._DEFAULT;
		String versionKey = version;

		if (Validator.isNull(version)) {
			versionKey = StringUtil.randomId();
		}
		else {
			semVer = new SemVer(version);
		}

		HeaderRequest headerRequest = (HeaderRequest)getPortletRequest();

		HttpServletRequest httpServletRequest =
			PortalUtil.getHttpServletRequest(headerRequest);

		OutputData outputData = (OutputData)httpServletRequest.getAttribute(
			WebKeys.OUTPUT_DATA);

		if (outputData == null) {
			outputData = new OutputData();

			httpServletRequest.setAttribute(WebKeys.OUTPUT_DATA, outputData);
		}

		Set outputKeys = outputData.getOutputKeys();

		Iterator iterator = outputKeys.iterator();

		while (iterator.hasNext()) {
			String existingKey = iterator.next();

			String[] existingKeyParts = StringUtil.split(
				existingKey, CharPool.COLON);

			if (existingKeyParts.length == 3) {
				String existingName = existingKeyParts[0];
				String existingScope = existingKeyParts[1];

				if (name.equals(existingName) && scope.equals(existingScope)) {
					String existingVersion = existingKeyParts[2];

					SemVer existingSemVer = new SemVer(existingVersion);

					if (existingSemVer.compareTo(semVer) < 0) {
						iterator.remove();
						outputData.setDataSB(
							existingKey, WebKeys.PAGE_TOP, null);
					}

					break;
				}
			}
		}

		StringBundler sb = new StringBundler(5);

		sb.append(name);
		sb.append(StringPool.COLON);
		sb.append(scope);
		sb.append(StringPool.COLON);
		sb.append(versionKey);

		String outputKey = sb.toString();

		if (outputData.addOutputKey(outputKey)) {
			if (parsedElement != null) {
				outputData.addDataSB(
					outputKey, WebKeys.PAGE_TOP,
					parsedElement.toStringBundler());
			}
			else if (portletDependency != null) {
				outputData.addDataSB(
					outputKey, WebKeys.PAGE_TOP,
					portletDependency.toStringBundler());
			}
		}
	}

	private void _addXMLToHead(String name, String scope, String xml) {
		xml = StringUtil.trim(xml);

		if (Validator.isBlank(xml)) {
			return;
		}

		if (_log.isDebugEnabled()) {
			_log.debug("Markup before conversion to well-formed XML:" + xml);
		}

		if (!xml.startsWith("")) {
			xml = "".concat(xml);
		}

		if (!xml.endsWith("")) {
			xml = xml.concat("");
		}

		xml = _addClosingTags(xml);

		if (_log.isDebugEnabled()) {
			_log.debug("Markup after conversion to well-formed XML:" + xml);
		}

		for (ParsedElement parsedElement : _parseElements(xml)) {
			_addDependencyToHead(name, scope, null, parsedElement, null);
		}
	}

	private List _parseElements(String xml) {
		List parsedElements = new ArrayList<>();

		XMLStreamReader xmlStreamReader = null;

		try {
			XMLInputFactory xmlInputFactory =
				StAXReaderUtil.getXMLInputFactory();

			xmlStreamReader = xmlInputFactory.createXMLStreamReader(
				new UnsyncStringReader(xml));

			Map elementAttributeValues = null;
			String elementName = null;
			StringBundler elementTextSB = null;

			while (xmlStreamReader.hasNext()) {
				int event = xmlStreamReader.next();

				if (elementTextSB == null) {
					elementTextSB = new StringBundler();
				}

				if (event == XMLStreamConstants.CHARACTERS) {
					String text = xmlStreamReader.getText();

					if (text != null) {
						text = StringUtil.trim(text);

						if (!text.isEmpty()) {
							elementTextSB.append(text);
						}
					}
				}
				else if (event == XMLStreamConstants.COMMENT) {
					if (elementName != null) {
						elementTextSB.append("\n");
					}
				}
				else if (event == XMLStreamConstants.END_ELEMENT) {
					if (elementName != null) {
						parsedElements.add(
							new ParsedElement(
								elementName, elementAttributeValues,
								elementTextSB.toString(), true));

						elementAttributeValues = null;
						elementName = null;
						elementTextSB.setIndex(0);
					}
				}
				else if (event == XMLStreamConstants.START_ELEMENT) {
					String localName = xmlStreamReader.getLocalName();

					if (!localName.equals("head")) {
						if (localName.equals("link") ||
							localName.equals("meta") ||
							localName.equals("script") ||
							localName.equals("style")) {

							elementName = localName;

							elementAttributeValues = new LinkedHashMap<>();

							int attributeCount =
								xmlStreamReader.getAttributeCount();

							for (int i = 0; i < attributeCount; i++) {
								elementAttributeValues.put(
									xmlStreamReader.getAttributeLocalName(i),
									xmlStreamReader.getAttributeValue(i));
							}
						}
						else {
							throw new XMLStreamException(
								"Invalid element: " + localName);
						}
					}
				}
			}
		}
		catch (XMLStreamException xmlse) {
			_log.error(xmlse, xmlse);

			parsedElements.add(new ParsedElement(null, null, null, false));
		}
		finally {
			if (xmlStreamReader != null) {
				try {
					xmlStreamReader.close();
				}
				catch (XMLStreamException xmlse) {
					_log.error(xmlse, xmlse);
				}
			}
		}

		return parsedElements;
	}

	private static final Log _log = LogFactoryUtil.getLog(
		HeaderResponseImpl.class);

	private int _bufferSize;
	private boolean _calledFlushBuffer;
	private boolean _calledGetPortletOutputStream;
	private boolean _calledGetWriter;
	private OutputStream _portletOutputStream;
	private PrintWriter _printWriter;

	private static class ParsedElement {

		public boolean isValid() {
			return _valid;
		}

		public StringBundler toStringBundler() {
			StringBundler sb = new StringBundler(_attributes.size() * 5 + 7);

			sb.append("\n<");
			sb.append(_name);

			for (Map.Entry entry : _attributes.entrySet()) {
				sb.append(StringPool.SPACE);
				sb.append(entry.getKey());
				sb.append("=\"");
				sb.append(entry.getValue());
				sb.append(StringPool.QUOTE);
			}

			sb.append(StringPool.GREATER_THAN);

			if (!Objects.equals(_name, "link") &&
				!Objects.equals(_name, "meta")) {

				if (_text != null) {
					sb.append(_text);
				}

				sb.append("\n attributes, String text,
			boolean valid) {

			_name = name;

			if (attributes == null) {
				_attributes = Collections.emptyMap();
			}
			else {
				_attributes = attributes;
			}

			_text = text;
			_valid = valid;
		}

		private final Map _attributes;
		private final String _name;
		private final String _text;
		private final boolean _valid;

	}

	private static class SemVer implements Comparable {

		@Override
		public int compareTo(SemVer semVer) {
			int result = Integer.compare(_major, semVer._major);

			if (result != 0) {
				return result;
			}

			result = Integer.compare(_minor, semVer._minor);

			if (result != 0) {
				return result;
			}

			return Integer.compare(_micro, semVer._micro);
		}

		private SemVer(int major, int minor, int micro) {
			_major = major;
			_minor = minor;
			_micro = micro;
		}

		private SemVer(String version) {
			String[] parts = StringUtil.split(version, CharPool.PERIOD);

			if (parts.length > 0) {
				_major = GetterUtil.getInteger(parts[0]);
			}
			else {
				_major = 0;
			}

			if (parts.length > 1) {
				_minor = GetterUtil.getInteger(parts[1]);
			}
			else {
				_minor = 0;
			}

			if (parts.length > 2) {
				_micro = GetterUtil.getInteger(parts[2]);
			}
			else {
				_micro = 0;
			}
		}

		private static final SemVer _DEFAULT = new SemVer(0, 0, 0);

		private final int _major;
		private final int _micro;
		private final int _minor;

	}

	private class HeaderOutputStream extends UnsyncByteArrayOutputStream {

		@Override
		public String toString() {
			try {
				return toString(getCharacterEncoding());
			}
			catch (UnsupportedEncodingException uee) {
				_log.error(uee, uee);
			}

			return StringPool.BLANK;
		}

	}

	private class HeaderPrintWriter extends UnsyncPrintWriter {

		@Override
		public String toString() {
			return _unsyncStringWriter.toString();
		}

		private HeaderPrintWriter(UnsyncStringWriter unsyncStringWriter) {
			super(unsyncStringWriter);

			_unsyncStringWriter = unsyncStringWriter;
		}

		private final UnsyncStringWriter _unsyncStringWriter;

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy