1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17   * SOFTWARE.
18   */
19  
20  package com.liferay.portal.deploy.hot;
21  
22  import com.liferay.portal.events.EventsProcessor;
23  import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
24  import com.liferay.portal.kernel.configuration.Configuration;
25  import com.liferay.portal.kernel.configuration.ConfigurationFactoryUtil;
26  import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
27  import com.liferay.portal.kernel.deploy.hot.HotDeployException;
28  import com.liferay.portal.kernel.events.Action;
29  import com.liferay.portal.kernel.events.ActionWrapper;
30  import com.liferay.portal.kernel.events.InvokerSimpleAction;
31  import com.liferay.portal.kernel.events.SimpleAction;
32  import com.liferay.portal.kernel.events.SimpleActionWrapper;
33  import com.liferay.portal.kernel.language.LanguageUtil;
34  import com.liferay.portal.kernel.log.Log;
35  import com.liferay.portal.kernel.log.LogFactoryUtil;
36  import com.liferay.portal.kernel.util.FileUtil;
37  import com.liferay.portal.kernel.util.GetterUtil;
38  import com.liferay.portal.kernel.util.HttpUtil;
39  import com.liferay.portal.kernel.util.StringPool;
40  import com.liferay.portal.kernel.util.StringUtil;
41  import com.liferay.portal.kernel.util.Time;
42  import com.liferay.portal.kernel.util.Validator;
43  import com.liferay.portal.kernel.xml.Document;
44  import com.liferay.portal.kernel.xml.Element;
45  import com.liferay.portal.kernel.xml.SAXReaderUtil;
46  import com.liferay.portal.model.ModelListener;
47  import com.liferay.portal.model.ModelListenerWrapper;
48  import com.liferay.portal.security.auth.CompanyThreadLocal;
49  import com.liferay.portal.service.persistence.BasePersistence;
50  import com.liferay.portal.servlet.filters.cache.CacheUtil;
51  import com.liferay.portal.util.PortalInstances;
52  import com.liferay.portal.util.PortalUtil;
53  import com.liferay.portal.util.PropsKeys;
54  import com.liferay.portal.util.PropsUtil;
55  import com.liferay.portal.util.PropsValues;
56  
57  import java.io.File;
58  
59  import java.lang.reflect.Field;
60  
61  import java.util.ArrayList;
62  import java.util.HashMap;
63  import java.util.HashSet;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Map;
67  import java.util.Properties;
68  import java.util.Set;
69  
70  import javax.servlet.ServletContext;
71  
72  /**
73   * <a href="HookHotDeployListener.java.html"><b><i>View Source</i></b></a>
74   *
75   * @author Brian Wing Shun Chan
76   *
77   */
78  public class HookHotDeployListener extends BaseHotDeployListener {
79  
80      public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
81          try {
82              doInvokeDeploy(event);
83          }
84          catch (Throwable t) {
85              throwHotDeployException(event, "Error registering hook for ", t);
86          }
87      }
88  
89      public void invokeUndeploy(HotDeployEvent event) throws HotDeployException {
90          try {
91              doInvokeUndeploy(event);
92          }
93          catch (Throwable t) {
94              throwHotDeployException(event, "Error unregistering hook for ", t);
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         //String timestamp = customJspBag.getTimestamp();
111 
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 + " is available for use");
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("Hook for " + servletContextName + " was unregistered");
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 File getPortalJspBackupFile(File portalJspFile) {
390         String fileName = portalJspFile.toString();
391 
392         if (fileName.endsWith(".jsp")) {
393             fileName =
394                 fileName.substring(0, fileName.length() - 4) + ".portal.jsp";
395         }
396         else if (fileName.endsWith(".jspf")) {
397             fileName =
398                 fileName.substring(0, fileName.length() - 5) + ".portal.jsp";
399         }
400 
401         return new File(fileName);
402     }
403 
404     protected void initCustomJspBag(CustomJspBag customJspBag)
405         throws Exception {
406 
407         String customJspDir = customJspBag.getCustomJspDir();
408         List<String> customJsps = customJspBag.getCustomJsps();
409         //String timestamp = customJspBag.getTimestamp();
410 
411         String portalWebDir = PortalUtil.getPortalWebDir();
412 
413         for (String customJsp : customJsps) {
414             int pos = customJsp.indexOf(customJspDir);
415 
416             String portalJsp = customJsp.substring(
417                 pos + customJspDir.length(), customJsp.length());
418 
419             File portalJspFile = new File(portalWebDir + portalJsp);
420             File portalJspBackupFile = getPortalJspBackupFile(portalJspFile);
421 
422             if (portalJspFile.exists() && !portalJspBackupFile.exists()) {
423                 FileUtil.copyFile(portalJspFile, portalJspBackupFile);
424             }
425 
426             String customJspContent = FileUtil.read(customJsp);
427 
428             FileUtil.write(portalJspFile, customJspContent);
429         }
430     }
431 
432     protected Object initEvent(
433             String eventClass, String eventType, ClassLoader portletClassLoader)
434         throws Exception {
435 
436         if (eventType.equals(PropsKeys.APPLICATION_STARTUP_EVENTS)) {
437             SimpleAction simpleAction = new InvokerSimpleAction(
438                 (SimpleAction)portletClassLoader.loadClass(
439                     eventClass).newInstance());
440 
441             simpleAction = new SimpleActionWrapper(
442                 simpleAction, portletClassLoader);
443 
444             long companyId = CompanyThreadLocal.getCompanyId();
445 
446             long[] companyIds = PortalInstances.getCompanyIds();
447 
448             for (long curCompanyId : companyIds) {
449                 CompanyThreadLocal.setCompanyId(curCompanyId);
450 
451                 simpleAction.run(new String[] {String.valueOf(curCompanyId)});
452             }
453 
454             CompanyThreadLocal.setCompanyId(companyId);
455 
456             return null;
457         }
458 
459         if (eventType.equals(PropsKeys.LOGIN_EVENTS_POST) ||
460             eventType.equals(PropsKeys.LOGIN_EVENTS_PRE) ||
461             eventType.equals(PropsKeys.LOGOUT_EVENTS_POST) ||
462             eventType.equals(PropsKeys.LOGOUT_EVENTS_PRE) ||
463             eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_POST) ||
464             eventType.equals(PropsKeys.SERVLET_SERVICE_EVENTS_PRE)) {
465 
466             Action action = (Action)portletClassLoader.loadClass(
467                 eventClass).newInstance();
468 
469             action = new ActionWrapper(action, portletClassLoader);
470 
471             EventsProcessor.registerEvent(eventType, action);
472 
473             return action;
474         }
475 
476         return null;
477     }
478 
479     protected ModelListener initModelListener(
480             String modelListenerClass, String modelName,
481             ClassLoader portletClassLoader)
482         throws Exception {
483 
484         ModelListener modelListener =
485             (ModelListener)portletClassLoader.loadClass(
486                 modelListenerClass).newInstance();
487 
488         modelListener = new ModelListenerWrapper(
489             modelListener, portletClassLoader);
490 
491         BasePersistence persistence = getPersistence(modelName);
492 
493         persistence.registerListener(modelListener);
494 
495         return modelListener;
496     }
497 
498     protected void initPortalProperties(Properties portalProperties)
499         throws Exception {
500 
501         PropsUtil.addProperties(portalProperties);
502 
503         if (_log.isDebugEnabled() &&
504             portalProperties.containsKey(PropsKeys.LOCALES)) {
505 
506             _log.debug(
507                 "Portlet locales " +
508                     portalProperties.getProperty(PropsKeys.LOCALES));
509             _log.debug(
510                 "Merged locales " + PropsUtil.get(PropsKeys.LOCALES));
511             _log.debug(
512                 "Merged locales array length " +
513                     PropsUtil.getArray(PropsKeys.LOCALES).length);
514         }
515 
516         resetPortalProperties(portalProperties);
517     }
518 
519     protected void resetPortalProperties(Properties portalProperties)
520         throws Exception {
521 
522         for (String fieldName : _PROPS_KEYS_BOOLEAN) {
523             String key = StringUtil.replace(
524                 fieldName.toLowerCase(), StringPool.UNDERLINE,
525                 StringPool.PERIOD);
526 
527             if (!containsKey(portalProperties, key)) {
528                 continue;
529             }
530 
531             try {
532                 Field field = PropsValues.class.getField(fieldName);
533 
534                 Boolean value = Boolean.valueOf(GetterUtil.getBoolean(
535                     PropsUtil.get(key)));
536 
537                 field.setBoolean(null, value);
538             }
539             catch (Exception e) {
540                 _log.error(
541                     "Error setting field " + fieldName + ": " + e.getMessage());
542             }
543         }
544 
545         for (String fieldName : _PROPS_KEYS_INTEGER) {
546             String key = StringUtil.replace(
547                 fieldName.toLowerCase(), StringPool.UNDERLINE,
548                 StringPool.PERIOD);
549 
550             if (!containsKey(portalProperties, key)) {
551                 continue;
552             }
553 
554             try {
555                 Field field = PropsValues.class.getField(fieldName);
556 
557                 Integer value = Integer.valueOf(GetterUtil.getInteger(
558                     PropsUtil.get(key)));
559 
560                 field.setInt(null, value);
561             }
562             catch (Exception e) {
563                 _log.error(
564                     "Error setting field " + fieldName + ": " + e.getMessage());
565             }
566         }
567 
568         for (String fieldName : _PROPS_KEYS_LONG) {
569             String key = StringUtil.replace(
570                 fieldName.toLowerCase(), StringPool.UNDERLINE,
571                 StringPool.PERIOD);
572 
573             if (!containsKey(portalProperties, key)) {
574                 continue;
575             }
576 
577             try {
578                 Field field = PropsValues.class.getField(fieldName);
579 
580                 Long value = Long.valueOf(GetterUtil.getLong(
581                     PropsUtil.get(key)));
582 
583                 field.setLong(null, value);
584             }
585             catch (Exception e) {
586                 _log.error(
587                     "Error setting field " + fieldName + ": " + e.getMessage());
588             }
589         }
590 
591         for (String fieldName : _PROPS_KEYS_STRING) {
592             String key = StringUtil.replace(
593                 fieldName.toLowerCase(), StringPool.UNDERLINE,
594                 StringPool.PERIOD);
595 
596             if (!containsKey(portalProperties, key)) {
597                 continue;
598             }
599 
600             try {
601                 Field field = PropsValues.class.getField(fieldName);
602 
603                 String value = GetterUtil.getString(PropsUtil.get(key));
604 
605                 field.set(null, value);
606             }
607             catch (Exception e) {
608                 _log.error(
609                     "Error setting field " + fieldName + ": " + e.getMessage());
610             }
611         }
612 
613         for (String fieldName : _PROPS_KEYS_STRING_ARRAY) {
614             String key = StringUtil.replace(
615                 fieldName.toLowerCase(), StringPool.UNDERLINE,
616                 StringPool.PERIOD);
617 
618             if (!containsKey(portalProperties, key)) {
619                 continue;
620             }
621 
622             try {
623                 Field field = PropsValues.class.getField(fieldName);
624 
625                 String[] value = PropsUtil.getArray(key);
626 
627                 field.set(null, value);
628             }
629             catch (Exception e) {
630                 _log.error(
631                     "Error setting field " + fieldName + ": " + e.getMessage());
632             }
633         }
634 
635         if (containsKey(portalProperties, PropsKeys.LOCALES)) {
636             PropsValues.LOCALES = PropsUtil.getArray(PropsKeys.LOCALES);
637 
638             LanguageUtil.init();
639         }
640 
641         CacheUtil.clearCache();
642     }
643 
644     private static final String[] _PROPS_KEYS_BOOLEAN = new String[] {
645         "AUTH_FORWARD_BY_LAST_PATH",
646         "JAVASCRIPT_FAST_LOAD",
647         "LAYOUT_TEMPLATE_CACHE_ENABLED",
648         "LAYOUT_USER_PRIVATE_LAYOUTS_AUTO_CREATE",
649         "LAYOUT_USER_PRIVATE_LAYOUTS_ENABLED",
650         "LAYOUT_USER_PRIVATE_LAYOUTS_MODIFIABLE",
651         "LAYOUT_USER_PUBLIC_LAYOUTS_AUTO_CREATE",
652         "LAYOUT_USER_PUBLIC_LAYOUTS_ENABLED",
653         "LAYOUT_USER_PUBLIC_LAYOUTS_MODIFIABLE",
654         "MY_PLACES_SHOW_COMMUNITY_PRIVATE_SITES_WITH_NO_LAYOUTS",
655         "MY_PLACES_SHOW_COMMUNITY_PUBLIC_SITES_WITH_NO_LAYOUTS",
656         "MY_PLACES_SHOW_ORGANIZATION_PRIVATE_SITES_WITH_NO_LAYOUTS",
657         "MY_PLACES_SHOW_ORGANIZATION_PUBLIC_SITES_WITH_NO_LAYOUTS",
658         "MY_PLACES_SHOW_USER_PRIVATE_SITES_WITH_NO_LAYOUTS",
659         "MY_PLACES_SHOW_USER_PUBLIC_SITES_WITH_NO_LAYOUTS",
660         "ORGANIZATIONS_COUNTRY_REQUIRED",
661         "TERMS_OF_USE_REQUIRED",
662         "THEME_CSS_FAST_LOAD",
663         "THEME_IMAGES_FAST_LOAD"
664     };
665 
666     private static final String[] _PROPS_KEYS_INTEGER = new String[] {
667     };
668 
669     private static final String[] _PROPS_KEYS_LONG = new String[] {
670     };
671 
672     private static final String[] _PROPS_KEYS_STRING = new String[] {
673         "PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR",
674         "PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC"
675     };
676 
677     private static final String[] _PROPS_KEYS_STRING_ARRAY = new String[] {
678         "LAYOUT_STATIC_PORTLETS_ALL"
679     };
680 
681     private static Log _log =
682          LogFactoryUtil.getLog(HookHotDeployListener.class);
683 
684     private Set<String> _servletContextNames = new HashSet<String>();
685     private Map<String, EventsContainer> _eventsContainerMap =
686         new HashMap<String, EventsContainer>();
687     private Map<String, ModelListenersContainer> _modelListenersContainerMap =
688         new HashMap<String, ModelListenersContainer>();
689     private Map<String, Properties> _portalPropertiesMap =
690         new HashMap<String, Properties>();
691     private Map<String, CustomJspBag> _customJspBagsMap =
692         new HashMap<String, CustomJspBag>();
693 
694     private class EventsContainer {
695 
696         public void addEvent(String eventType, Object event) {
697             List<Object> events = _eventsMap.get(eventType);
698 
699             if (events == null) {
700                 events = new ArrayList<Object>();
701 
702                 _eventsMap.put(eventType, events);
703             }
704 
705             events.add(event);
706         }
707 
708         public void unregisterEvents() {
709             for (Map.Entry<String, List<Object>> entry :
710                     _eventsMap.entrySet()) {
711 
712                 String eventType = entry.getKey();
713                 List<Object> events = entry.getValue();
714 
715                 for (Object event : events) {
716                     EventsProcessor.unregisterEvent(eventType, event);
717                 }
718             }
719         }
720 
721         private Map<String, List<Object>> _eventsMap =
722             new HashMap<String, List<Object>>();
723 
724     }
725 
726     private class ModelListenersContainer {
727 
728         public void addModelListener(
729             String modelName, ModelListener modelListener) {
730 
731             List<ModelListener> modelListeners = _modelListenersMap.get(
732                 modelName);
733 
734             if (modelListeners == null) {
735                 modelListeners = new ArrayList<ModelListener>();
736 
737                 _modelListenersMap.put(modelName, modelListeners);
738             }
739 
740             modelListeners.add(modelListener);
741         }
742 
743         public void unregisterModelListeners() {
744             for (Map.Entry<String, List<ModelListener>> entry :
745                     _modelListenersMap.entrySet()) {
746 
747                 String modelName = entry.getKey();
748                 List<ModelListener> modelListeners = entry.getValue();
749 
750                 BasePersistence persistence = getPersistence(modelName);
751 
752                 for (ModelListener modelListener : modelListeners) {
753                     persistence.unregisterListener(modelListener);
754                 }
755             }
756         }
757 
758         private Map<String, List<ModelListener>> _modelListenersMap =
759             new HashMap<String, List<ModelListener>>();
760 
761     }
762 
763     private class CustomJspBag {
764 
765         public CustomJspBag(String customJspDir, List<String> customJsps) {
766             _customJspDir = customJspDir;
767             _customJsps = customJsps;
768             _timestamp = Time.getTimestamp();
769         }
770 
771         public String getCustomJspDir() {
772             return _customJspDir;
773         }
774 
775         public List<String> getCustomJsps() {
776             return _customJsps;
777         }
778 
779         public String getTimestamp() {
780             return _timestamp;
781         }
782 
783         private String _customJspDir;
784         private List<String> _customJsps;
785         private String _timestamp;
786 
787     }
788 
789 }