1 package uk.ac.cam.spectra.spectrasub;
2
3 import java.beans.PropertyDescriptor;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.lang.reflect.Method;
9 import java.lang.reflect.Modifier;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Properties;
15 import java.util.Set;
16
17 import nu.xom.Attribute;
18 import nu.xom.Builder;
19 import nu.xom.DocType;
20 import nu.xom.Document;
21 import nu.xom.Element;
22 import nu.xom.Node;
23 import nu.xom.Nodes;
24 import nu.xom.ParsingException;
25 import nu.xom.ValidityException;
26
27 import org.apache.commons.beanutils.PropertyUtils;
28 import org.apache.commons.io.IOUtils;
29 import org.apache.log4j.Logger;
30 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
31
32 import uk.ac.cam.spectra.CMLType;
33 import uk.ac.cam.spectra.EmbargoMetadataEncoder;
34 import uk.ac.cam.spectra.Util;
35
36
37
38
39
40
41
42 public class Profile2BeanConfig {
43
44 private static final String DATA_DIR = "data";
45
46 private final static Logger LOG = Logger
47 .getLogger(Profile2BeanConfig.class);
48
49 private File profile = null;
50
51 private File configPropertiesFile = null;
52
53 private File springBeanFile = null;
54
55 private File workDir = null;
56
57 private Set<String> allProperties = new HashSet<String>(200);
58
59 private Document profileDoc = null;
60
61 private Document springBeans;
62
63 private Properties configProperties = new Properties();
64
65 public void defaultConfiguration(File workspace) {
66 setProfile(new File(workspace, Constants.PROFILE_FILENAME));
67 setConfigPropertiesFile(new File(workspace,
68 Constants.CONFIG_PROPERTY_FILENAME));
69 setSpringBeanFile(new File(workspace, Constants.SPRING_CONFIG_FILENAME));
70 setWorkDir(new File(workspace, DATA_DIR));
71 }
72
73 public void writeConfiguration() {
74 FileOutputStream fout = null;
75 try {
76 if (!springBeanFile.exists()) {
77 springBeanFile.createNewFile();
78 }
79 fout = new FileOutputStream(springBeanFile);
80 Util.print(springBeans, fout, true);
81 } catch (IOException e) {
82 throw new RuntimeException(
83 "Could not create new spring config file: "
84 + e.getMessage(), e);
85 } finally {
86 IOUtils.closeQuietly(fout);
87 }
88 FileOutputStream fout2 = null;
89 try {
90 fout2 = new FileOutputStream(configPropertiesFile);
91 Properties toStore = sort(configProperties);
92 toStore.store(fout2, "Auto-generated by Profile2BeanConfig");
93 } catch (IOException e) {
94 throw new RuntimeException("Could not write config.properties: "
95 + e.getMessage(), e);
96 } finally {
97 IOUtils.closeQuietly(fout2);
98 }
99 }
100
101 private Properties sort(Properties props) {
102 List<String> keys = new ArrayList<String>(props.size());
103 for (Object key : props.keySet()) {
104 keys.add((String) key);
105 }
106 Collections.sort(keys);
107 Properties p2 = new Properties();
108 for (String key : keys) {
109 p2.setProperty(key, props.getProperty(key));
110 }
111 return p2;
112 }
113
114 public void init() {
115 if (profile == null) {
116 throw new IllegalStateException("No valid profile provided");
117 }
118
119 FileInputStream fin = null;
120 try {
121 fin = new FileInputStream(profile);
122 profileDoc = new Builder().build(fin);
123 } catch (ValidityException e) {
124 throw new RuntimeException(e);
125 } catch (ParsingException e) {
126 throw new RuntimeException(e);
127 } catch (IOException e) {
128 throw new RuntimeException(e);
129 } finally {
130 IOUtils.closeQuietly(fin);
131 }
132 }
133
134
135
136
137
138 public void generateConfiguration() throws ConfigurationException {
139 try {
140 Element beans = new Element("beans");
141 springBeans = new Document(beans);
142 springBeans.setDocType(new DocType("beans",
143 "-//SPRING//DTD BEAN//EN",
144 "http://www.springframework.org/dtd/spring-beans.dtd"));
145 beans.addAttribute(new Attribute("default-init-method", "init"));
146 beans.addAttribute(new Attribute("default-autowire", "byType"));
147 beans.appendChild(createProfileBean());
148 beans.appendChild(createPropertyPlaceHolderBean());
149 beans.appendChild(createReflectedBean("application", Configuration.class.getCanonicalName()));
150 beans.appendChild(createPackageDAOBean());
151 beans.appendChild(createPackageManager());
152 beans.appendChild(createTypeBean("cml", CMLType.class
153 .getCanonicalName(), false));
154 Nodes nds = profileDoc.query("//dataFiles/dataFile");
155 for (int i = 0; i < nds.size(); i++) {
156 Element e = (Element) nds.get(i);
157 beans.appendChild(createTypeBean(e.getAttributeValue("name"), e
158 .getAttributeValue("type")));
159 }
160 beans.appendChild(createReflectedBean("embargoEncoder",
161 EmbargoMetadataEncoder.class.getCanonicalName()));
162 Nodes nds2 = profileDoc.query("//packager|//bundler|//submission");
163 for (int i = 0; i < nds2.size(); i++) {
164 Element e = (Element) nds2.get(i);
165 beans.appendChild(createReflectedBean(e.getLocalName(), e
166 .getAttributeValue("type")));
167 }
168 if (configPropertiesFile != null && configPropertiesFile.exists()) {
169 FileInputStream fin = null;
170 try {
171 fin = new FileInputStream(configPropertiesFile);
172 configProperties.load(fin);
173 } catch (IOException e) {
174 throw new RuntimeException(
175 "Could not read config.properties: "
176 + e.getMessage(), e);
177 } finally {
178 IOUtils.closeQuietly(fin);
179 }
180 } else {
181 try {
182 configPropertiesFile.createNewFile();
183 } catch (IOException e) {
184 throw new RuntimeException(
185 "Could not create new config.properties: "
186 + e.getMessage(), e);
187 }
188 }
189 LOG.debug("Adding " + allProperties.size() + " dynamic properties");
190 for (String key : allProperties) {
191 LOG.debug("Adding property: " + key);
192 if (!configProperties.containsKey(key)) {
193 configProperties.setProperty(key, "{Enter value}");
194 }
195 }
196
197 } catch (IOException e) {
198 throw new RuntimeException(e);
199 }
200 }
201
202
203
204
205
206
207 private Node createProfileBean() throws IOException {
208 Element bean = new Element("bean");
209 bean.addAttribute(new Attribute("id", "profile"));
210 bean.addAttribute(new Attribute("class",
211 "uk.ac.cam.spectra.spectrasub.Profile"));
212 Element property = new Element("property");
213 property.addAttribute(new Attribute("name", "file"));
214 bean.appendChild(property);
215 Element value = new Element("value");
216 value.appendChild(profile.getCanonicalPath());
217 property.appendChild(value);
218 return bean;
219 }
220
221
222
223
224
225
226
227 private Node createPropertyPlaceHolderBean() throws IOException {
228 Element bean = new Element("bean");
229 bean.addAttribute(new Attribute("class",
230 PropertyPlaceholderConfigurer.class.getCanonicalName()));
231 Element property = new Element("property");
232 bean.appendChild(property);
233 property.addAttribute(new Attribute("name", "location"));
234 Element value = new Element("value");
235 property.appendChild(value);
236 value.appendChild(configPropertiesFile.getCanonicalFile().toURL()
237 .toExternalForm());
238 return bean;
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 private Node createPackageDAOBean() throws IOException {
256 Element bean = new Element("bean");
257 bean.addAttribute(new Attribute("id", "packageDAO"));
258 bean.addAttribute(new Attribute("class", DataPackageDAO.class
259 .getCanonicalName()));
260 Element prop1 = new Element("property");
261 prop1.addAttribute(new Attribute("name", "storageBase"));
262 prop1.addAttribute(new Attribute("value", workDir.getCanonicalPath()));
263 bean.appendChild(prop1);
264 Element prop2 = new Element("property");
265 prop2.addAttribute(new Attribute("name", "type"));
266 bean.appendChild(prop2);
267 Element map = new Element("map");
268 prop2.appendChild(map);
269 addEntry(map, "cml", "uk.ac.cam.spectra.CMLType");
270 Nodes nds = profileDoc.query("//dataFiles/dataFile");
271 for (int i = 0; i < nds.size(); i++) {
272 Element e = (Element) nds.get(i);
273 addEntry(map, e.getAttributeValue("name"), e.getAttributeValue("type"));
274 }
275 return bean;
276 }
277
278 private void addEntry(Element map, String name, String type) {
279 Element entry = new Element("entry");
280 map.appendChild(entry);
281 entry.addAttribute(new Attribute("key", type));
282 Element ref = new Element("ref");
283 entry.appendChild(ref);
284 ref.addAttribute(new Attribute("bean", name));
285 }
286
287
288
289
290 private Element createPackageManager() {
291 Element bean = new Element("bean");
292 bean.addAttribute(new Attribute("id", "packageManager"));
293 bean.addAttribute(new Attribute("class", DataPackageManager.class
294 .getCanonicalName()));
295 return bean;
296 }
297
298 private Element createTypeBean(String id, String type) {
299 return createTypeBean(id, type, true);
300 }
301
302
303 private Element createTypeBean(String id, String type, boolean autoWire) {
304 Element bean = new Element("bean");
305 bean.addAttribute(new Attribute("id", id));
306 bean.addAttribute(new Attribute("class", type));
307 if (!autoWire) {
308 bean.addAttribute(new Attribute("autowire", "no"));
309 }
310 return bean;
311 }
312
313
314
315
316
317
318
319
320
321 private Element createReflectedBean(String id, String clazz)
322 throws ConfigurationException {
323 Element bean = new Element("bean");
324 bean.addAttribute(new Attribute("id", id));
325 bean.addAttribute(new Attribute("class", clazz));
326 try {
327 Class c = Class.forName(clazz);
328 for (PropertyDescriptor pd : PropertyUtils
329 .getPropertyDescriptors(c)) {
330 String pName = pd.getName();
331 LOG.debug("Property is " + pName + " writable? "
332 + isWritable(pd));
333 if (String.class.equals(pd.getPropertyType()) && isWritable(pd)) {
334 String qName = id + "." + pName;
335 allProperties.add(qName);
336 Element property = new Element("property");
337 bean.appendChild(property);
338 property.addAttribute(new Attribute("name", pName));
339 property.addAttribute(new Attribute("value", "${" + qName
340 + "}"));
341 }
342 }
343 } catch (ClassNotFoundException e) {
344 throw new ConfigurationException(
345 "Profile refers to a non-existent class " + clazz + ". "
346 + e.getMessage(), e);
347 } catch (SecurityException e) {
348 throw new RuntimeException(e);
349 }
350 return bean;
351 }
352
353 private boolean isWritable(PropertyDescriptor pd) {
354 Method write = pd.getWriteMethod();
355 LOG.debug(pd + " write: " + write);
356 if (write == null) {
357 return false;
358 } else {
359 return Modifier.isPublic(write.getModifiers());
360 }
361 }
362
363 public File getConfigPropertiesFile() {
364 return configPropertiesFile;
365 }
366
367 public void setConfigPropertiesFile(File configPropertiesFile) {
368 this.configPropertiesFile = configPropertiesFile;
369 }
370
371 public File getProfile() {
372 return profile;
373 }
374
375 public void setProfile(File profile) {
376 this.profile = profile;
377 }
378
379 public File getSpringBeanFile() {
380 return springBeanFile;
381 }
382
383 public void setSpringBeanFile(File springBeanFile) {
384 this.springBeanFile = springBeanFile;
385 }
386
387 public Properties getConfigProperties() {
388 return configProperties;
389 }
390
391 public Document getSpringBeans() {
392 return springBeans;
393 }
394
395 public File getWorkDir() {
396 return workDir;
397 }
398
399 public void setWorkDir(File workDir) {
400 this.workDir = workDir;
401 }
402
403 }