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

com.liferay.frontend.js.web.internal.servlet.FrontendJsWebLanguageServlet Maven / Gradle / Ivy

/**
 * SPDX-FileCopyrightText: (c) 2024 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

package com.liferay.frontend.js.web.internal.servlet;

import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMap;
import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMapFactory;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONException;
import com.liferay.portal.kernel.json.JSONFactory;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.language.Language;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.HttpHeaders;
import com.liferay.portal.kernel.util.ContentTypes;
import com.liferay.portal.kernel.util.DigesterUtil;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.Portal;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.URLUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import java.net.URL;

import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;

/**
 * @author Iván Zaera Avellón
 */
@Component(
	property = {
		"osgi.http.whiteboard.servlet.name=Language Resources Servlet",
		"osgi.http.whiteboard.servlet.pattern=/js/language/*",
		"service.ranking:Integer=" + (Integer.MAX_VALUE - 1000)
	},
	service = Servlet.class
)
public class FrontendJsWebLanguageServlet extends HttpServlet {

	@Activate
	protected void activate(BundleContext bundleContext) {
		_eTags.clear();

		_serviceTrackerMap = ServiceTrackerMapFactory.openSingleValueMap(
			bundleContext, ServletContext.class, null,
			(serviceReference, emitter) -> {
				ServletContext servletContext = bundleContext.getService(
					serviceReference);

				try {
					emitter.emit(servletContext.getContextPath());
				}
				finally {
					bundleContext.ungetService(serviceReference);
				}
			});
	}

	@Deactivate
	protected void deactivate() {
		_eTags.clear();

		_serviceTrackerMap.close();

		_serviceTrackerMap = null;
	}

	@Override
	protected void doGet(
			HttpServletRequest httpServletRequest,
			HttpServletResponse httpServletResponse)
		throws IOException, ServletException {

		String pathInfo = httpServletRequest.getPathInfo();

		// Check if path is valid

		String[] parts = pathInfo.split(StringPool.SLASH);

		if ((parts.length != 4) || !parts[3].equals("all.js")) {
			httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);

			return;
		}

		// Check if browser cache can be used

		String ifNoneMatch = httpServletRequest.getHeader(
			HttpHeaders.IF_NONE_MATCH);

		if (ifNoneMatch != null) {
			String eTag = _eTags.get(pathInfo);

			if ((eTag != null) && eTag.equals(ifNoneMatch)) {
				httpServletResponse.setStatus(
					HttpServletResponse.SC_NOT_MODIFIED);
				httpServletResponse.setContentLength(0);

				return;
			}
		}

		// Check if servlet context exists

		String webContextPath = parts[2];

		ServletContext servletContext = _serviceTrackerMap.getService(
			Portal.PATH_MODULE + StringPool.SLASH + webContextPath);

		if (servletContext == null) {
			httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);

			return;
		}

		// Send response

		Locale locale = LocaleUtil.fromLanguageId(parts[1]);

		String content = _getContent(locale, servletContext);

		if (content == null) {
			httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);

			return;
		}

		String etag =
			StringPool.QUOTE + DigesterUtil.digestBase64("SHA-1", content) +
				StringPool.QUOTE;

		_eTags.put(pathInfo, etag);

		httpServletResponse.setCharacterEncoding(StringPool.UTF8);
		httpServletResponse.setContentType(ContentTypes.TEXT_JAVASCRIPT_UTF8);
		httpServletResponse.setHeader(HttpHeaders.ETAG, etag);

		PrintWriter printWriter = httpServletResponse.getWriter();

		printWriter.write(content);
	}

	private static String _loadTemplate(String name) {
		try (InputStream inputStream =
				FrontendJsWebLanguageServlet.class.getResourceAsStream(
					"dependencies/" + name)) {

			return StringUtil.read(inputStream);
		}
		catch (Exception exception) {
			_log.error("Unable to read template " + name, exception);
		}

		return StringPool.BLANK;
	}

	private String _getContent(Locale locale, ServletContext servletContext)
		throws IOException {

		JSONArray languageKeysJSONArray = _getLanguageKeysJSONArray(
			servletContext);

		if (languageKeysJSONArray == null) {
			return null;
		}

		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < languageKeysJSONArray.length(); i++) {
			String key = languageKeysJSONArray.getString(i);

			String label = _language.get(locale, key);

			sb.append(StringPool.APOSTROPHE);
			sb.append(key.replaceAll("'", "\\\\'"));
			sb.append("':'");
			sb.append(label.replaceAll("'", "\\\\'"));
			sb.append("',\n");
		}

		return StringUtil.replace(
			_TPL_JAVA_SCRIPT, new String[] {"[$LABELS$]"},
			new String[] {sb.toString()});
	}

	private JSONArray _getLanguageKeysJSONArray(ServletContext servletContext)
		throws IOException {

		URL url = servletContext.getResource("/language.json");

		if (url == null) {
			return null;
		}

		try {
			JSONObject jsonObject = _jsonFactory.createJSONObject(
				URLUtil.toString(url));

			return jsonObject.getJSONArray("keys");
		}
		catch (JSONException jsonException) {
			throw new IOException(
				"Invalid language JSON file " + url, jsonException);
		}
	}

	private static final String _TPL_JAVA_SCRIPT;

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

	static {
		_TPL_JAVA_SCRIPT = _loadTemplate("all.js.tpl");
	}

	private final ConcurrentHashMap _eTags =
		new ConcurrentHashMap<>();

	@Reference
	private JSONFactory _jsonFactory;

	@Reference
	private Language _language;

	private ServiceTrackerMap _serviceTrackerMap;

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy