001    /**
002     * Copyright (c) 2000-2013 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.kernel.deploy.hot.HotDeploy;
018    import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
019    import com.liferay.portal.kernel.deploy.hot.HotDeployException;
020    import com.liferay.portal.kernel.deploy.hot.HotDeployListener;
021    import com.liferay.portal.kernel.freemarker.FreeMarkerEngineUtil;
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.util.BasePortalLifecycle;
028    import com.liferay.portal.kernel.util.HttpUtil;
029    import com.liferay.portal.kernel.util.PortalLifecycle;
030    import com.liferay.portal.kernel.util.PortalLifecycleUtil;
031    import com.liferay.portal.kernel.util.PropertiesUtil;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.StringUtil;
034    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
035    import com.liferay.portal.util.ClassLoaderUtil;
036    
037    import java.util.ArrayList;
038    import java.util.Collections;
039    import java.util.HashSet;
040    import java.util.List;
041    import java.util.Properties;
042    import java.util.Set;
043    import java.util.concurrent.CopyOnWriteArrayList;
044    
045    import javax.servlet.ServletContext;
046    
047    /**
048     * @author Ivica Cardic
049     * @author Brian Wing Shun Chan
050     * @author Raymond Aug??
051     */
052    @DoPrivileged
053    public class HotDeployImpl implements HotDeploy {
054    
055            public HotDeployImpl() {
056                    if (_log.isDebugEnabled()) {
057                            _log.debug("Initializing hot deploy manager " + this.hashCode());
058                    }
059    
060                    _dependentHotDeployEvents = new ArrayList<HotDeployEvent>();
061                    _deployedServletContextNames = new HashSet<String>();
062                    _hotDeployListeners = new CopyOnWriteArrayList<HotDeployListener>();
063            }
064    
065            @Override
066            public synchronized void fireDeployEvent(
067                    final HotDeployEvent hotDeployEvent) {
068    
069                    PortalLifecycleUtil.register(
070                            new HotDeployPortalLifecycle(hotDeployEvent),
071                            PortalLifecycle.METHOD_INIT);
072    
073                    if (_capturePrematureEvents) {
074    
075                            // Capture events that are fired before the portal initialized
076    
077                            PortalLifecycle portalLifecycle = new BasePortalLifecycle() {
078    
079                                    @Override
080                                    protected void doPortalDestroy() {
081                                    }
082    
083                                    @Override
084                                    protected void doPortalInit() {
085                                            fireDeployEvent(hotDeployEvent);
086                                    }
087    
088                            };
089    
090                            PortalLifecycleUtil.register(
091                                    portalLifecycle, PortalLifecycle.METHOD_INIT);
092                    }
093                    else {
094    
095                            // Fire event
096    
097                            doFireDeployEvent(hotDeployEvent);
098                    }
099            }
100    
101            @Override
102            public synchronized void fireUndeployEvent(HotDeployEvent hotDeployEvent) {
103                    for (HotDeployListener hotDeployListener : _hotDeployListeners) {
104                            try {
105                                    PortletClassLoaderUtil.setClassLoader(
106                                            hotDeployEvent.getContextClassLoader());
107                                    PortletClassLoaderUtil.setServletContextName(
108                                            hotDeployEvent.getServletContextName());
109    
110                                    hotDeployListener.invokeUndeploy(hotDeployEvent);
111                            }
112                            catch (HotDeployException hde) {
113                                    _log.error(hde, hde);
114                            }
115                            finally {
116                                    PortletClassLoaderUtil.setClassLoader(null);
117                                    PortletClassLoaderUtil.setServletContextName(null);
118                            }
119                    }
120    
121                    _deployedServletContextNames.remove(
122                            hotDeployEvent.getServletContextName());
123    
124                    ClassLoader classLoader = hotDeployEvent.getContextClassLoader();
125    
126                    FreeMarkerEngineUtil.clearClassLoader(classLoader);
127                    VelocityEngineUtil.clearClassLoader(classLoader);
128    
129                    _pacl.unregister(classLoader);
130            }
131    
132            @Override
133            public void registerListener(HotDeployListener hotDeployListener) {
134                    _hotDeployListeners.add(hotDeployListener);
135            }
136    
137            @Override
138            public synchronized void reset() {
139                    _capturePrematureEvents = true;
140                    _dependentHotDeployEvents.clear();
141                    _deployedServletContextNames.clear();
142                    _hotDeployListeners.clear();
143            }
144    
145            @Override
146            public synchronized void setCapturePrematureEvents(
147                    boolean capturePrematureEvents) {
148    
149                    _capturePrematureEvents = capturePrematureEvents;
150            }
151    
152            @Override
153            public void unregisterListener(HotDeployListener hotDeployListener) {
154                    _hotDeployListeners.remove(hotDeployListener);
155            }
156    
157            @Override
158            public void unregisterListeners() {
159                    _hotDeployListeners.clear();
160            }
161    
162            protected void doFireDeployEvent(HotDeployEvent hotDeployEvent) {
163                    String servletContextName = hotDeployEvent.getServletContextName();
164    
165                    if (_deployedServletContextNames.contains(servletContextName)) {
166                            return;
167                    }
168    
169                    boolean hasDependencies = true;
170    
171                    for (String dependentServletContextName :
172                                    hotDeployEvent.getDependentServletContextNames()) {
173    
174                            if (!_deployedServletContextNames.contains(
175                                            dependentServletContextName)) {
176    
177                                    hasDependencies = false;
178    
179                                    break;
180                            }
181                    }
182    
183                    if (hasDependencies) {
184                            if (_log.isInfoEnabled()) {
185                                    _log.info("Deploying " + servletContextName + " from queue");
186                            }
187    
188                            for (HotDeployListener hotDeployListener : _hotDeployListeners) {
189                                    try {
190                                            PortletClassLoaderUtil.setClassLoader(
191                                                    hotDeployEvent.getContextClassLoader());
192                                            PortletClassLoaderUtil.setServletContextName(
193                                                    hotDeployEvent.getServletContextName());
194    
195                                            hotDeployListener.invokeDeploy(hotDeployEvent);
196                                    }
197                                    catch (HotDeployException hde) {
198                                            _log.error(hde, hde);
199                                    }
200                                    finally {
201                                            PortletClassLoaderUtil.setClassLoader(null);
202                                            PortletClassLoaderUtil.setServletContextName(null);
203                                    }
204                            }
205    
206                            _deployedServletContextNames.add(servletContextName);
207    
208                            _dependentHotDeployEvents.remove(hotDeployEvent);
209    
210                            ClassLoader contextClassLoader = getContextClassLoader();
211    
212                            try {
213                                    setContextClassLoader(ClassLoaderUtil.getPortalClassLoader());
214    
215                                    List<HotDeployEvent> dependentEvents =
216                                            new ArrayList<HotDeployEvent>(_dependentHotDeployEvents);
217    
218                                    for (HotDeployEvent dependentEvent : dependentEvents) {
219                                            setContextClassLoader(
220                                                    dependentEvent.getContextClassLoader());
221    
222                                            doFireDeployEvent(dependentEvent);
223                                    }
224                            }
225                            finally {
226                                    setContextClassLoader(contextClassLoader);
227                            }
228                    }
229                    else {
230                            if (!_dependentHotDeployEvents.contains(hotDeployEvent)) {
231                                    if (_log.isInfoEnabled()) {
232                                            StringBundler sb = new StringBundler(4);
233    
234                                            sb.append("Queueing ");
235                                            sb.append(servletContextName);
236                                            sb.append(" for deploy because it is missing ");
237                                            sb.append(getRequiredServletContextNames(hotDeployEvent));
238    
239                                            _log.info(sb.toString());
240                                    }
241    
242                                    _dependentHotDeployEvents.add(hotDeployEvent);
243                            }
244                            else {
245                                    if (_log.isInfoEnabled()) {
246                                            for (HotDeployEvent dependentHotDeployEvent :
247                                                            _dependentHotDeployEvents) {
248    
249                                                    StringBundler sb = new StringBundler(3);
250    
251                                                    sb.append(servletContextName);
252                                                    sb.append(" is still in queue because it is missing ");
253                                                    sb.append(
254                                                            getRequiredServletContextNames(
255                                                                    dependentHotDeployEvent));
256    
257                                                    _log.info(sb.toString());
258                                            }
259                                    }
260                            }
261                    }
262            }
263    
264            protected ClassLoader getContextClassLoader() {
265                    return ClassLoaderUtil.getContextClassLoader();
266            }
267    
268            protected String getRequiredServletContextNames(
269                    HotDeployEvent hotDeployEvent) {
270    
271                    List<String> requiredServletContextNames = new ArrayList<String>();
272    
273                    for (String dependentServletContextName :
274                                    hotDeployEvent.getDependentServletContextNames()) {
275    
276                            if (!_deployedServletContextNames.contains(
277                                            dependentServletContextName)) {
278    
279                                    requiredServletContextNames.add(dependentServletContextName);
280                            }
281                    }
282    
283                    Collections.sort(requiredServletContextNames);
284    
285                    return StringUtil.merge(requiredServletContextNames, ", ");
286            }
287    
288            protected void setContextClassLoader(ClassLoader contextClassLoader) {
289                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
290            }
291    
292            private static Log _log = LogFactoryUtil.getLog(HotDeployImpl.class);
293    
294            private static PACL _pacl = new NoPACL();
295    
296            private boolean _capturePrematureEvents = true;
297            private List<HotDeployEvent> _dependentHotDeployEvents;
298            private Set<String> _deployedServletContextNames;
299            private List<HotDeployListener> _hotDeployListeners;
300    
301            private static class NoPACL implements PACL {
302    
303                    @Override
304                    public void initPolicy(
305                            String servletContextName, ClassLoader classLoader,
306                            Properties properties) {
307                    }
308    
309                    @Override
310                    public void unregister(ClassLoader classLoader) {
311                    }
312    
313            }
314    
315            public static interface PACL {
316    
317                    public void initPolicy(
318                            String servletContextName, ClassLoader classLoader,
319                            Properties properties);
320    
321                    public void unregister(ClassLoader classLoader);
322    
323            }
324    
325            private class HotDeployPortalLifecycle extends BasePortalLifecycle {
326    
327                    public HotDeployPortalLifecycle(HotDeployEvent hotDeployEvent) {
328                            _servletContext = hotDeployEvent.getServletContext();
329                            _classLoader = hotDeployEvent.getContextClassLoader();
330    
331                            ServletContextPool.put(
332                                    _servletContext.getServletContextName(), _servletContext);
333                    }
334    
335                    @Override
336                    protected void doPortalDestroy() {
337                    }
338    
339                    @Override
340                    protected void doPortalInit() throws Exception {
341                            Properties properties = null;
342    
343                            String propertiesString = HttpUtil.URLtoString(
344                                    _servletContext.getResource(
345                                            "/WEB-INF/liferay-plugin-package.properties"));
346    
347                            if (propertiesString != null) {
348                                    properties = PropertiesUtil.load(propertiesString);
349                            }
350                            else {
351                                    properties = new Properties();
352                            }
353    
354                            _pacl.initPolicy(
355                                    _servletContext.getServletContextName(), _classLoader,
356                                    properties);
357                    }
358    
359                    private ClassLoader _classLoader;
360                    private ServletContext _servletContext;
361    
362            }
363    
364    }