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