/** * 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.deploy.RequiredPluginsUtil; import com.liferay.portal.kernel.deploy.hot.HotDeploy; import com.liferay.portal.kernel.deploy.hot.HotDeployEvent; import com.liferay.portal.kernel.deploy.hot.HotDeployException; import com.liferay.portal.kernel.deploy.hot.HotDeployListener; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.portlet.PortletClassLoaderUtil; import com.liferay.portal.kernel.security.pacl.DoPrivileged; import com.liferay.portal.kernel.servlet.ServletContextPool; import com.liferay.portal.kernel.template.TemplateManagerUtil; import com.liferay.portal.kernel.url.ServletContextURLContainer; import com.liferay.portal.kernel.url.URLContainer; import com.liferay.portal.kernel.util.BasePortalLifecycle; import com.liferay.portal.kernel.util.ClassLoaderUtil; import com.liferay.portal.kernel.util.HttpUtil; import com.liferay.portal.kernel.util.JavaConstants; import com.liferay.portal.kernel.util.PortalLifecycle; import com.liferay.portal.kernel.util.PortalLifecycleUtil; import com.liferay.portal.kernel.util.PropertiesUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringUtil; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Properties; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import javax.servlet.ServletContext; /** * @author Ivica Cardic * @author Brian Wing Shun Chan * @author Raymond Augé */ @DoPrivileged public class HotDeployImpl implements HotDeploy { public HotDeployImpl() { if (_log.isDebugEnabled()) { _log.debug("Initializing hot deploy manager " + hashCode()); } _dependentHotDeployEvents = new ConcurrentLinkedQueue<>(); _deployedServletContextNames = new HashSet<>(); _hotDeployListeners = new ArrayList<>(); } @Override public synchronized void fireDeployEvent( final HotDeployEvent hotDeployEvent) { PortalLifecycleUtil.register( new HotDeployPortalLifecycle(hotDeployEvent), PortalLifecycle.METHOD_INIT); if (_capturePrematureEvents) { // Capture events that are fired before the portal initialized PortalLifecycle portalLifecycle = new BasePortalLifecycle() { @Override protected void doPortalDestroy() { } @Override protected void doPortalInit() { fireDeployEvent(hotDeployEvent); } }; PortalLifecycleUtil.register( portalLifecycle, PortalLifecycle.METHOD_INIT); } else { // Fire event doFireDeployEvent(hotDeployEvent); } } @Override public synchronized void fireUndeployEvent(HotDeployEvent hotDeployEvent) { for (int i = _hotDeployListeners.size() - 1; i >= 0; i--) { HotDeployListener hotDeployListener = _hotDeployListeners.get(i); PortletClassLoaderUtil.setServletContextName( hotDeployEvent.getServletContextName()); try { hotDeployListener.invokeUndeploy(hotDeployEvent); } catch (HotDeployException hde) { _log.error(hde, hde); } finally { PortletClassLoaderUtil.setServletContextName(null); } } _deployedServletContextNames.remove( hotDeployEvent.getServletContextName()); ClassLoader classLoader = hotDeployEvent.getContextClassLoader(); TemplateManagerUtil.destroy(classLoader); _pacl.unregister(classLoader); RequiredPluginsUtil.startCheckingRequiredPlugins(); } @Override public boolean registerDependentPortalLifecycle( String servletContextName, PortalLifecycle portalLifecycle) { for (HotDeployEvent hotDeployEvent : _dependentHotDeployEvents) { if (Objects.equals( servletContextName, hotDeployEvent.getServletContextName())) { synchronized (this) { hotDeployEvent.addPortalLifecycle(portalLifecycle); } return true; } } return false; } @Override public synchronized void registerListener( HotDeployListener hotDeployListener) { _hotDeployListeners.add(hotDeployListener); } @Override public synchronized void reset() { _capturePrematureEvents = true; _dependentHotDeployEvents.clear(); _deployedServletContextNames.clear(); _hotDeployListeners.clear(); } @Override public synchronized void setCapturePrematureEvents( boolean capturePrematureEvents) { _capturePrematureEvents = capturePrematureEvents; } @Override public synchronized void unregisterListener( HotDeployListener hotDeployListener) { _hotDeployListeners.remove(hotDeployListener); } @Override public synchronized void unregisterListeners() { _hotDeployListeners.clear(); } public interface PACL { public void initPolicy( String contextName, URLContainer urlContainer, ClassLoader classLoader, Properties properties); public void unregister(ClassLoader classLoader); } protected void doFireDeployEvent(HotDeployEvent hotDeployEvent) { String servletContextName = hotDeployEvent.getServletContextName(); if (_deployedServletContextNames.contains(servletContextName)) { return; } boolean hasDependencies = true; for (String dependentServletContextName : hotDeployEvent.getDependentServletContextNames()) { if (!_deployedServletContextNames.contains( dependentServletContextName)) { hasDependencies = false; break; } } if (hasDependencies) { if (_log.isInfoEnabled()) { _log.info("Deploying " + servletContextName + " from queue"); } for (int i = 0; i < _hotDeployListeners.size(); i++) { HotDeployListener hotDeployListener = _hotDeployListeners.get( i); PortletClassLoaderUtil.setServletContextName( hotDeployEvent.getServletContextName()); try { hotDeployListener.invokeDeploy(hotDeployEvent); } catch (HotDeployException hde) { _log.error(hde, hde); } finally { PortletClassLoaderUtil.setServletContextName(null); } } _deployedServletContextNames.add(servletContextName); _dependentHotDeployEvents.remove(hotDeployEvent); ClassLoader contextClassLoader = getContextClassLoader(); try { setContextClassLoader(ClassLoaderUtil.getPortalClassLoader()); List<HotDeployEvent> dependentEvents = new ArrayList<>( _dependentHotDeployEvents); for (HotDeployEvent dependentEvent : dependentEvents) { setContextClassLoader( dependentEvent.getContextClassLoader()); doFireDeployEvent(dependentEvent); if (!_dependentHotDeployEvents.contains(dependentEvent)) { dependentEvent.flushInits(); } } } finally { setContextClassLoader(contextClassLoader); } } else { if (!_dependentHotDeployEvents.contains(hotDeployEvent)) { if (_log.isInfoEnabled()) { StringBundler sb = new StringBundler(4); sb.append("Queueing "); sb.append(servletContextName); sb.append(" for deploy because it is missing "); sb.append(getRequiredServletContextNames(hotDeployEvent)); _log.info(sb.toString()); } _dependentHotDeployEvents.add(hotDeployEvent); } else { if (_log.isInfoEnabled()) { for (HotDeployEvent dependentHotDeployEvent : _dependentHotDeployEvents) { StringBundler sb = new StringBundler(3); sb.append(servletContextName); sb.append(" is still in queue because it is missing "); sb.append( getRequiredServletContextNames( dependentHotDeployEvent)); _log.info(sb.toString()); } } } } } protected ClassLoader getContextClassLoader() { return ClassLoaderUtil.getContextClassLoader(); } protected String getRequiredServletContextNames( HotDeployEvent hotDeployEvent) { List<String> requiredServletContextNames = new ArrayList<>(); for (String dependentServletContextName : hotDeployEvent.getDependentServletContextNames()) { if (!_deployedServletContextNames.contains( dependentServletContextName)) { requiredServletContextNames.add(dependentServletContextName); } } Collections.sort(requiredServletContextNames); return StringUtil.merge(requiredServletContextNames, ", "); } protected void setContextClassLoader(ClassLoader contextClassLoader) { ClassLoaderUtil.setContextClassLoader(contextClassLoader); } private static final Log _log = LogFactoryUtil.getLog(HotDeployImpl.class); private static final PACL _pacl = new NoPACL(); private boolean _capturePrematureEvents = true; private final Queue<HotDeployEvent> _dependentHotDeployEvents; private final Set<String> _deployedServletContextNames; private final List<HotDeployListener> _hotDeployListeners; private static class HotDeployPortalLifecycle extends BasePortalLifecycle { public HotDeployPortalLifecycle(HotDeployEvent hotDeployEvent) { _servletContext = hotDeployEvent.getServletContext(); _classLoader = hotDeployEvent.getContextClassLoader(); ServletContextPool.put( _servletContext.getServletContextName(), _servletContext); } @Override protected void doPortalDestroy() { } @Override protected void doPortalInit() throws Exception { Properties properties = null; String propertiesString = HttpUtil.URLtoString( _servletContext.getResource( "/WEB-INF/liferay-plugin-package.properties")); if (propertiesString != null) { properties = PropertiesUtil.load(propertiesString); } else { properties = new Properties(); } File tempDir = (File)_servletContext.getAttribute( JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR); properties.put( JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR, tempDir.getAbsolutePath()); _pacl.initPolicy( _servletContext.getServletContextName(), new ServletContextURLContainer(_servletContext), _classLoader, properties); } private final ClassLoader _classLoader; private final ServletContext _servletContext; } private static class NoPACL implements PACL { @Override public void initPolicy( String contextName, URLContainer urlContainer, ClassLoader classLoader, Properties properties) { } @Override public void unregister(ClassLoader classLoader) { } } }