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.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.portlet.PortletClassLoaderUtil;
024    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
025    import com.liferay.portal.kernel.servlet.ServletContextPool;
026    import com.liferay.portal.kernel.template.TemplateManagerUtil;
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.util.ClassLoaderUtil;
035    
036    import java.util.ArrayList;
037    import java.util.Collections;
038    import java.util.HashSet;
039    import java.util.List;
040    import java.util.Properties;
041    import java.util.Set;
042    import java.util.concurrent.CopyOnWriteArrayList;
043    
044    import javax.servlet.ServletContext;
045    
046    /**
047     * @author Ivica Cardic
048     * @author Brian Wing Shun Chan
049     * @author Raymond Aug??
050     */
051    @DoPrivileged
052    public class HotDeployImpl implements HotDeploy {
053    
054            public HotDeployImpl() {
055                    if (_log.isDebugEnabled()) {
056                            _log.debug("Initializing hot deploy manager " + this.hashCode());
057                    }
058    
059                    _dependentHotDeployEvents = new ArrayList<HotDeployEvent>();
060                    _deployedServletContextNames = new HashSet<String>();
061                    _hotDeployListeners = new CopyOnWriteArrayList<HotDeployListener>();
062            }
063    
064            @Override
065            public synchronized void fireDeployEvent(
066                    final HotDeployEvent hotDeployEvent) {
067    
068                    PortalLifecycleUtil.register(
069                            new HotDeployPortalLifecycle(hotDeployEvent),
070                            PortalLifecycle.METHOD_INIT);
071    
072                    if (_capturePrematureEvents) {
073    
074                            // Capture events that are fired before the portal initialized
075    
076                            PortalLifecycle portalLifecycle = new BasePortalLifecycle() {
077    
078                                    @Override
079                                    protected void doPortalDestroy() {
080                                    }
081    
082                                    @Override
083                                    protected void doPortalInit() {
084                                            fireDeployEvent(hotDeployEvent);
085                                    }
086    
087                            };
088    
089                            PortalLifecycleUtil.register(
090                                    portalLifecycle, PortalLifecycle.METHOD_INIT);
091                    }
092                    else {
093    
094                            // Fire event
095    
096                            doFireDeployEvent(hotDeployEvent);
097                    }
098            }
099    
100            @Override
101            public synchronized void fireUndeployEvent(HotDeployEvent hotDeployEvent) {
102                    for (int i = _hotDeployListeners.size() - 1; i >= 0; i--) {
103                            HotDeployListener hotDeployListener = _hotDeployListeners.get(i);
104    
105                            try {
106                                    PortletClassLoaderUtil.setClassLoader(
107                                            hotDeployEvent.getContextClassLoader());
108                                    PortletClassLoaderUtil.setServletContextName(
109                                            hotDeployEvent.getServletContextName());
110    
111                                    hotDeployListener.invokeUndeploy(hotDeployEvent);
112                            }
113                            catch (HotDeployException hde) {
114                                    _log.error(hde, hde);
115                            }
116                            finally {
117                                    PortletClassLoaderUtil.setClassLoader(null);
118                                    PortletClassLoaderUtil.setServletContextName(null);
119                            }
120                    }
121    
122                    _deployedServletContextNames.remove(
123                            hotDeployEvent.getServletContextName());
124    
125                    ClassLoader classLoader = hotDeployEvent.getContextClassLoader();
126    
127                    TemplateManagerUtil.destroy(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            public static interface PACL {
163    
164                    public void initPolicy(
165                            String servletContextName, ClassLoader classLoader,
166                            Properties properties);
167    
168                    public void unregister(ClassLoader classLoader);
169    
170            }
171    
172            protected void doFireDeployEvent(HotDeployEvent hotDeployEvent) {
173                    String servletContextName = hotDeployEvent.getServletContextName();
174    
175                    if (_deployedServletContextNames.contains(servletContextName)) {
176                            return;
177                    }
178    
179                    boolean hasDependencies = true;
180    
181                    for (String dependentServletContextName :
182                                    hotDeployEvent.getDependentServletContextNames()) {
183    
184                            if (!_deployedServletContextNames.contains(
185                                            dependentServletContextName)) {
186    
187                                    hasDependencies = false;
188    
189                                    break;
190                            }
191                    }
192    
193                    if (hasDependencies) {
194                            if (_log.isInfoEnabled()) {
195                                    _log.info("Deploying " + servletContextName + " from queue");
196                            }
197    
198                            for (HotDeployListener hotDeployListener : _hotDeployListeners) {
199                                    try {
200                                            PortletClassLoaderUtil.setClassLoader(
201                                                    hotDeployEvent.getContextClassLoader());
202                                            PortletClassLoaderUtil.setServletContextName(
203                                                    hotDeployEvent.getServletContextName());
204    
205                                            hotDeployListener.invokeDeploy(hotDeployEvent);
206                                    }
207                                    catch (HotDeployException hde) {
208                                            _log.error(hde, hde);
209                                    }
210                                    finally {
211                                            PortletClassLoaderUtil.setClassLoader(null);
212                                            PortletClassLoaderUtil.setServletContextName(null);
213                                    }
214                            }
215    
216                            _deployedServletContextNames.add(servletContextName);
217    
218                            _dependentHotDeployEvents.remove(hotDeployEvent);
219    
220                            ClassLoader contextClassLoader = getContextClassLoader();
221    
222                            try {
223                                    setContextClassLoader(ClassLoaderUtil.getPortalClassLoader());
224    
225                                    List<HotDeployEvent> dependentEvents =
226                                            new ArrayList<HotDeployEvent>(_dependentHotDeployEvents);
227    
228                                    for (HotDeployEvent dependentEvent : dependentEvents) {
229                                            setContextClassLoader(
230                                                    dependentEvent.getContextClassLoader());
231    
232                                            doFireDeployEvent(dependentEvent);
233                                    }
234                            }
235                            finally {
236                                    setContextClassLoader(contextClassLoader);
237                            }
238                    }
239                    else {
240                            if (!_dependentHotDeployEvents.contains(hotDeployEvent)) {
241                                    if (_log.isInfoEnabled()) {
242                                            StringBundler sb = new StringBundler(4);
243    
244                                            sb.append("Queueing ");
245                                            sb.append(servletContextName);
246                                            sb.append(" for deploy because it is missing ");
247                                            sb.append(getRequiredServletContextNames(hotDeployEvent));
248    
249                                            _log.info(sb.toString());
250                                    }
251    
252                                    _dependentHotDeployEvents.add(hotDeployEvent);
253                            }
254                            else {
255                                    if (_log.isInfoEnabled()) {
256                                            for (HotDeployEvent dependentHotDeployEvent :
257                                                            _dependentHotDeployEvents) {
258    
259                                                    StringBundler sb = new StringBundler(3);
260    
261                                                    sb.append(servletContextName);
262                                                    sb.append(" is still in queue because it is missing ");
263                                                    sb.append(
264                                                            getRequiredServletContextNames(
265                                                                    dependentHotDeployEvent));
266    
267                                                    _log.info(sb.toString());
268                                            }
269                                    }
270                            }
271                    }
272            }
273    
274            protected ClassLoader getContextClassLoader() {
275                    return ClassLoaderUtil.getContextClassLoader();
276            }
277    
278            protected String getRequiredServletContextNames(
279                    HotDeployEvent hotDeployEvent) {
280    
281                    List<String> requiredServletContextNames = new ArrayList<String>();
282    
283                    for (String dependentServletContextName :
284                                    hotDeployEvent.getDependentServletContextNames()) {
285    
286                            if (!_deployedServletContextNames.contains(
287                                            dependentServletContextName)) {
288    
289                                    requiredServletContextNames.add(dependentServletContextName);
290                            }
291                    }
292    
293                    Collections.sort(requiredServletContextNames);
294    
295                    return StringUtil.merge(requiredServletContextNames, ", ");
296            }
297    
298            protected void setContextClassLoader(ClassLoader contextClassLoader) {
299                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
300            }
301    
302            private static Log _log = LogFactoryUtil.getLog(HotDeployImpl.class);
303    
304            private static PACL _pacl = new NoPACL();
305    
306            private boolean _capturePrematureEvents = true;
307            private List<HotDeployEvent> _dependentHotDeployEvents;
308            private Set<String> _deployedServletContextNames;
309            private List<HotDeployListener> _hotDeployListeners;
310    
311            private static class NoPACL implements PACL {
312    
313                    @Override
314                    public void initPolicy(
315                            String servletContextName, ClassLoader classLoader,
316                            Properties properties) {
317                    }
318    
319                    @Override
320                    public void unregister(ClassLoader classLoader) {
321                    }
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    }