1 package org.naftulin.configmgr.parsers;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedReader;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.InputStreamReader;
8 import java.io.Reader;
9 import java.io.StringReader;
10 import java.net.URL;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.regex.Matcher;
14 import java.util.regex.Pattern;
15
16 import org.apache.commons.digester.Digester;
17 import org.apache.commons.digester.xmlrules.DigesterLoader;
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.naftulin.configmgr.ConfigurationManagementEntry;
21 import org.naftulin.configmgr.ConfigurationManagementEntryImpl;
22 import org.naftulin.configmgr.ConfigurationManagerException;
23 import org.naftulin.configmgr.ConfigurationType;
24 import org.naftulin.configmgr.content.MasterRecordImpl;
25 import org.xml.sax.SAXException;
26
27 /***
28 * Parses master configuration file to create a configuration entry.
29 *
30 * @author Henry Naftulin
31 * @version 1.0
32 */
33 public class MasterRecordParser extends AbstractConfigEntryParser {
34 private static final long serialVersionUID = 1L;
35 private static final String MASTER_RECORD_RULES_XML = "master-record-rules.xml";
36 private static final Logger log = Logger.getLogger(MasterRecordParser.class);
37
38 /***
39 * Retrurns a configuration managment entry by reading the master record file passed in, and storing it's content.
40 * @param key the key configuration entry will be assigned
41 * @param fileUrl the file URL to be parsed.
42 * @return a configuration managment with content of master configuration file.
43 * @throws ConfigurationManagerException if an error occurs while parsing an entry.
44 */
45 public ConfigurationManagementEntry getConfigurationManagementEntry(final String key,
46 final URL fileUrl) throws ConfigurationManagerException {
47 validateParameters(key, fileUrl);
48
49 final String fileName = fileUrl.getFile();
50 String content = null;
51 ConfigurationManagementEntry entry = null;
52 try {
53 final InputStream stream = fileUrl.openStream();
54 log.debug("reading master record configuration from file " + fileName);
55 content = readStreamContentAsString(stream);
56 log.debug("master record configuration is " + content);
57
58 } catch (IOException e) {
59 log.warn("Error while reading log4j file", e);
60 throw new ConfigurationManagerException("Error while reading log4j file",e);
61 }
62 entry = new ConfigurationManagementEntryImpl(key, fileName , content, this, ConfigurationType.MASTER_RECORD);
63 log.info("configured entry " + entry);
64 return entry;
65 }
66
67 private void validateParameters(final String key, final URL fileUrl)
68 throws ConfigurationManagerException {
69 if (fileUrl == null) {
70 throw new ConfigurationManagerException("file URL is null");
71 }
72 if (fileUrl.getFile() == null) {
73 throw new ConfigurationManagerException("file name passed in the URL " + fileUrl + " is null");
74 }
75 if (key == null) {
76 throw new ConfigurationManagerException("key is null");
77 }
78 }
79
80 /***
81 * Returns a string representation of this parser.
82 * @return a string representation of this parser.
83 */
84 public String toString() {
85 return "master record parser";
86 }
87
88 /***
89 * Returns master record based on the configuration file passed in. Parses the passed in file based
90 * on the digester rules defined in MASTER_RECORD_RULES_XML and
91 * uses commons digester to create master record object based on it.
92 * @param fileName xml file that describe the configuration.
93 * @return master record.
94 * @exception ConfigurationManagerException if a master record cannot be parsed.
95 */
96 public MasterRecordImpl digestMasterRecord(final String fileName) throws ConfigurationManagerException{
97 MasterRecordImpl masterRecord = null;
98 final Digester masterRecordDigester = getDigester();
99 if (masterRecordDigester == null) {
100 throw new ConfigurationManagerException("Major error: could not create a digester from " + MASTER_RECORD_RULES_XML + " file with rules to parse master record. Make sure all jars that configuration manager depends on are present.");
101 }
102 log.debug("loaded rules to read master record");
103
104 try {
105 log.debug("loading master record file " + fileName);
106 final URL masterRecordXml = MasterRecordParser.class.getClassLoader().getResource(fileName);
107 if (masterRecordXml == null) {
108 throw new ConfigurationManagerException("Could not read master record from file " + fileName + ". Please check whether this file exists and you have read permissions to read it.");
109 }
110
111 final Reader preprocessedStream = preprocessConfigurationFile(masterRecordXml);
112
113 masterRecord = (MasterRecordImpl) masterRecordDigester.parse(preprocessedStream);
114 if (masterRecord == null) {
115 throw new ConfigurationManagerException("Could not parse master record from file " + fileName + " based on rules file " + MASTER_RECORD_RULES_XML + ". Master record is null. Please check these files to make sure they are valid");
116 }
117 masterRecord.setFileName(fileName);
118 } catch (IOException e) {
119 throw new ConfigurationManagerException("Could not read master record from file " + fileName + " and rules file " + MASTER_RECORD_RULES_XML + ". Please check whether these files exist and you have read permissions to read them.", e);
120 } catch (SAXException e) {
121 throw new ConfigurationManagerException("Could not parse master record from file " + fileName + " and rules file " + MASTER_RECORD_RULES_XML + ". Please check whether these files are valid xml files.", e);
122 }
123 log.info("master record parsed " + masterRecord);
124 return masterRecord;
125 }
126
127 private Reader preprocessConfigurationFile(final URL masterRecordXml) throws IOException {
128 final BufferedReader readStream = new BufferedReader(new InputStreamReader(masterRecordXml.openStream()));
129 final StringBuffer sb = new StringBuffer();
130 String line;
131 while((line=readStream.readLine()) != null) {
132 sb.append(line);
133 }
134
135 final String fileContent = sb.toString();
136 final String modifiedFileContent = doSubstitutions(fileContent);
137
138 final Reader preprocessedFile = new StringReader(modifiedFileContent);
139 return preprocessedFile;
140 }
141
142 String doSubstitutions(final String fileContent) {
143 String modifiedFileContent = fileContent;
144
145 final Pattern environmentVarPattern = Pattern.compile("//?//S+//?");
146 final Matcher environmentVarMatcher = environmentVarPattern.matcher(fileContent);
147
148 String environmentVar = null;
149 String value = null;
150 while(environmentVarMatcher.find()) {
151 String environmentVarWithMarkers = environmentVarMatcher.group();
152 if (environmentVarWithMarkers.length()>2) {
153 environmentVar = environmentVarWithMarkers.substring(1, environmentVarWithMarkers.length()-1);
154 value = getEnvironmentVarValue(environmentVar);
155 modifiedFileContent = StringUtils.replace(modifiedFileContent, environmentVarWithMarkers, value);
156 }
157 }
158 return modifiedFileContent;
159 }
160
161 String getEnvironmentVarValue(final String environmentVar) {
162 String value = System.getProperty(environmentVar);
163
164 if (value == null) {
165 value = System.getenv(environmentVar);
166 }
167
168 if (value == null) {
169 value = "";
170 }
171 return value;
172 }
173
174 public Digester getDigester() throws ConfigurationManagerException {
175 URL rules = MasterRecordParser.class.getResource(MASTER_RECORD_RULES_XML);
176 if (rules == null) {
177 throw new ConfigurationManagerException("Major error: could not find " + MASTER_RECORD_RULES_XML + " file with rules to parse master record. Please get a configuration manager distribution.");
178 }
179 log.debug("about to load rules to read master record");
180 Digester masterRecordDigester = DigesterLoader.createDigester(rules);
181 return masterRecordDigester;
182 }
183
184 }