/** * 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.portal.deploy.hot; import com.liferay.portal.apache.bridges.struts.LiferayServletContextProvider; import com.liferay.portal.kernel.configuration.Configuration; import com.liferay.portal.kernel.configuration.ConfigurationFactoryUtil; import com.liferay.portal.kernel.deploy.hot.BaseHotDeployListener; import com.liferay.portal.kernel.deploy.hot.HotDeployEvent; import com.liferay.portal.kernel.deploy.hot.HotDeployException; import com.liferay.portal.kernel.javadoc.JavadocManagerUtil; 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.PortletApp; import com.liferay.portal.kernel.model.PortletCategory; import com.liferay.portal.kernel.model.PortletConstants; import com.liferay.portal.kernel.model.PortletFilter; import com.liferay.portal.kernel.model.PortletURLListener; import com.liferay.portal.kernel.portlet.CustomUserAttributes; import com.liferay.portal.kernel.portlet.InvokerPortlet; import com.liferay.portal.kernel.portlet.PortletBag; import com.liferay.portal.kernel.portlet.PortletBagPool; import com.liferay.portal.kernel.portlet.PortletInstanceFactoryUtil; import com.liferay.portal.kernel.security.permission.ResourceActionsUtil; import com.liferay.portal.kernel.service.PortletLocalServiceUtil; import com.liferay.portal.kernel.servlet.DirectServletRegistryUtil; import com.liferay.portal.kernel.servlet.FileTimestampUtil; import com.liferay.portal.kernel.servlet.PortletServlet; import com.liferay.portal.kernel.servlet.ServletContextPool; import com.liferay.portal.kernel.servlet.ServletContextProvider; import com.liferay.portal.kernel.util.ClassUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.HttpUtil; import com.liferay.portal.kernel.util.Portal; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.ResourceBundleLoader; import com.liferay.portal.kernel.util.ResourceBundleUtil; 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.util.PortalInstances; import com.liferay.portal.util.WebAppPool; import com.liferay.portlet.PortletContextBag; import com.liferay.portlet.PortletContextBagPool; import com.liferay.portlet.PortletFilterFactory; import com.liferay.portlet.PortletURLListenerFactory; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.ServiceRegistration; import com.liferay.util.JS; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.portlet.PortletURLGenerationListener; import javax.servlet.ServletContext; import org.apache.portals.bridges.struts.StrutsPortlet; /** * @author Brian Wing Shun Chan * @author Brian Myunghun Kim * @author Ivica Cardic * @author Raymond Augé */ public class PortletHotDeployListener extends BaseHotDeployListener { @Override public void invokeDeploy(HotDeployEvent hotDeployEvent) throws HotDeployException { try { doInvokeDeploy(hotDeployEvent); } catch (Throwable t) { throwHotDeployException( hotDeployEvent, "Error registering portlets for ", t); } } @Override public void invokeUndeploy(HotDeployEvent hotDeployEvent) throws HotDeployException { try { doInvokeUndeploy(hotDeployEvent); } catch (Throwable t) { throwHotDeployException( hotDeployEvent, "Error unregistering portlets for ", t); } } protected void checkResourceBundles( ClassLoader classLoader, Portlet portlet) { if (Validator.isNull(portlet.getResourceBundle())) { return; } Registry registry = RegistryUtil.getRegistry(); ResourceBundleLoader resourceBundleLoader = ResourceBundleUtil.getResourceBundleLoader( portlet.getResourceBundle(), classLoader); Map<String, Object> properties = new HashMap<>(); properties.put( "resource.bundle.base.name", portlet.getResourceBundle()); properties.put("service.ranking", Integer.MIN_VALUE); properties.put("servlet.context.name", portlet.getContextName()); _resourceBundleLoaderServiceRegistrations.put( portlet.getPortletId(), registry.registerService( ResourceBundleLoader.class, resourceBundleLoader, properties)); } /** * @deprecated As of 7.0.0, with no direct replacement */ @Deprecated protected void destroyPortlet(Portlet portlet, Set<String> portletIds) throws Exception { _destroyPortlet(null, portlet, portletIds); } protected void doInvokeDeploy(HotDeployEvent hotDeployEvent) throws Exception { ServletContext servletContext = hotDeployEvent.getServletContext(); String servletContextName = servletContext.getServletContextName(); if (_log.isDebugEnabled()) { _log.debug("Invoking deploy for " + servletContextName); } String[] xmls = new String[] { HttpUtil.URLtoString( servletContext.getResource( "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD)), HttpUtil.URLtoString( servletContext.getResource( "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM)), HttpUtil.URLtoString( servletContext.getResource("/WEB-INF/liferay-portlet.xml")), HttpUtil.URLtoString(servletContext.getResource("/WEB-INF/web.xml")) }; if ((xmls[0] == null) && (xmls[1] == null)) { return; } if (_log.isInfoEnabled()) { _log.info("Registering portlets for " + servletContextName); } PortletContextBag portletContextBag = new PortletContextBag( servletContextName); PortletContextBagPool.put(servletContextName, portletContextBag); List<Portlet> portlets = PortletLocalServiceUtil.initWAR( servletContextName, servletContext, xmls, hotDeployEvent.getPluginPackage()); boolean portletAppInitialized = false; boolean strutsBridges = false; ClassLoader classLoader = hotDeployEvent.getContextClassLoader(); Iterator<Portlet> itr = portlets.iterator(); while (itr.hasNext()) { Portlet portlet = itr.next(); PortletBag portletBag = PortletBagPool.get( portlet.getRootPortletId()); if (!portletAppInitialized) { initPortletApp( servletContextName, servletContext, classLoader, portlet); portletAppInitialized = true; } javax.portlet.Portlet portletInstance = portletBag.getPortletInstance(); if (ClassUtil.isSubclass( portletInstance.getClass(), StrutsPortlet.class.getName())) { strutsBridges = true; } } if (!strutsBridges) { strutsBridges = GetterUtil.getBoolean( servletContext.getInitParameter( "struts-bridges-context-provider")); } if (strutsBridges) { servletContext.setAttribute( ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER, new LiferayServletContextProvider()); } String xml = HttpUtil.URLtoString( servletContext.getResource("/WEB-INF/liferay-display.xml")); PortletCategory newPortletCategory = PortletLocalServiceUtil.getWARDisplay(servletContextName, xml); long[] companyIds = PortalInstances.getCompanyIds(); for (long companyId : companyIds) { PortletCategory portletCategory = (PortletCategory)WebAppPool.get( companyId, WebKeys.PORTLET_CATEGORY); if (portletCategory != null) { portletCategory.merge(newPortletCategory); } else { _log.error( "Unable to register portlet for company " + companyId + " because it does not exist"); } } processPortletProperties(servletContextName, classLoader); for (Portlet portlet : portlets) { ResourceActionsUtil.check(portlet.getPortletId()); checkResourceBundles(classLoader, portlet); for (long companyId : companyIds) { Portlet curPortlet = PortletLocalServiceUtil.getPortletById( companyId, portlet.getPortletId()); PortletLocalServiceUtil.checkPortlet(curPortlet); } } for (Portlet portlet : portlets) { boolean ready = GetterUtil.getBoolean( servletContext.getInitParameter("portlets-ready-by-default"), true); portlet.setReady(ready); } JavadocManagerUtil.load(servletContextName, classLoader); DirectServletRegistryUtil.clearServlets(); FileTimestampUtil.reset(); _portlets.put(servletContextName, portlets); servletContext.setAttribute(WebKeys.PLUGIN_PORTLETS, portlets); if (_log.isInfoEnabled()) { if (portlets.size() == 1) { _log.info( "1 portlet for " + servletContextName + " is available for use"); } else { _log.info( portlets.size() + " portlets for " + servletContextName + " are available for use"); } } } protected void doInvokeUndeploy(HotDeployEvent hotDeployEvent) throws Exception { ServletContext servletContext = hotDeployEvent.getServletContext(); String servletContextName = servletContext.getServletContextName(); if (_log.isDebugEnabled()) { _log.debug("Invoking undeploy for " + servletContextName); } List<Portlet> portlets = _portlets.remove(servletContextName); if (portlets == null) { return; } Set<String> portletIds = new HashSet<>(); if (portlets != null) { if (_log.isInfoEnabled()) { _log.info("Unregistering portlets for " + servletContextName); } for (Portlet portlet : portlets) { _destroyPortlet(servletContext, portlet, portletIds); } } ServletContextPool.remove(servletContextName); if (!portletIds.isEmpty()) { long[] companyIds = PortalInstances.getCompanyIds(); for (long companyId : companyIds) { PortletCategory portletCategory = (PortletCategory)WebAppPool.get( companyId, WebKeys.PORTLET_CATEGORY); portletCategory.separate(portletIds); } } PortletContextBagPool.remove(servletContextName); JavadocManagerUtil.unload(servletContextName); DirectServletRegistryUtil.clearServlets(); if (_log.isInfoEnabled()) { if (portlets.size() == 1) { _log.info( "1 portlet for " + servletContextName + " was unregistered"); } else { _log.info( portlets.size() + " portlets for " + servletContextName + " were unregistered"); } } } protected void initPortletApp( String servletContextName, ServletContext servletContext, ClassLoader classLoader, Portlet portlet) throws Exception { PortletContextBag portletContextBag = PortletContextBagPool.get( servletContextName); PortletApp portletApp = portlet.getPortletApp(); servletContext.setAttribute(PortletServlet.PORTLET_APP, portletApp); Map<String, String> customUserAttributes = portletApp.getCustomUserAttributes(); for (Map.Entry<String, String> entry : customUserAttributes.entrySet()) { String attrCustomClass = entry.getValue(); Class<?> clazz = classLoader.loadClass(attrCustomClass); CustomUserAttributes customUserAttributesInstance = (CustomUserAttributes)clazz.newInstance(); portletContextBag.getCustomUserAttributes().put( attrCustomClass, customUserAttributesInstance); } Set<PortletFilter> portletFilters = portletApp.getPortletFilters(); for (PortletFilter portletFilter : portletFilters) { javax.portlet.filter.PortletFilter portletFilterInstance = (javax.portlet.filter.PortletFilter)newInstance( classLoader, new Class<?>[] { javax.portlet.filter.ActionFilter.class, javax.portlet.filter.EventFilter.class, javax.portlet.filter.PortletFilter.class, javax.portlet.filter.RenderFilter.class, javax.portlet.filter.ResourceFilter.class }, portletFilter.getFilterClass()); portletContextBag.getPortletFilters().put( portletFilter.getFilterName(), portletFilterInstance); } InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create( portlet, servletContext); invokerPortlet.setPortletFilters(); Set<PortletURLListener> portletURLListeners = portletApp.getPortletURLListeners(); for (PortletURLListener portletURLListener : portletURLListeners) { PortletURLGenerationListener portletURLListenerInstance = (PortletURLGenerationListener)newInstance( classLoader, PortletURLGenerationListener.class, portletURLListener.getListenerClass()); portletContextBag.getPortletURLListeners().put( portletURLListener.getListenerClass(), portletURLListenerInstance); PortletURLListenerFactory.create(portletURLListener); } } protected void processPortletProperties( String servletContextName, ClassLoader classLoader) throws Exception { Configuration portletPropertiesConfiguration = null; try { portletPropertiesConfiguration = ConfigurationFactoryUtil.getConfiguration( classLoader, "portlet"); } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug("Unable to read portlet.properties"); } return; } Properties portletProperties = portletPropertiesConfiguration.getProperties(); if (portletProperties.isEmpty()) { return; } ResourceActionsUtil.read( servletContextName, classLoader, StringUtil.split( portletProperties.getProperty( PropsKeys.RESOURCE_ACTIONS_CONFIGS))); } protected void unbindDataSource(String servletContextName) { Boolean dataSourceBindState = _dataSourceBindStates.remove( servletContextName); if (dataSourceBindState == null) { return; } try { if (_log.isDebugEnabled()) { _log.debug("Dynamically unbinding the Liferay data source"); } Context context = new InitialContext(); try { context.lookup(_JNDI_JDBC_LIFERAY_POOL); context.unbind(_JNDI_JDBC_LIFERAY_POOL); } catch (NamingException ne) { } try { context.lookup(_JNDI_JDBC); context.destroySubcontext(_JNDI_JDBC); } catch (NamingException ne) { } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn( "Unable to dynamically unbind the Liferay data source: " + e.getMessage()); } } } private void _destroyPortlet( ServletContext servletContext, Portlet portlet, Set<String> portletIds) throws Exception { PortletApp portletApp = portlet.getPortletApp(); Set<PortletFilter> portletFilters = portletApp.getPortletFilters(); for (PortletFilter portletFilter : portletFilters) { PortletFilterFactory.destroy(portletFilter); } Set<PortletURLListener> portletURLListeners = portletApp.getPortletURLListeners(); for (PortletURLListener portletURLListener : portletURLListeners) { PortletURLListenerFactory.destroy(portletURLListener); } PortletInstanceFactoryUtil.destroy(portlet); portletIds.add(portlet.getPortletId()); ServiceRegistration<ResourceBundleLoader> resourceBundleLoaderServiceRegistration = _resourceBundleLoaderServiceRegistrations.remove( portlet.getPortletId()); if (resourceBundleLoaderServiceRegistration != null) { resourceBundleLoaderServiceRegistration.unregister(); } String portletName = portlet.getPortletName(); if (servletContext != null) { String servletContextName = servletContext.getServletContextName(); if (servletContextName != null) { portletName = portletName.concat( PortletConstants.WAR_SEPARATOR); portletName = portletName.concat(servletContextName); } } portletName = JS.getSafeName(portletName); ResourceActionsUtil.removePortletResource(portletName); } private static final String _JNDI_JDBC = "java_liferay:jdbc"; private static final String _JNDI_JDBC_LIFERAY_POOL = _JNDI_JDBC + "/LiferayPool"; private static final Log _log = LogFactoryUtil.getLog( PortletHotDeployListener.class); private static final Map<String, Boolean> _dataSourceBindStates = new HashMap<>(); private static final Map<String, List<Portlet>> _portlets = new HashMap<>(); private final Map<String, ServiceRegistration<ResourceBundleLoader>> _resourceBundleLoaderServiceRegistrations = new HashMap<>(); }