001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.deploy.hot;
016    
017    import com.liferay.portal.deploy.RequiredPluginsUtil;
018    import com.liferay.portal.kernel.deploy.hot.HotDeploy;
019    import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
020    import com.liferay.portal.kernel.deploy.hot.HotDeployException;
021    import com.liferay.portal.kernel.deploy.hot.HotDeployListener;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.portlet.PortletClassLoaderUtil;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.servlet.ServletContextPool;
027    import com.liferay.portal.kernel.template.TemplateManagerUtil;
028    import com.liferay.portal.kernel.url.ServletContextURLContainer;
029    import com.liferay.portal.kernel.url.URLContainer;
030    import com.liferay.portal.kernel.util.BasePortalLifecycle;
031    import com.liferay.portal.kernel.util.ClassLoaderUtil;
032    import com.liferay.portal.kernel.util.HttpUtil;
033    import com.liferay.portal.kernel.util.JavaConstants;
034    import com.liferay.portal.kernel.util.PortalLifecycle;
035    import com.liferay.portal.kernel.util.PortalLifecycleUtil;
036    import com.liferay.portal.kernel.util.PropertiesUtil;
037    import com.liferay.portal.kernel.util.StringBundler;
038    import com.liferay.portal.kernel.util.StringUtil;
039    import com.liferay.portal.kernel.util.Validator;
040    
041    import java.io.File;
042    
043    import java.util.ArrayList;
044    import java.util.Collections;
045    import java.util.HashSet;
046    import java.util.List;
047    import java.util.Properties;
048    import java.util.Set;
049    
050    import javax.servlet.ServletContext;
051    
052    /**
053     * @author Ivica Cardic
054     * @author Brian Wing Shun Chan
055     * @author Raymond Aug??
056     */
057    @DoPrivileged
058    public class HotDeployImpl implements HotDeploy {
059    
060            public HotDeployImpl() {
061                    if (_log.isDebugEnabled()) {
062                            _log.debug("Initializing hot deploy manager " + this.hashCode());
063                    }
064    
065                    _dependentHotDeployEvents = new ArrayList<>();
066                    _deployedServletContextNames = new HashSet<>();
067                    _hotDeployListeners = new ArrayList<>();
068            }
069    
070            @Override
071            public synchronized void fireDeployEvent(
072                    final HotDeployEvent hotDeployEvent) {
073    
074                    PortalLifecycleUtil.register(
075                            new HotDeployPortalLifecycle(hotDeployEvent),
076                            PortalLifecycle.METHOD_INIT);
077    
078                    if (_capturePrematureEvents) {
079    
080                            // Capture events that are fired before the portal initialized
081    
082                            PortalLifecycle portalLifecycle = new BasePortalLifecycle() {
083    
084                                    @Override
085                                    protected void doPortalDestroy() {
086                                    }
087    
088                                    @Override
089                                    protected void doPortalInit() {
090                                            fireDeployEvent(hotDeployEvent);
091                                    }
092    
093                            };
094    
095                            PortalLifecycleUtil.register(
096                                    portalLifecycle, PortalLifecycle.METHOD_INIT);
097                    }
098                    else {
099    
100                            // Fire event
101    
102                            doFireDeployEvent(hotDeployEvent);
103                    }
104            }
105    
106            @Override
107            public synchronized void fireUndeployEvent(HotDeployEvent hotDeployEvent) {
108                    for (int i = _hotDeployListeners.size() - 1; i >= 0; i--) {
109                            HotDeployListener hotDeployListener = _hotDeployListeners.get(i);
110    
111                            PortletClassLoaderUtil.setServletContextName(
112                                    hotDeployEvent.getServletContextName());
113    
114                            try {
115                                    hotDeployListener.invokeUndeploy(hotDeployEvent);
116                            }
117                            catch (HotDeployException hde) {
118                                    _log.error(hde, hde);
119                            }
120                            finally {
121                                    PortletClassLoaderUtil.setServletContextName(null);
122                            }
123                    }
124    
125                    _deployedServletContextNames.remove(
126                            hotDeployEvent.getServletContextName());
127    
128                    ClassLoader classLoader = hotDeployEvent.getContextClassLoader();
129    
130                    TemplateManagerUtil.destroy(classLoader);
131    
132                    _pacl.unregister(classLoader);
133    
134                    RequiredPluginsUtil.startCheckingRequiredPlugins();
135            }
136    
137            @Override
138            public synchronized boolean registerDependentPortalLifecycle(
139                    String servletContextName, PortalLifecycle portalLifecycle) {
140    
141                    for (HotDeployEvent hotDeployEvent : _dependentHotDeployEvents) {
142                            if (Validator.equals(
143                                            servletContextName,
144                                            hotDeployEvent.getServletContextName())) {
145    
146                                    hotDeployEvent.addPortalLifecycle(portalLifecycle);
147    
148                                    return true;
149                            }
150                    }
151    
152                    return false;
153            }
154    
155            @Override
156            public synchronized void registerListener(
157                    HotDeployListener hotDeployListener) {
158    
159                    _hotDeployListeners.add(hotDeployListener);
160            }
161    
162            @Override
163            public synchronized void reset() {
164                    _capturePrematureEvents = true;
165                    _dependentHotDeployEvents.clear();
166                    _deployedServletContextNames.clear();
167                    _hotDeployListeners.clear();
168            }
169    
170            @Override
171            public synchronized void setCapturePrematureEvents(
172                    boolean capturePrematureEvents) {
173    
174                    _capturePrematureEvents = capturePrematureEvents;
175            }
176    
177            @Override
178            public synchronized void unregisterListener(
179                    HotDeployListener hotDeployListener) {
180    
181                    _hotDeployListeners.remove(hotDeployListener);
182            }
183    
184            @Override
185            public synchronized void unregisterListeners() {
186                    _hotDeployListeners.clear();
187            }
188    
189            public interface PACL {
190    
191                    public void initPolicy(
192                            String contextName, URLContainer urlContainer,
193                            ClassLoader classLoader, Properties properties);
194    
195                    public void unregister(ClassLoader classLoader);
196    
197            }
198    
199            protected void doFireDeployEvent(HotDeployEvent hotDeployEvent) {
200                    String servletContextName = hotDeployEvent.getServletContextName();
201    
202                    if (_deployedServletContextNames.contains(servletContextName)) {
203                            return;
204                    }
205    
206                    boolean hasDependencies = true;
207    
208                    for (String dependentServletContextName :
209                                    hotDeployEvent.getDependentServletContextNames()) {
210    
211                            if (!_deployedServletContextNames.contains(
212                                            dependentServletContextName)) {
213    
214                                    hasDependencies = false;
215    
216                                    break;
217                            }
218                    }
219    
220                    if (hasDependencies) {
221                            if (_log.isInfoEnabled()) {
222                                    _log.info("Deploying " + servletContextName + " from queue");
223                            }
224    
225                            for (int i = 0; i < _hotDeployListeners.size(); i++) {
226                                    HotDeployListener hotDeployListener = _hotDeployListeners.get(
227                                            i);
228    
229                                    PortletClassLoaderUtil.setServletContextName(
230                                            hotDeployEvent.getServletContextName());
231    
232                                    try {
233                                            hotDeployListener.invokeDeploy(hotDeployEvent);
234                                    }
235                                    catch (HotDeployException hde) {
236                                            _log.error(hde, hde);
237                                    }
238                                    finally {
239                                            PortletClassLoaderUtil.setServletContextName(null);
240                                    }
241                            }
242    
243                            _deployedServletContextNames.add(servletContextName);
244    
245                            _dependentHotDeployEvents.remove(hotDeployEvent);
246    
247                            ClassLoader contextClassLoader = getContextClassLoader();
248    
249                            try {
250                                    setContextClassLoader(ClassLoaderUtil.getPortalClassLoader());
251    
252                                    List<HotDeployEvent> dependentEvents = new ArrayList<>(
253                                            _dependentHotDeployEvents);
254    
255                                    for (HotDeployEvent dependentEvent : dependentEvents) {
256                                            setContextClassLoader(
257                                                    dependentEvent.getContextClassLoader());
258    
259                                            doFireDeployEvent(dependentEvent);
260    
261                                            dependentEvent.flushInits();
262                                    }
263                            }
264                            finally {
265                                    setContextClassLoader(contextClassLoader);
266                            }
267                    }
268                    else {
269                            if (!_dependentHotDeployEvents.contains(hotDeployEvent)) {
270                                    if (_log.isInfoEnabled()) {
271                                            StringBundler sb = new StringBundler(4);
272    
273                                            sb.append("Queueing ");
274                                            sb.append(servletContextName);
275                                            sb.append(" for deploy because it is missing ");
276                                            sb.append(getRequiredServletContextNames(hotDeployEvent));
277    
278                                            _log.info(sb.toString());
279                                    }
280    
281                                    _dependentHotDeployEvents.add(hotDeployEvent);
282                            }
283                            else {
284                                    if (_log.isInfoEnabled()) {
285                                            for (HotDeployEvent dependentHotDeployEvent :
286                                                            _dependentHotDeployEvents) {
287    
288                                                    StringBundler sb = new StringBundler(3);
289    
290                                                    sb.append(servletContextName);
291                                                    sb.append(" is still in queue because it is missing ");
292                                                    sb.append(
293                                                            getRequiredServletContextNames(
294                                                                    dependentHotDeployEvent));
295    
296                                                    _log.info(sb.toString());
297                                            }
298                                    }
299                            }
300                    }
301            }
302    
303            protected ClassLoader getContextClassLoader() {
304                    return ClassLoaderUtil.getContextClassLoader();
305            }
306    
307            protected String getRequiredServletContextNames(
308                    HotDeployEvent hotDeployEvent) {
309    
310                    List<String> requiredServletContextNames = new ArrayList<>();
311    
312                    for (String dependentServletContextName :
313                                    hotDeployEvent.getDependentServletContextNames()) {
314    
315                            if (!_deployedServletContextNames.contains(
316                                            dependentServletContextName)) {
317    
318                                    requiredServletContextNames.add(dependentServletContextName);
319                            }
320                    }
321    
322                    Collections.sort(requiredServletContextNames);
323    
324                    return StringUtil.merge(requiredServletContextNames, ", ");
325            }
326    
327            protected void setContextClassLoader(ClassLoader contextClassLoader) {
328                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
329            }
330    
331            private static final Log _log = LogFactoryUtil.getLog(HotDeployImpl.class);
332    
333            private static final PACL _pacl = new NoPACL();
334    
335            private boolean _capturePrematureEvents = true;
336            private final List<HotDeployEvent> _dependentHotDeployEvents;
337            private final Set<String> _deployedServletContextNames;
338            private final List<HotDeployListener> _hotDeployListeners;
339    
340            private static class NoPACL implements PACL {
341    
342                    @Override
343                    public void initPolicy(
344                            String contextName, URLContainer urlContainer,
345                            ClassLoader classLoader, Properties properties) {
346                    }
347    
348                    @Override
349                    public void unregister(ClassLoader classLoader) {
350                    }
351    
352            }
353    
354            private class HotDeployPortalLifecycle extends BasePortalLifecycle {
355    
356                    public HotDeployPortalLifecycle(HotDeployEvent hotDeployEvent) {
357                            _servletContext = hotDeployEvent.getServletContext();
358                            _classLoader = hotDeployEvent.getContextClassLoader();
359    
360                            ServletContextPool.put(
361                                    _servletContext.getServletContextName(), _servletContext);
362                    }
363    
364                    @Override
365                    protected void doPortalDestroy() {
366                    }
367    
368                    @Override
369                    protected void doPortalInit() throws Exception {
370                            Properties properties = null;
371    
372                            String propertiesString = HttpUtil.URLtoString(
373                                    _servletContext.getResource(
374                                            "/WEB-INF/liferay-plugin-package.properties"));
375    
376                            if (propertiesString != null) {
377                                    properties = PropertiesUtil.load(propertiesString);
378                            }
379                            else {
380                                    properties = new Properties();
381                            }
382    
383                            File tempDir = (File)_servletContext.getAttribute(
384                                    JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
385    
386                            properties.put(
387                                    JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR,
388                                    tempDir.getAbsolutePath());
389    
390                            _pacl.initPolicy(
391                                    _servletContext.getServletContextName(),
392                                    new ServletContextURLContainer(_servletContext), _classLoader,
393                                    properties);
394                    }
395    
396                    private final ClassLoader _classLoader;
397                    private final ServletContext _servletContext;
398    
399            }
400    
401    }