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.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  /**
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 (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         //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 + " 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         //String timestamp = customJspBag.getTimestamp();
412 
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 }