1
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.FileUtil;
36 import com.liferay.portal.kernel.util.GetterUtil;
37 import com.liferay.portal.kernel.util.HttpUtil;
38 import com.liferay.portal.kernel.util.StringPool;
39 import com.liferay.portal.kernel.util.StringUtil;
40 import com.liferay.portal.kernel.util.Time;
41 import com.liferay.portal.kernel.util.Validator;
42 import com.liferay.portal.kernel.xml.Document;
43 import com.liferay.portal.kernel.xml.Element;
44 import com.liferay.portal.kernel.xml.SAXReaderUtil;
45 import com.liferay.portal.model.ModelListener;
46 import com.liferay.portal.service.persistence.BasePersistence;
47 import com.liferay.portal.servlet.filters.layoutcache.LayoutCacheUtil;
48 import com.liferay.portal.util.PortalInstances;
49 import com.liferay.portal.util.PortalUtil;
50 import com.liferay.portal.util.PropsKeys;
51 import com.liferay.portal.util.PropsUtil;
52 import com.liferay.portal.util.PropsValues;
53 import com.liferay.util.WebDirDetector;
54
55 import java.io.File;
56
57 import java.lang.reflect.Field;
58
59 import java.util.ArrayList;
60 import java.util.HashMap;
61 import java.util.Iterator;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.Properties;
65 import java.util.Set;
66
67 import javax.servlet.ServletContext;
68
69 import org.apache.commons.logging.Log;
70 import org.apache.commons.logging.LogFactory;
71
72
78 public class HookHotDeployListener extends BaseHotDeployListener {
79
80 public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
81 try {
82 doInvokeDeploy(event);
83 }
84 catch (Exception e) {
85 throwHotDeployException(event, "Error registering hook for ", e);
86 }
87 }
88
89 public void invokeUndeploy(HotDeployEvent event) throws HotDeployException {
90 try {
91 doInvokeUndeploy(event);
92 }
93 catch (Exception e) {
94 throwHotDeployException(event, "Error unregistering hook for ", e);
95 }
96 }
97
98 protected boolean containsKey(Properties portalProperties, String key) {
99 if (_log.isDebugEnabled()) {
100 return true;
101 }
102 else {
103 return portalProperties.containsKey(key);
104 }
105 }
106
107 protected void destroyCustomJspBag(CustomJspBag customJspBag) {
108 String customJspDir = customJspBag.getCustomJspDir();
109 List<String> customJsps = customJspBag.getCustomJsps();
110
112 String portalWebDir = PortalUtil.getPortalWebDir();
113
114 for (String customJsp : customJsps) {
115 int pos = customJsp.indexOf(customJspDir);
116
117 String portalJsp = customJsp.substring(
118 pos + customJspDir.length(), customJsp.length());
119
120 File portalJspFile = new File(portalWebDir + portalJsp);
121 File portalJspBackupFile = new File(
122 portalWebDir + portalJsp + ".portal");
123
124 if (portalJspBackupFile.exists()) {
125 FileUtil.copyFile(portalJspBackupFile, portalJspFile);
126
127 portalJspBackupFile.delete();
128 }
129 }
130 }
131
132 protected void destroyPortalProperties(Properties portalProperties)
133 throws Exception {
134
135 PropsUtil.removeProperties(portalProperties);
136
137 if (_log.isDebugEnabled() &&
138 portalProperties.containsKey(PropsKeys.LOCALES)) {
139
140 _log.debug(
141 "Portlet locales " +
142 portalProperties.getProperty(PropsKeys.LOCALES));
143 _log.debug(
144 "Original locales " + PropsUtil.get(PropsKeys.LOCALES));
145 _log.debug(
146 "Original locales array length " +
147 PropsUtil.getArray(PropsKeys.LOCALES).length);
148 }
149
150 resetPortalProperties(portalProperties);
151 }
152
153 protected void doInvokeDeploy(HotDeployEvent event) throws Exception {
154 ServletContext servletContext = event.getServletContext();
155
156 String servletContextName = servletContext.getServletContextName();
157
158 if (_log.isDebugEnabled()) {
159 _log.debug("Invoking deploy for " + servletContextName);
160 }
161
162 String xml = HttpUtil.URLtoString(
163 servletContext.getResource("/WEB-INF/liferay-hook.xml"));
164
165 if (xml == null) {
166 return;
167 }
168
169 if (_log.isInfoEnabled()) {
170 _log.info("Registering hook for " + servletContextName);
171 }
172
173 ClassLoader portletClassLoader = event.getContextClassLoader();
174
175 Document doc = SAXReaderUtil.read(xml, true);
176
177 Element root = doc.getRootElement();
178
179 EventsContainer eventsContainer = new EventsContainer();
180
181 _eventsContainerMap.put(servletContextName, new EventsContainer());
182
183 List<Element> eventEls = root.elements("event");
184
185 for (Element eventEl : eventEls) {
186 String eventClass = eventEl.elementText("event-class");
187 String eventType = eventEl.elementText("event-type");
188
189 Object obj = initEvent(eventClass, eventType, portletClassLoader);
190
191 if (obj != null) {
192 eventsContainer.addEvent(eventType, obj);
193 }
194 }
195
196 ModelListenersContainer modelListenersContainer =
197 new ModelListenersContainer();
198
199 _modelListenersContainerMap.put(
200 servletContextName, new ModelListenersContainer());
201
202 List<Element> modelListenerEls = root.elements("model-listener");
203
204 for (Element modelListenerEl : modelListenerEls) {
205 String modelListenerClass = modelListenerEl.elementText(
206 "model-listener-class");
207 String modelName = modelListenerEl.elementText("model-name");
208
209 ModelListener modelListener = initModelListener(
210 modelListenerClass, modelName, portletClassLoader);
211
212 if (modelListener != null) {
213 modelListenersContainer.addModelListener(
214 modelName, modelListener);
215 }
216 }
217
218 String portalPropertiesLocation = root.elementText("portal-properties");
219
220 if (Validator.isNotNull(portalPropertiesLocation)) {
221 Configuration portalPropertiesConfiguration = null;
222
223 try {
224 String name = portalPropertiesLocation;
225
226 int pos = name.lastIndexOf(".properties");
227
228 if (pos != -1) {
229 name = name.substring(0, pos);
230 }
231
232 portalPropertiesConfiguration =
233 ConfigurationFactoryUtil.getConfiguration(
234 portletClassLoader, name);
235 }
236 catch (Exception e) {
237 _log.error("Unable to read " + portalPropertiesLocation, e);
238 }
239
240 if (portalPropertiesConfiguration != null) {
241 Properties portalProperties =
242 portalPropertiesConfiguration.getProperties();
243
244 if (portalProperties.size() > 0) {
245 _portalPropertiesMap.put(
246 servletContextName, portalProperties);
247
248 initPortalProperties(portalProperties);
249 }
250 }
251 }
252
253 String customJspDir = root.elementText("custom-jsp-dir");
254
255 if (Validator.isNotNull(customJspDir)) {
256 if (_log.isDebugEnabled()) {
257 _log.debug("Custom JSP directory: " + customJspDir);
258 }
259
260 List<String> customJsps = new ArrayList<String>();
261
262 String webDir = WebDirDetector.getRootDir(portletClassLoader);
263
264 getCustomJsps(servletContext, webDir, customJspDir, customJsps);
265
266 if (customJsps.size() > 0) {
267 CustomJspBag customJspBag = new CustomJspBag(
268 customJspDir, customJsps);
269
270 if (_log.isDebugEnabled()) {
271 StringBuilder sb = new StringBuilder();
272
273 sb.append("Custom JSP files:\n");
274
275 Iterator<String> itr = customJsps.iterator();
276
277 while (itr.hasNext()) {
278 String customJsp = itr.next();
279
280 sb.append(customJsp);
281
282 if (itr.hasNext()) {
283 sb.append(StringPool.NEW_LINE);
284 }
285 }
286
287 _log.debug(sb.toString());
288 }
289
290 _customJspBagsMap.put(servletContextName, customJspBag);
291
292 initCustomJspBag(customJspBag);
293 }
294 }
295
296 if (_log.isInfoEnabled()) {
297 _log.info(
298 "Hook for " + servletContextName + " registered successfully");
299 }
300 }
301
302 protected void doInvokeUndeploy(HotDeployEvent event) throws Exception {
303 ServletContext servletContext = event.getServletContext();
304
305 String servletContextName = servletContext.getServletContextName();
306
307 if (_log.isDebugEnabled()) {
308 _log.debug("Invoking undeploy for " + servletContextName);
309 }
310
311 String xml = HttpUtil.URLtoString(
312 servletContext.getResource("/WEB-INF/liferay-hook.xml"));
313
314 if (xml == null) {
315 return;
316 }
317
318 EventsContainer eventsContainer = _eventsContainerMap.get(
319 servletContextName);
320
321 if (eventsContainer != null) {
322 eventsContainer.unregisterEvents();
323 }
324
325 ModelListenersContainer modelListenersContainer =
326 _modelListenersContainerMap.get(servletContextName);
327
328 if (modelListenersContainer != null) {
329 modelListenersContainer.unregisterModelListeners();
330 }
331
332 Properties portalProperties = _portalPropertiesMap.get(
333 servletContextName);
334
335 if (portalProperties != null) {
336 destroyPortalProperties(portalProperties);
337 }
338
339 CustomJspBag customJspBag = _customJspBagsMap.get(servletContextName);
340
341 if (customJspBag != null) {
342 destroyCustomJspBag(customJspBag);
343 }
344
345 if (_log.isInfoEnabled()) {
346 _log.info(
347 "Hook for " + servletContextName +
348 " unregistered successfully");
349 }
350 }
351
352 protected void getCustomJsps(
353 ServletContext servletContext, String webDir, String resourcePath,
354 List<String> customJsps) {
355
356 Set<String> resourcePaths = servletContext.getResourcePaths(
357 resourcePath);
358
359 for (String curResourcePath : resourcePaths) {
360 if (curResourcePath.endsWith(StringPool.SLASH)) {
361 getCustomJsps(
362 servletContext, webDir, curResourcePath, customJsps);
363 }
364 else {
365 String customJsp = webDir + curResourcePath;
366
367 customJsp = StringUtil.replace(
368 customJsp, StringPool.DOUBLE_SLASH, StringPool.SLASH);
369
370 customJsps.add(customJsp);
371 }
372 }
373 }
374
375 protected BasePersistence getPersistence(String modelName) {
376 int pos = modelName.lastIndexOf(StringPool.PERIOD);
377
378 String entityName = modelName.substring(pos + 1);
379
380 pos = modelName.lastIndexOf(".model.");
381
382 String packagePath = modelName.substring(0, pos);
383
384 return (BasePersistence)PortalBeanLocatorUtil.locate(
385 packagePath + ".service.persistence." + entityName +
386 "Persistence.impl");
387 }
388
389 protected void initCustomJspBag(CustomJspBag customJspBag)
390 throws Exception {
391
392 String customJspDir = customJspBag.getCustomJspDir();
393 List<String> customJsps = customJspBag.getCustomJsps();
394
396 String portalWebDir = PortalUtil.getPortalWebDir();
397
398 for (String customJsp : customJsps) {
399 int pos = customJsp.indexOf(customJspDir);
400
401 String portalJsp = customJsp.substring(
402 pos + customJspDir.length(), customJsp.length());
403
404 File portalJspFile = new File(portalWebDir + portalJsp);
405 File portalJspBackupFile = new File(
406 portalWebDir + portalJsp + ".portal");
407
408 if (portalJspFile.exists() && !portalJspBackupFile.exists()) {
409 FileUtil.copyFile(portalJspFile, portalJspBackupFile);
410 }
411
412 String customJspContent = FileUtil.read(customJsp);
413
414 FileUtil.write(portalJspFile, customJspContent);
415 }
416 }
417
418 protected Object initEvent(
419 String eventClass, String eventType, ClassLoader portletClassLoader)
420 throws Exception {
421
422 if (eventType.equals(PropsKeys.APPLICATION_STARTUP_EVENTS)) {
423 SimpleAction simpleAction = new InvokerSimpleAction(
424 (SimpleAction)portletClassLoader.loadClass(
425 eventClass).newInstance());
426
427 long[] companyIds = PortalInstances.getCompanyIds();
428
429 for (long companyId : companyIds) {
430 simpleAction.run(new String[] {String.valueOf(companyId)});
431 }
432
433 return null;
434 }
435
436 if (eventType.equals(PropsKeys.LOGIN_EVENTS_POST) ||
437 eventType.equals(PropsKeys.LOGIN_EVENTS_PRE) ||
438 eventType.equals(PropsKeys.LOGOUT_EVENTS_POST) ||
439 eventType.equals(PropsKeys.LOGOUT_EVENTS_PRE) ||
440 eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_POST) ||
441 eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_PRE)) {
442
443 Action action = (Action)portletClassLoader.loadClass(
444 eventClass).newInstance();
445
446 EventsProcessor.registerEvent(eventType, action);
447
448 return action;
449 }
450
451 return null;
452 }
453
454 protected ModelListener initModelListener(
455 String modelListenerClass, String modelName,
456 ClassLoader portletClassLoader)
457 throws Exception {
458
459 ModelListener modelListener =
460 (ModelListener)portletClassLoader.loadClass(
461 modelListenerClass).newInstance();
462
463 BasePersistence persistence = getPersistence(modelName);
464
465 persistence.registerListener(modelListener);
466
467 return modelListener;
468 }
469
470 protected void initPortalProperties(Properties portalProperties)
471 throws Exception {
472
473 PropsUtil.addProperties(portalProperties);
474
475 if (_log.isDebugEnabled() &&
476 portalProperties.containsKey(PropsKeys.LOCALES)) {
477
478 _log.debug(
479 "Portlet locales " +
480 portalProperties.getProperty(PropsKeys.LOCALES));
481 _log.debug(
482 "Merged locales " + PropsUtil.get(PropsKeys.LOCALES));
483 _log.debug(
484 "Merged locales array length " +
485 PropsUtil.getArray(PropsKeys.LOCALES).length);
486 }
487
488 resetPortalProperties(portalProperties);
489 }
490
491 protected void resetPortalProperties(Properties portalProperties)
492 throws Exception {
493
494 for (String fieldName : _PROPS_KEYS_BOOLEAN) {
495 String key = StringUtil.replace(
496 fieldName.toLowerCase(), StringPool.UNDERLINE,
497 StringPool.PERIOD);
498
499 if (!containsKey(portalProperties, key)) {
500 continue;
501 }
502
503 try {
504 Field field = PropsValues.class.getField(fieldName);
505
506 Boolean value = Boolean.valueOf(GetterUtil.getBoolean(
507 PropsUtil.get(key)));
508
509 field.setBoolean(null, value);
510 }
511 catch (Exception e) {
512 _log.error(
513 "Error setting field " + fieldName + ": " + e.getMessage());
514 }
515 }
516
517 for (String fieldName : _PROPS_KEYS_INTEGER) {
518 String key = StringUtil.replace(
519 fieldName.toLowerCase(), StringPool.UNDERLINE,
520 StringPool.PERIOD);
521
522 if (!containsKey(portalProperties, key)) {
523 continue;
524 }
525
526 try {
527 Field field = PropsValues.class.getField(fieldName);
528
529 Integer value = Integer.valueOf(GetterUtil.getInteger(
530 PropsUtil.get(key)));
531
532 field.setInt(null, value);
533 }
534 catch (Exception e) {
535 _log.error(
536 "Error setting field " + fieldName + ": " + e.getMessage());
537 }
538 }
539
540 for (String fieldName : _PROPS_KEYS_LONG) {
541 String key = StringUtil.replace(
542 fieldName.toLowerCase(), StringPool.UNDERLINE,
543 StringPool.PERIOD);
544
545 if (!containsKey(portalProperties, key)) {
546 continue;
547 }
548
549 try {
550 Field field = PropsValues.class.getField(fieldName);
551
552 Long value = Long.valueOf(GetterUtil.getLong(
553 PropsUtil.get(key)));
554
555 field.setLong(null, value);
556 }
557 catch (Exception e) {
558 _log.error(
559 "Error setting field " + fieldName + ": " + e.getMessage());
560 }
561 }
562
563 for (String fieldName : _PROPS_KEYS_STRING) {
564 String key = StringUtil.replace(
565 fieldName.toLowerCase(), StringPool.UNDERLINE,
566 StringPool.PERIOD);
567
568 if (!containsKey(portalProperties, key)) {
569 continue;
570 }
571
572 try {
573 Field field = PropsValues.class.getField(fieldName);
574
575 String value = GetterUtil.getString(PropsUtil.get(key));
576
577 field.set(null, value);
578 }
579 catch (Exception e) {
580 _log.error(
581 "Error setting field " + fieldName + ": " + e.getMessage());
582 }
583 }
584
585 for (String fieldName : _PROPS_KEYS_STRING_ARRAY) {
586 String key = StringUtil.replace(
587 fieldName.toLowerCase(), StringPool.UNDERLINE,
588 StringPool.PERIOD);
589
590 if (!containsKey(portalProperties, key)) {
591 continue;
592 }
593
594 try {
595 Field field = PropsValues.class.getField(fieldName);
596
597 String[] value = PropsUtil.getArray(key);
598
599 field.set(null, value);
600 }
601 catch (Exception e) {
602 _log.error(
603 "Error setting field " + fieldName + ": " + e.getMessage());
604 }
605 }
606
607 if (containsKey(portalProperties, PropsKeys.LOCALES)) {
608 PropsValues.LOCALES = PropsUtil.getArray(PropsKeys.LOCALES);
609
610 LanguageUtil.init();
611 }
612
613 LayoutCacheUtil.clearCache();
614 }
615
616 private static final String[] _PROPS_KEYS_BOOLEAN = new String[] {
617 "AUTH_FORWARD_BY_LAST_PATH",
618 "JAVASCRIPT_FAST_LOAD",
619 "LAYOUT_TEMPLATE_CACHE_ENABLED",
620 "LAYOUT_USER_PRIVATE_LAYOUTS_AUTO_CREATE",
621 "LAYOUT_USER_PRIVATE_LAYOUTS_ENABLED",
622 "LAYOUT_USER_PRIVATE_LAYOUTS_MODIFIABLE",
623 "LAYOUT_USER_PUBLIC_LAYOUTS_AUTO_CREATE",
624 "LAYOUT_USER_PUBLIC_LAYOUTS_ENABLED",
625 "LAYOUT_USER_PUBLIC_LAYOUTS_MODIFIABLE",
626 "MY_PLACES_SHOW_COMMUNITY_PRIVATE_SITES_WITH_NO_LAYOUTS",
627 "MY_PLACES_SHOW_COMMUNITY_PUBLIC_SITES_WITH_NO_LAYOUTS",
628 "MY_PLACES_SHOW_ORGANIZATION_PRIVATE_SITES_WITH_NO_LAYOUTS",
629 "MY_PLACES_SHOW_ORGANIZATION_PUBLIC_SITES_WITH_NO_LAYOUTS",
630 "MY_PLACES_SHOW_USER_PRIVATE_SITES_WITH_NO_LAYOUTS",
631 "MY_PLACES_SHOW_USER_PUBLIC_SITES_WITH_NO_LAYOUTS",
632 "ORGANIZATIONS_COUNTRY_REQUIRED",
633 "TERMS_OF_USE_REQUIRED",
634 "THEME_CSS_FAST_LOAD"
635 };
636
637 private static final String[] _PROPS_KEYS_INTEGER = new String[] {
638 };
639
640 private static final String[] _PROPS_KEYS_LONG = new String[] {
641 };
642
643 private static final String[] _PROPS_KEYS_STRING = new String[] {
644 "PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR",
645 "PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC"
646 };
647
648 private static final String[] _PROPS_KEYS_STRING_ARRAY = new String[] {
649 "LAYOUT_STATIC_PORTLETS_ALL"
650 };
651
652 private static Log _log = LogFactory.getLog(HookHotDeployListener.class);
653
654 private Map<String, EventsContainer> _eventsContainerMap =
655 new HashMap<String, EventsContainer>();
656 private Map<String, ModelListenersContainer> _modelListenersContainerMap =
657 new HashMap<String, ModelListenersContainer>();
658 private Map<String, Properties> _portalPropertiesMap =
659 new HashMap<String, Properties>();
660 private Map<String, CustomJspBag> _customJspBagsMap =
661 new HashMap<String, CustomJspBag>();
662
663 private class EventsContainer {
664
665 public void addEvent(String eventType, Object event) {
666 List<Object> events = _eventsMap.get(eventType);
667
668 if (events == null) {
669 events = new ArrayList<Object>();
670
671 _eventsMap.put(eventType, events);
672 }
673
674 events.add(event);
675 }
676
677 public void unregisterEvents() {
678 for (Map.Entry<String, List<Object>> entry :
679 _eventsMap.entrySet()) {
680
681 String eventType = entry.getKey();
682 List<Object> events = entry.getValue();
683
684 for (Object event : events) {
685 EventsProcessor.unregisterEvent(eventType, event);
686 }
687 }
688 }
689
690 private Map<String, List<Object>> _eventsMap =
691 new HashMap<String, List<Object>>();
692
693 }
694
695 private class ModelListenersContainer {
696
697 public void addModelListener(
698 String modelName, ModelListener modelListener) {
699
700 List<ModelListener> modelListeners = _modelListenersMap.get(
701 modelName);
702
703 if (modelListeners == null) {
704 modelListeners = new ArrayList<ModelListener>();
705
706 _modelListenersMap.put(modelName, modelListeners);
707 }
708
709 modelListeners.add(modelListener);
710 }
711
712 public void unregisterModelListeners() {
713 for (Map.Entry<String, List<ModelListener>> entry :
714 _modelListenersMap.entrySet()) {
715
716 String modelName = entry.getKey();
717 List<ModelListener> modelListeners = entry.getValue();
718
719 BasePersistence persistence = getPersistence(modelName);
720
721 for (ModelListener modelListener : modelListeners) {
722 persistence.unregisterListener(modelListener);
723 }
724 }
725 }
726
727 private Map<String, List<ModelListener>> _modelListenersMap =
728 new HashMap<String, List<ModelListener>>();
729
730 }
731
732 private class CustomJspBag {
733
734 public CustomJspBag(String customJspDir, List<String> customJsps) {
735 _customJspDir = customJspDir;
736 _customJsps = customJsps;
737 _timestamp = Time.getTimestamp();
738 }
739
740 public String getCustomJspDir() {
741 return _customJspDir;
742 }
743
744 public List<String> getCustomJsps() {
745 return _customJsps;
746 }
747
748 public String getTimestamp() {
749 return _timestamp;
750 }
751
752 private String _customJspDir;
753 private List<String> _customJsps;
754 private String _timestamp;
755
756 }
757
758 }