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

com.liferay.site.initializer.extender.internal.SiteInitializerClientExtension Maven / Gradle / Ivy

/**
 * SPDX-FileCopyrightText: (c) 2023 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.site.initializer.extender.internal;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.liferay.headless.site.dto.v1_0.Site;
import com.liferay.headless.site.resource.v1_0.SiteResource;
import com.liferay.layout.util.LayoutServiceContextHelper;
import com.liferay.petra.lang.SafeCloseable;
import com.liferay.petra.reflect.ReflectionUtil;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.model.Company;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.security.auth.CompanyThreadLocal;
import com.liferay.portal.kernel.security.auth.PrincipalThreadLocal;
import com.liferay.portal.kernel.security.permission.PermissionChecker;
import com.liferay.portal.kernel.security.permission.PermissionThreadLocal;
import com.liferay.portal.kernel.service.CompanyLocalService;
import com.liferay.portal.kernel.service.ServiceContext;
import com.liferay.portal.kernel.service.ServiceContextThreadLocal;
import com.liferay.portal.kernel.service.UserLocalService;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.transaction.Propagation;
import com.liferay.portal.kernel.transaction.TransactionConfig;
import com.liferay.portal.kernel.transaction.TransactionInvokerUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.PropsUtil;
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.permission.PermissionCacheUtil;
import com.liferay.portal.vulcan.multipart.BinaryFile;
import com.liferay.portal.vulcan.multipart.MultipartBody;
import com.liferay.site.initializer.extender.SiteInitializerUtil;

import java.io.File;
import java.io.IOException;

import java.net.URL;
import java.net.URLConnection;

import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;

import javax.servlet.http.HttpServletRequest;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
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;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;

/**
 * @author Nilton Vieira
 */
