1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.deploy.hot;
24  
25  import com.liferay.portal.events.EventsProcessor;
26  import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
27  import com.liferay.portal.kernel.configuration.Configuration;
28  import com.liferay.portal.kernel.configuration.ConfigurationFactoryUtil;
29  import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
30  import com.liferay.portal.kernel.deploy.hot.HotDeployException;
31  import com.liferay.portal.kernel.events.Action;
32  import com.liferay.portal.kernel.events.InvokerSimpleAction;
33  import com.liferay.portal.kernel.events.SimpleAction;
34  import com.liferay.portal.kernel.language.LanguageUtil;
35  import com.liferay.portal.kernel.util.GetterUtil;
36  import com.liferay.portal.kernel.util.HttpUtil;
37  import com.liferay.portal.kernel.util.StringPool;
38  import com.liferay.portal.kernel.util.StringUtil;
39  import com.liferay.portal.kernel.util.Validator;
40  import com.liferay.portal.model.ModelListener;
41  import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
42  import com.liferay.portal.servlet.filters.layoutcache.LayoutCacheUtil;
43  import com.liferay.portal.util.DocumentUtil;
44  import com.liferay.portal.util.PortalInstances;
45  import com.liferay.portal.util.PropsKeys;
46  import com.liferay.portal.util.PropsUtil;
47  import com.liferay.portal.util.PropsValues;
48  
49  import java.lang.reflect.Field;
50  
51  import java.util.ArrayList;
52  import java.util.HashMap;
53  import java.util.List;
54  import java.util.Map;
55  import java.util.Properties;
56  
57  import javax.servlet.ServletContext;
58  
59  import org.apache.commons.logging.Log;
60  import org.apache.commons.logging.LogFactory;
61  
62  import org.dom4j.Document;
63  import org.dom4j.Element;
64  
65  /**
66   * <a href="HookHotDeployListener.java.html"><b><i>View Source</i></b></a>
67   *
68   * @author Brian Wing Shun Chan
69   *
70   */
71  public class HookHotDeployListener extends BaseHotDeployListener {
72  
73      public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
74          try {
75              doInvokeDeploy(event);
76          }
77          catch (Exception e) {
78              throwHotDeployException(event, "Error registering hook for ", e);
79          }
80      }
81  
82      public void invokeUndeploy(HotDeployEvent event) throws HotDeployException {
83          try {
84              doInvokeUndeploy(event);
85          }
86          catch (Exception e) {
87              throwHotDeployException(event, "Error unregistering hook for ", e);
88          }
89      }
90  
91      protected boolean containsKey(Properties portalProperties, String key) {
92          if (_log.isDebugEnabled()) {
93              return true;
94          }
95          else {
96              return portalProperties.containsKey(key);
97          }
98      }
99  
100     protected void destroyPortalProperties(Properties portalProperties)
101         throws Exception {
102 
103         PropsUtil.removeProperties(portalProperties);
104 
105         if (_log.isDebugEnabled() &&
106             portalProperties.containsKey(PropsKeys.LOCALES)) {
107 
108             _log.debug(
109                 "Portlet locales " +
110                     portalProperties.getProperty(PropsKeys.LOCALES));
111             _log.debug(
112                 "Original locales " + PropsUtil.get(PropsKeys.LOCALES));
113             _log.debug(
114                 "Original locales array length " +
115                     PropsUtil.getArray(PropsKeys.LOCALES).length);
116         }
117 
118         resetPortalProperties(portalProperties);
119     }
120 
121     protected void doInvokeDeploy(HotDeployEvent event) throws Exception {
122         ServletContext servletContext = event.getServletContext();
123 
124         String servletContextName = servletContext.getServletContextName();
125 
126         if (_log.isDebugEnabled()) {
127             _log.debug("Invoking deploy for " + servletContextName);
128         }
129 
130         String xml = HttpUtil.URLtoString(
131             servletContext.getResource("/WEB-INF/liferay-hook.xml"));
132 
133         if (xml == null) {
134             return;
135         }
136 
137         if (_log.isInfoEnabled()) {
138             _log.info("Registering hook for " + servletContextName);
139         }
140 
141         ClassLoader portletClassLoader = event.getContextClassLoader();
142 
143         Document doc = DocumentUtil.readDocumentFromXML(xml, true);
144 
145         Element root = doc.getRootElement();
146 
147         List<Element> eventEls = root.elements("event");
148 
149         for (Element eventEl : eventEls) {
150             String eventClass = eventEl.elementText("event-class");
151             String eventType = eventEl.elementText("event-type");
152 
153             Object obj = initEvent(eventClass, eventType, portletClassLoader);
154 
155             if (obj != null) {
156                 List<Object> events = _eventsMap.get(eventType);
157 
158                 if (events == null) {
159                     events = new ArrayList<Object>();
160 
161                     _eventsMap.put(eventType, events);
162                 }
163 
164                 events.add(obj);
165             }
166         }
167 
168         List<Element> modelListenerEls = root.elements("model-listener");
169 
170         for (Element modelListenerEl : modelListenerEls) {
171             String modelListenerClass = modelListenerEl.elementText(
172                 "model-listener-class");
173             String modelName = modelListenerEl.elementText("model-name");
174 
175             ModelListener modelListener = initModelListener(
176                 modelListenerClass, modelName, portletClassLoader);
177 
178             if (modelListener != null) {
179                 List<ModelListener> modelListeners = _modelListenersMap.get(
180                     modelName);
181 
182                 if (modelListeners == null) {
183                     modelListeners = new ArrayList<ModelListener>();
184 
185                     _modelListenersMap.put(modelName, modelListeners);
186                 }
187 
188                 modelListeners.add(modelListener);
189             }
190         }
191 
192         String portalPropertiesLocation = root.elementText("portal-properties");
193 
194         if (Validator.isNotNull(portalPropertiesLocation)) {
195             Configuration portalPropertiesConfiguration = null;
196 
197             try {
198                 String name = portalPropertiesLocation;
199 
200                 int pos = name.lastIndexOf(".properties");
201 
202                 if (pos != -1) {
203                     name = name.substring(0, pos);
204                 }
205 
206                 portalPropertiesConfiguration =
207                     ConfigurationFactoryUtil.getConfiguration(
208                         portletClassLoader, name);
209             }
210             catch (Exception e) {
211                 _log.error("Unable to read " + portalPropertiesLocation, e);
212             }
213 
214             if (portalPropertiesConfiguration != null) {
215                 Properties portalProperties =
216                     portalPropertiesConfiguration.getProperties();
217 
218                 if (portalProperties.size() > 0) {
219                     _portalPropertiesMap.put(
220                         servletContextName, portalProperties);
221 
222                     initPortalProperties(portalProperties);
223                 }
224             }
225         }
226 
227         if (_log.isInfoEnabled()) {
228             _log.info(
229                 "Hook for " + servletContextName + " registered successfully");
230         }
231     }
232 
233     protected void doInvokeUndeploy(HotDeployEvent event) throws Exception {
234         ServletContext servletContext = event.getServletContext();
235 
236         String servletContextName = servletContext.getServletContextName();
237 
238         if (_log.isDebugEnabled()) {
239             _log.debug("Invoking undeploy for " + servletContextName);
240         }
241 
242         for (Map.Entry<String, List<Object>> entry : _eventsMap.entrySet()) {
243             String eventType = entry.getKey();
244             List<Object> events = entry.getValue();
245 
246             for (Object obj : events) {
247                 EventsProcessor.unregisterEvent(eventType, obj);
248             }
249         }
250 
251         for (Map.Entry<String, List<ModelListener>> entry :
252                 _modelListenersMap.entrySet()) {
253 
254             String modelName = entry.getKey();
255             List<ModelListener> modelListeners = entry.getValue();
256 
257             BasePersistenceImpl persistence = getPersistence(modelName);
258 
259             for (ModelListener modelListener : modelListeners) {
260                 persistence.unregisterListener(modelListener);
261             }
262         }
263 
264         Properties portalProperties = _portalPropertiesMap.get(
265             servletContextName);
266 
267         if (portalProperties != null) {
268             destroyPortalProperties(portalProperties);
269         }
270 
271         if (_log.isInfoEnabled()) {
272             _log.info(
273                 "Hook for " + servletContextName +
274                     " unregistered successfully");
275         }
276     }
277 
278     protected BasePersistenceImpl getPersistence(String modelName) {
279         int pos = modelName.lastIndexOf(StringPool.PERIOD);
280 
281         String entityName = modelName.substring(pos + 1);
282 
283         pos = modelName.lastIndexOf(".model.");
284 
285         String packagePath = modelName.substring(0, pos);
286 
287         return (BasePersistenceImpl)PortalBeanLocatorUtil.locate(
288             packagePath + ".service.persistence." + entityName +
289                 "Persistence.impl");
290     }
291 
292     protected Object initEvent(
293             String eventClass, String eventType, ClassLoader portletClassLoader)
294         throws Exception {
295 
296         if (eventType.equals(PropsKeys.APPLICATION_STARTUP_EVENTS)) {
297             SimpleAction simpleAction = new InvokerSimpleAction(
298                 (SimpleAction)portletClassLoader.loadClass(
299                     eventClass).newInstance());
300 
301             long[] companyIds = PortalInstances.getCompanyIds();
302 
303             for (long companyId : companyIds) {
304                 simpleAction.run(new String[] {String.valueOf(companyId)});
305             }
306 
307             return null;
308         }
309 
310         if (eventType.equals(PropsKeys.LOGIN_EVENTS_POST) ||
311             eventType.equals(PropsKeys.LOGIN_EVENTS_PRE) ||
312             eventType.equals(PropsKeys.LOGOUT_EVENTS_POST) ||
313             eventType.equals(PropsKeys.LOGOUT_EVENTS_PRE) ||
314             eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_POST) ||
315             eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_PRE)) {
316 
317             Action action = (Action)portletClassLoader.loadClass(
318                 eventClass).newInstance();
319 
320             EventsProcessor.registerEvent(eventType, action);
321 
322             return action;
323         }
324 
325         return null;
326     }
327 
328     protected ModelListener initModelListener(
329             String modelListenerClass, String modelName,
330             ClassLoader portletClassLoader)
331         throws Exception {
332 
333         ModelListener modelListener =
334             (ModelListener)portletClassLoader.loadClass(
335                 modelListenerClass).newInstance();
336 
337         BasePersistenceImpl persistence = getPersistence(modelName);
338 
339         persistence.registerListener(modelListener);
340 
341         return modelListener;
342     }
343 
344     protected void initPortalProperties(Properties portalProperties)
345         throws Exception {
346 
347         PropsUtil.addProperties(portalProperties);
348 
349         if (_log.isDebugEnabled() &&
350             portalProperties.containsKey(PropsKeys.LOCALES)) {
351 
352             _log.debug(
353                 "Portlet locales " +
354                     portalProperties.getProperty(PropsKeys.LOCALES));
355             _log.debug(
356                 "Merged locales " + PropsUtil.get(PropsKeys.LOCALES));
357             _log.debug(
358                 "Merged locales array length " +
359                     PropsUtil.getArray(PropsKeys.LOCALES).length);
360         }
361 
362         resetPortalProperties(portalProperties);
363     }
364 
365     protected void resetPortalProperties(Properties portalProperties)
366         throws Exception {
367 
368         for (String fieldName : _PROPS_KEYS_BOOLEAN) {
369             String key = StringUtil.replace(
370                 fieldName.toLowerCase(), StringPool.UNDERLINE,
371                 StringPool.PERIOD);
372 
373             if (!containsKey(portalProperties, key)) {
374                 continue;
375             }
376 
377             try {
378                 Field field = PropsValues.class.getField(fieldName);
379 
380                 Boolean value = Boolean.valueOf(GetterUtil.getBoolean(
381                     PropsUtil.get(key)));
382 
383                 field.setBoolean(null, value);
384             }
385             catch (Exception e) {
386                 _log.error(
387                     "Error setting field " + fieldName + ": " + e.getMessage());
388             }
389         }
390 
391         for (String fieldName : _PROPS_KEYS_INTEGER) {
392             String key = StringUtil.replace(
393                 fieldName.toLowerCase(), StringPool.UNDERLINE,
394                 StringPool.PERIOD);
395 
396             if (!containsKey(portalProperties, key)) {
397                 continue;
398             }
399 
400             try {
401                 Field field = PropsValues.class.getField(fieldName);
402 
403                 Integer value = Integer.valueOf(GetterUtil.getInteger(
404                     PropsUtil.get(key)));
405 
406                 field.setInt(null, value);
407             }
408             catch (Exception e) {
409                 _log.error(
410                     "Error setting field " + fieldName + ": " + e.getMessage());
411             }
412         }
413 
414         for (String fieldName : _PROPS_KEYS_LONG) {
415             String key = StringUtil.replace(
416                 fieldName.toLowerCase(), StringPool.UNDERLINE,
417                 StringPool.PERIOD);
418 
419             if (!containsKey(portalProperties, key)) {
420                 continue;
421             }
422 
423             try {
424                 Field field = PropsValues.class.getField(fieldName);
425 
426                 Long value = Long.valueOf(GetterUtil.getLong(
427                     PropsUtil.get(key)));
428 
429                 field.setLong(null, value);
430             }
431             catch (Exception e) {
432                 _log.error(
433                     "Error setting field " + fieldName + ": " + e.getMessage());
434             }
435         }
436 
437         for (String fieldName : _PROPS_KEYS_STRING) {
438             String key = StringUtil.replace(
439                 fieldName.toLowerCase(), StringPool.UNDERLINE,
440                 StringPool.PERIOD);
441 
442             if (!containsKey(portalProperties, key)) {
443                 continue;
444             }
445 
446             try {
447                 Field field = PropsValues.class.getField(fieldName);
448 
449                 String value = GetterUtil.getString(PropsUtil.get(key));
450 
451                 field.set(null, value);
452             }
453             catch (Exception e) {
454                 _log.error(
455                     "Error setting field " + fieldName + ": " + e.getMessage());
456             }
457         }
458 
459         if (containsKey(portalProperties, PropsKeys.LOCALES)) {
460             PropsValues.LOCALES = PropsUtil.getArray(PropsKeys.LOCALES);
461 
462             LanguageUtil.init();
463         }
464 
465         LayoutCacheUtil.clearCache();
466     }
467 
468     private static final String[] _PROPS_KEYS_BOOLEAN = new String[] {
469         "JAVASCRIPT_FAST_LOAD",
470         "LAYOUT_TEMPLATE_CACHE_ENABLED",
471         "LAYOUT_USER_PRIVATE_LAYOUTS_AUTO_CREATE",
472         "LAYOUT_USER_PRIVATE_LAYOUTS_ENABLED",
473         "LAYOUT_USER_PRIVATE_LAYOUTS_MODIFIABLE",
474         "LAYOUT_USER_PUBLIC_LAYOUTS_AUTO_CREATE",
475         "LAYOUT_USER_PUBLIC_LAYOUTS_ENABLED",
476         "LAYOUT_USER_PUBLIC_LAYOUTS_MODIFIABLE",
477         "MY_PLACES_SHOW_COMMUNITY_PRIVATE_SITES_WITH_NO_LAYOUTS",
478         "MY_PLACES_SHOW_COMMUNITY_PUBLIC_SITES_WITH_NO_LAYOUTS",
479         "MY_PLACES_SHOW_ORGANIZATION_PRIVATE_SITES_WITH_NO_LAYOUTS",
480         "MY_PLACES_SHOW_ORGANIZATION_PUBLIC_SITES_WITH_NO_LAYOUTS",
481         "MY_PLACES_SHOW_USER_PRIVATE_SITES_WITH_NO_LAYOUTS",
482         "MY_PLACES_SHOW_USER_PUBLIC_SITES_WITH_NO_LAYOUTS",
483         "ORGANIZATIONS_COUNTRY_REQUIRED",
484         "THEME_CSS_FAST_LOAD"
485     };
486 
487     private static final String[] _PROPS_KEYS_INTEGER = new String[] {
488     };
489 
490     private static final String[] _PROPS_KEYS_LONG = new String[] {
491     };
492 
493     private static final String[] _PROPS_KEYS_STRING = new String[] {
494         "PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR",
495         "PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC"
496     };
497 
498     private static Log _log = LogFactory.getLog(HookHotDeployListener.class);
499 
500     private Map<String, List<Object>> _eventsMap =
501         new HashMap<String, List<Object>>();
502     private Map<String, List<ModelListener>> _modelListenersMap =
503         new HashMap<String, List<ModelListener>>();
504     private Map<String, Properties> _portalPropertiesMap =
505         new HashMap<String, Properties>();
506 
507 }