1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.datadictionary;
17
18 import no.geosoft.cc.io.FileListener;
19 import no.geosoft.cc.io.FileMonitor;
20 import org.apache.commons.collections.ListUtils;
21 import org.apache.commons.lang.StringUtils;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.kuali.rice.core.api.config.property.ConfigurationService;
25 import org.kuali.rice.krad.service.KRADServiceLocator;
26 import org.kuali.rice.krad.uif.util.UifBeanFactoryPostProcessor;
27 import org.kuali.rice.krad.util.KRADConstants;
28 import org.springframework.beans.BeansException;
29 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
30 import org.springframework.context.ApplicationContext;
31 import org.springframework.context.ApplicationContextAware;
32 import org.springframework.context.ApplicationListener;
33 import org.springframework.context.ConfigurableApplicationContext;
34 import org.springframework.context.event.ContextClosedEvent;
35 import org.springframework.core.io.FileSystemResource;
36 import org.springframework.core.io.InputStreamResource;
37 import org.springframework.core.io.Resource;
38
39 import java.io.File;
40 import java.io.InputStream;
41 import java.net.URL;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class ReloadingDataDictionary extends DataDictionary implements FileListener, URLMonitor.URLContentChangedListener, ApplicationContextAware {
66 private static final Log LOG = LogFactory.getLog(DataDictionary.class);
67
68 private static final String CLASS_DIR_CONFIG_PARM = "reload.data.dictionary.classes.dir";
69 private static final String SOURCE_DIR_CONFIG_PARM = "reload.data.dictionary.source.dir";
70 private static final String INTERVAL_CONFIG_PARM = "reload.data.dictionary.interval";
71
72 private Map<String, String> fileToNamespaceMapping;
73 private Map<String, String> urlToNamespaceMapping;
74
75 private FileMonitor dictionaryFileMonitor;
76 private URLMonitor dictionaryUrlMonitor;
77
78 public ReloadingDataDictionary() {
79 super();
80 }
81
82
83
84
85
86
87
88 @Override
89 public void parseDataDictionaryConfigurationFiles(boolean allowConcurrentValidation) {
90 ConfigurationService configurationService = KRADServiceLocator.getKualiConfigurationService();
91
92
93 String classesDir = configurationService.getPropertyValueAsString(CLASS_DIR_CONFIG_PARM);
94
95
96 String sourceDir = configurationService.getPropertyValueAsString(SOURCE_DIR_CONFIG_PARM);
97
98
99 int reloadInterval = Integer.parseInt(configurationService.getPropertyValueAsString(INTERVAL_CONFIG_PARM));
100
101 dictionaryFileMonitor = new FileMonitor(reloadInterval);
102 dictionaryFileMonitor.addListener(this);
103
104 dictionaryUrlMonitor = new URLMonitor(reloadInterval);
105 dictionaryUrlMonitor.addListener(this);
106
107 super.parseDataDictionaryConfigurationFiles(allowConcurrentValidation);
108
109
110
111 fileToNamespaceMapping = new HashMap<String, String>();
112 urlToNamespaceMapping = new HashMap<String, String>();
113
114
115 for (Map.Entry<String, List<String>> moduleDictionary : moduleDictionaryFiles.entrySet()) {
116 String namespace = moduleDictionary.getKey();
117 List<String> configLocations = moduleDictionary.getValue();
118
119 for (String configLocation : configLocations) {
120 Resource classFileResource = getFileResource(configLocation);
121
122 try {
123 if (classFileResource.getURI().toString().startsWith("jar:")) {
124 LOG.trace("Monitoring dictionary file at URI: " + classFileResource.getURI().toString());
125
126 dictionaryUrlMonitor.addURI(classFileResource.getURL());
127 urlToNamespaceMapping.put(classFileResource.getURL().toString(), namespace);
128 } else {
129 String filePathClassesDir = classFileResource.getFile().getAbsolutePath();
130 String sourceFilePath = StringUtils.replace(filePathClassesDir, classesDir, sourceDir);
131
132 File dictionaryFile = new File(filePathClassesDir);
133 if (dictionaryFile.exists()) {
134 LOG.trace("Monitoring dictionary file: " + dictionaryFile.getName());
135
136 dictionaryFileMonitor.addFile(dictionaryFile);
137 fileToNamespaceMapping.put(dictionaryFile.getAbsolutePath(), namespace);
138 }
139 }
140 } catch (Exception e) {
141 LOG.info("Exception in picking up dictionary file for monitoring: " + e.getMessage(), e);
142 }
143 }
144 }
145 }
146
147
148
149
150
151
152
153
154 public void fileChanged(File file) {
155 LOG.info("reloading dictionary configuration for " + file.getName());
156 try {
157 List<String> beforeReloadBeanNames = Arrays.asList(ddBeans.getBeanDefinitionNames());
158
159 Resource resource = new FileSystemResource(file);
160 xmlReader.loadBeanDefinitions(resource);
161
162 List<String> afterReloadBeanNames = Arrays.asList(ddBeans.getBeanDefinitionNames());
163
164 List<String> addedBeanNames = ListUtils.removeAll(afterReloadBeanNames, beforeReloadBeanNames);
165 String namespace = KRADConstants.DEFAULT_NAMESPACE;
166 if (fileToNamespaceMapping.containsKey(file.getAbsolutePath())) {
167 namespace = fileToNamespaceMapping.get(file.getAbsolutePath());
168 }
169
170 ddIndex.addBeanNamesToNamespace(namespace, addedBeanNames);
171
172 performDictionaryPostProcessing(true);
173 } catch (Exception e) {
174 LOG.info("Exception in dictionary hot deploy: " + e.getMessage(), e);
175 }
176 }
177
178 public void urlContentChanged(final URL url) {
179 LOG.info("reloading dictionary configuration for " + url.toString());
180 try {
181 InputStream urlStream = url.openStream();
182 InputStreamResource resource = new InputStreamResource(urlStream);
183
184 List<String> beforeReloadBeanNames = Arrays.asList(ddBeans.getBeanDefinitionNames());
185
186 int originalValidationMode = xmlReader.getValidationMode();
187 xmlReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
188 xmlReader.loadBeanDefinitions(resource);
189 xmlReader.setValidationMode(originalValidationMode);
190
191 List<String> afterReloadBeanNames = Arrays.asList(ddBeans.getBeanDefinitionNames());
192
193 List<String> addedBeanNames = ListUtils.removeAll(afterReloadBeanNames, beforeReloadBeanNames);
194 String namespace = KRADConstants.DEFAULT_NAMESPACE;
195 if (urlToNamespaceMapping.containsKey(url.toString())) {
196 namespace = urlToNamespaceMapping.get(url.toString());
197 }
198
199 ddIndex.addBeanNamesToNamespace(namespace, addedBeanNames);
200
201 performDictionaryPostProcessing(true);
202 } catch (Exception e) {
203 LOG.info("Exception in dictionary hot deploy: " + e.getMessage(), e);
204 }
205 }
206
207 @Override
208 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
209
210 if (applicationContext instanceof ConfigurableApplicationContext) {
211 ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
212 context.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
213 @Override
214 public void onApplicationEvent(ContextClosedEvent e) {
215 LOG.info("Context '" + e.getApplicationContext().getDisplayName() +
216 "' closed, shutting down URLMonitor scheduler");
217 dictionaryUrlMonitor.shutdownScheduler();
218 }
219 });
220 }
221 }
222 }