@Component(service = {})
public class SiteInitializerClientExtension
	implements BundleTrackerCustomizer {

	@Override
	public Bundle addingBundle(Bundle bundle, BundleEvent bundleEvent) {
		Dictionary headers = bundle.getHeaders(
			StringPool.BLANK);

		if (Validator.isNull(
				headers.get("Liferay-Client-Extension-Site-Initializer")) ||
			_isAlreadyProcessed(bundle)) {

			return null;
		}

		PermissionChecker permissionChecker =
			PermissionThreadLocal.getPermissionChecker();
		String name = PrincipalThreadLocal.getName();

		try {
			_initialize(bundle, headers);
		}
		catch (Throwable throwable) {
			throw new RuntimeException(throwable);
		}
		finally {
			PermissionThreadLocal.setPermissionChecker(permissionChecker);
			PrincipalThreadLocal.setName(name);
		}

		return bundle;
	}

	@Override
	public void modifiedBundle(
		Bundle bundle, BundleEvent bundleEvent, Bundle unusedBundle) {
	}

	@Override
	public void removedBundle(
		Bundle bundle, BundleEvent bundleEvent, Bundle unusedBundle) {
	}

	@Activate
	protected void activate(
		BundleContext bundleContext, Map properties) {

		_bundleTracker = new BundleTracker<>(
			bundleContext, Bundle.ACTIVE, this);

		_bundleTracker.open();
	}

	@Deactivate
	protected void deactivate() {
		_bundleTracker.close();
	}

	private Site _addOrUpdateSite(
			String externalReferenceCode, MultipartBody multipartBody,
			User user)
		throws Exception {

		ServiceContext serviceContext =
			ServiceContextThreadLocal.getServiceContext();

		HttpServletRequest httpServletRequest = serviceContext.getRequest();

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

		// LayoutServiceContextHelper#getServiceContextAutoCloseable ensures
		// the wrapped HTTP servlet request has an attribute for WebKeys#LAYOUT
		// and WebKeys#THEME_DISPLAY. However, fragments are processed with
		// com.liferay.taglib.portletext.RuntimeTag which grabs the original
		// HTTP servlet request.

		httpServletRequest.setAttribute(
			WebKeys.LAYOUT, themeDisplay.getLayout());
		httpServletRequest.setAttribute(WebKeys.THEME_DISPLAY, themeDisplay);

		SiteResource.Builder builder = _siteResourceFactory.create();

		SiteResource siteResource = builder.user(
			user
		).httpServletRequest(
			httpServletRequest
		).build();

		return siteResource.putSiteByExternalReferenceCode(
			externalReferenceCode, multipartBody);
	}

	private void _initialize(Bundle bundle, Dictionary headers)
		throws Throwable {

		Map binaryFiles = new HashMap<>();
		Site site = null;

		Enumeration enumeration = bundle.findEntries(
			headers.get("Liferay-Client-Extension-Site-Initializer"), "*",
			true);

		while (enumeration.hasMoreElements()) {
			URL url = enumeration.nextElement();

			if (StringUtil.endsWith(url.getPath(), "site-initializer.json")) {
				String json = SiteInitializerUtil.read(
					bundle, "site-initializer.json", url);

				site = Site.toDTO(json);

				if (site == null) {
					throw new Exception(
						"Unable to transform site from JSON: " + json);
				}
			}
			else if (StringUtil.endsWith(
						url.getPath(), "site-initializer.zip")) {

				URLConnection urlConnection = url.openConnection();

				binaryFiles.put(
					"file",
					new BinaryFile(
						".zip", "site-initializer",
						urlConnection.getInputStream(),
						urlConnection.getContentLength()));
			}
		}

		Company company = _companyLocalService.getCompanyByWebId(
			PropsUtil.get(PropsKeys.COMPANY_DEFAULT_WEB_ID));

		long companyId = company.getCompanyId();

		try (SafeCloseable safeCloseable =
				CompanyThreadLocal.setCompanyIdWithSafeCloseable(companyId)) {

			TransactionInvokerUtil.invoke(
				_transactionConfig,
				new SiteCallable(
					company,
					MultipartBody.of(
						binaryFiles, __ -> _objectMapper,
						Collections.singletonMap("site", site.toString())),
					site,
					_userLocalService.getUserByScreenName(
						companyId,
						PropsUtil.get(PropsKeys.DEFAULT_ADMIN_SCREEN_NAME))));
		}
	}

	private boolean _isAlreadyProcessed(Bundle bundle) {
		String lastModifiedString = String.valueOf(bundle.getLastModified());

		File file = bundle.getDataFile(
			".liferay-client-extension-site-initializer");

		try {
			if ((file != null) && file.exists() &&
				Objects.equals(FileUtil.read(file), lastModifiedString)) {

				return true;
			}

			if (!file.exists()) {
				file.createNewFile();
			}

			FileUtil.write(file, lastModifiedString, true);
		}
		catch (IOException ioException) {
			ReflectionUtil.throwException(ioException);
		}

		return false;
	}

	private static final ObjectMapper _objectMapper = new ObjectMapper();
	private static final TransactionConfig _transactionConfig =
		TransactionConfig.Factory.create(
			Propagation.REQUIRED, new Class[] {Exception.class});

	private BundleTracker _bundleTracker;

	@Reference
	private CompanyLocalService _companyLocalService;

	@Reference
	private LayoutServiceContextHelper _layoutServiceContextHelper;

	@Reference
	private SiteResource.Factory _siteResourceFactory;

	@Reference
	private UserLocalService _userLocalService;

	private class SiteCallable implements Callable {

		@Override
		public Site call() throws Exception {
			try (AutoCloseable autoCloseable =
					_layoutServiceContextHelper.getServiceContextAutoCloseable(
						_company)) {

				return _addOrUpdateSite(
					_site.getExternalReferenceCode(), _multipartBody, _user);
			}
			catch (Exception exception) {
				PermissionCacheUtil.clearCache(_user.getUserId());

				throw exception;
			}
		}

		private SiteCallable(
			Company company, MultipartBody multipartBody, Site site,
			User user) {

			_company = company;
			_multipartBody = multipartBody;
			_site = site;
			_user = user;
		}

		private final Company _company;
		private final MultipartBody _multipartBody;
		private final Site _site;
		private final User _user;

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy