/** * 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.frontend.js.spa.web.internal.servlet.taglib.util; import com.liferay.frontend.js.spa.web.configuration.SPAConfiguration; import com.liferay.frontend.js.spa.web.configuration.SPAConfigurationUtil; import com.liferay.osgi.util.StringPlus; import com.liferay.portal.kernel.json.JSONArray; import com.liferay.portal.kernel.json.JSONFactoryUtil; 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.service.PortletLocalService; import com.liferay.portal.kernel.servlet.ServletResponseConstants; import com.liferay.portal.kernel.theme.ThemeDisplay; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.portal.kernel.util.ResourceBundleUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Time; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.ResourceBundle; import java.util.concurrent.CopyOnWriteArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; 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.Modified; import org.osgi.service.component.annotations.Reference; import org.osgi.service.metatype.annotations.Designate; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * @author Bruno Basto */ @Component( configurationPid = "com.liferay.frontend.js.spa.web.configuration.SPAConfiguration", service = SPAUtil.class ) @Designate(ocd = SPAConfiguration.class) public class SPAUtil { public long getCacheExpirationTime(long companyId) { return _cacheExpirationTime; } public String getExcludedPaths() { return _spaExcludedPaths; } public ResourceBundle getLanguageResourceBundle(Locale locale) { return ResourceBundleUtil.getBundle( "content.Language", locale, getClass()); } public String getLoginRedirect(HttpServletRequest request) { return ParamUtil.getString(request, _redirectParamName); } public String getNavigationExceptionSelectors() { return _navigationExceptionSelectorsString; } public String getPortletsBlacklist(ThemeDisplay themeDisplay) { StringBundler sb = new StringBundler(); sb.append(StringPool.OPEN_CURLY_BRACE); List<Portlet> companyPortlets = _portletLocalService.getPortlets( themeDisplay.getCompanyId()); for (Portlet portlet : companyPortlets) { if (!portlet.isSinglePageApplication() && !portlet.isUndeployedPortlet() && portlet.isActive() && portlet.isReady()) { sb.append(StringPool.QUOTE); sb.append(portlet.getPortletId()); sb.append("\":true,"); } } if (sb.index() == 1) { sb.append(StringPool.CLOSE_CURLY_BRACE); } else { sb.setIndex(sb.index() - 1); sb.append("\":true}"); } return sb.toString(); } public int getRequestTimeout() { return _spaConfiguration.requestTimeout(); } public int getUserNotificationTimeout() { return _spaConfiguration.userNotificationTimeout(); } public String getValidStatusCodes() { return _VALID_STATUS_CODES; } public boolean isClearScreensCache( HttpServletRequest request, HttpSession session) { boolean singlePageApplicationClearCache = GetterUtil.getBoolean( request.getAttribute(WebKeys.SINGLE_PAGE_APPLICATION_CLEAR_CACHE)); if (singlePageApplicationClearCache) { return true; } String portletId = request.getParameter("p_p_id"); if (Validator.isNull(portletId)) { return false; } String singlePageApplicationLastPortletId = (String)session.getAttribute( WebKeys.SINGLE_PAGE_APPLICATION_LAST_PORTLET_ID); if (Validator.isNotNull(singlePageApplicationLastPortletId) && !Objects.equals(portletId, singlePageApplicationLastPortletId)) { return true; } return false; } public boolean isDebugEnabled() { return _log.isDebugEnabled(); } @Activate protected void activate( BundleContext bundleContext, SPAConfiguration spaConfiguration) throws InvalidSyntaxException { _cacheExpirationTime = _getCacheExpirationTime(spaConfiguration); _spaConfiguration = spaConfiguration; Collections.addAll( _navigationExceptionSelectors, _spaConfiguration.navigationExceptionSelectors()); _navigationExceptionSelectorsString = ListUtil.toString( _navigationExceptionSelectors, (String)null, StringPool.BLANK); Filter filter = bundleContext.createFilter( "(&(objectClass=java.lang.Object)(" + _SPA_NAVIGATION_EXCEPTION_SELECTOR_KEY + "=*))"); _navigationExceptionSelectorTracker = new ServiceTracker<>( bundleContext, filter, new NavigationExceptionSelectorTrackerCustomizer(bundleContext)); _navigationExceptionSelectorTracker.open(); } @Deactivate protected void deactivate() { _navigationExceptionSelectorTracker.close(); } @Modified protected void modified(SPAConfiguration spaConfiguration) { _cacheExpirationTime = _getCacheExpirationTime(spaConfiguration); _navigationExceptionSelectors.removeAll( Arrays.asList(_spaConfiguration.navigationExceptionSelectors())); _spaConfiguration = spaConfiguration; Collections.addAll( _navigationExceptionSelectors, _spaConfiguration.navigationExceptionSelectors()); _navigationExceptionSelectorsString = ListUtil.toString( _navigationExceptionSelectors, (String)null, StringPool.BLANK); } @Reference(unbind = "-") protected void setPortletLocalService( PortletLocalService portletLocalService) { _portletLocalService = portletLocalService; } private long _getCacheExpirationTime(SPAConfiguration spaConfiguration) { long cacheExpirationTime = spaConfiguration.cacheExpirationTime(); if (cacheExpirationTime > 0) { cacheExpirationTime *= Time.MINUTE; } return cacheExpirationTime; } private static final String _SPA_NAVIGATION_EXCEPTION_SELECTOR_KEY = "javascript.single.page.application.navigation.exception.selector"; private static final String _VALID_STATUS_CODES; private static final Log _log = LogFactoryUtil.getLog(SPAUtil.class); private static final List<String> _navigationExceptionSelectors = new CopyOnWriteArrayList<>(); private static volatile String _navigationExceptionSelectorsString; private static final String _redirectParamName; private static final String _spaExcludedPaths; static { Class<?> clazz = ServletResponseConstants.class; JSONArray jsonArray = JSONFactoryUtil.createJSONArray(); for (Field field : clazz.getDeclaredFields()) { try { jsonArray.put(field.getInt(null)); } catch (Exception e) { } } _VALID_STATUS_CODES = jsonArray.toJSONString(); String portletNamespace = PortalUtil.getPortletNamespace( PropsUtil.get(PropsKeys.AUTH_LOGIN_PORTLET_NAME)); _redirectParamName = portletNamespace.concat("redirect"); jsonArray = JSONFactoryUtil.createJSONArray(); String[] excludedPaths = StringUtil.split( SPAConfigurationUtil.get("spa.excluded.paths")); for (String excludedPath : excludedPaths) { jsonArray.put(excludedPath); } _spaExcludedPaths = jsonArray.toString(); } private long _cacheExpirationTime; private ServiceTracker<Object, Object> _navigationExceptionSelectorTracker; private PortletLocalService _portletLocalService; private SPAConfiguration _spaConfiguration; private static final class NavigationExceptionSelectorTrackerCustomizer implements ServiceTrackerCustomizer<Object, Object> { public NavigationExceptionSelectorTrackerCustomizer( BundleContext bundleContext) { _bundleContext = bundleContext; } @Override public Object addingService(ServiceReference<Object> reference) { List<String> selectors = StringPlus.asList( reference.getProperty(_SPA_NAVIGATION_EXCEPTION_SELECTOR_KEY)); _navigationExceptionSelectors.addAll(selectors); _navigationExceptionSelectorsString = ListUtil.toString( _navigationExceptionSelectors, (String)null, StringPool.BLANK); Object service = _bundleContext.getService(reference); _serviceReferences.add(reference); return service; } @Override public void modifiedService( ServiceReference<Object> reference, Object service) { removedService(reference, service); addingService(reference); } @Override public void removedService( ServiceReference<Object> reference, Object service) { List<String> selectors = StringPlus.asList( reference.getProperty(_SPA_NAVIGATION_EXCEPTION_SELECTOR_KEY)); _navigationExceptionSelectors.removeAll(selectors); _navigationExceptionSelectorsString = ListUtil.toString( _navigationExceptionSelectors, (String)null, StringPool.BLANK); _serviceReferences.remove(reference); _bundleContext.ungetService(reference); } private final BundleContext _bundleContext; private final List<ServiceReference<Object>> _serviceReferences = new CopyOnWriteArrayList<>(); } }