001/* 002 * ConfigurationFileLoader.java 003 * 004 * Created on 28. April 2002, 14:45 005 */ 006 007package org.jconfig; 008 009import java.io.*; 010import java.util.HashMap; 011import java.util.Hashtable; 012import java.util.Vector; 013import java.util.Iterator; 014import java.util.Enumeration; 015import java.util.Properties; 016import javax.xml.parsers.*; 017import org.xml.sax.*; 018import org.xml.sax.helpers.*; 019import org.w3c.dom.*; 020/** 021 * 022 * @author Andreas Mecky andreas.mecky@xcom.de 023 * @author Terry Dye terry.dye@xcom.de 024 */ 025public class ConfigurationFileHandler implements ConfigurationHandler { 026 027 // separator for category and property for HashMap 028 private static final char C_SEP = 127; 029 // prefix for category names 030 private static final char C_PFIX = 64; 031 private boolean validate = false; 032 private HashMap initValues; 033 private HashMap props; 034 private Vector categories; 035 /** Creates a new instance of ConfigurationFileLoader */ 036 public ConfigurationFileHandler() { 037 } 038 039 /** 040 * @param parameters */ 041 public void initialize(HashMap parameters) { 042 initValues = parameters; 043 props = new HashMap(); 044 categories = new Vector(); 045 } 046 047 public void setValidation(boolean validate) { 048 this.validate = validate; 049 } 050 051 /** 052 * @throws ConfigurationManagerException */ 053 public synchronized void load() throws ConfigurationManagerException { 054 if ( initValues.get("File") != null ) { 055 load((File)initValues.get("File")); 056 } 057 else if ( initValues.get("InputStream") != null ) { 058 load((InputStream)initValues.get("InputStream")); 059 } 060 else if ( initValues.get("URL") != null ) { 061 load((String)initValues.get("URL")); 062 } 063 } 064 065 /** 066 * @param file 067 * @throws ConfigurationManagerException */ 068 public synchronized void load(File file) throws ConfigurationManagerException { 069 if ( file == null ) { 070 return; 071 } 072 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 073 // set the validation flag (true if it should be validated) 074 dbf.setValidating(validate); 075 DocumentBuilder db = null; 076 try { 077 db = dbf.newDocumentBuilder(); 078 } catch (ParserConfigurationException pce) { 079 throw new ConfigurationManagerException("The parser cannot create a new document builder: " + pce.getMessage()); 080 } 081 // if we have to validate then we need to define an error handler here 082 if (validate) { 083 try { 084 // Set an ErrorHandler before parsing 085 OutputStreamWriter errorWriter = 086 new OutputStreamWriter(System.err, "UTF-8"); 087 db.setErrorHandler( 088 new MyErrorHandler(new PrintWriter(errorWriter, true))); 089 } catch (Exception e) { 090 throw new ConfigurationManagerException("The parser cannot set up the error handler"); 091 } 092 } 093 Document doc = null; 094 try { 095 doc = db.parse(file); 096 } catch (SAXException se) { 097 throw new ConfigurationManagerException("The parser cannot parse the file: " + se.getMessage()); 098 } catch (IOException ioe) { 099 throw new ConfigurationManagerException("The parser cannot open the file: " + ioe.getMessage()); 100 } 101 processProperties(doc); 102 103 } 104 105 /** 106 * Gets the variables attribute of the ConfigurationManager object 107 * 108 *@param doc Description of the Parameter 109 *@return The variables value 110 */ 111 private Hashtable getVariables(Document doc) { 112 Hashtable vars = null; 113 NodeList nl = doc.getElementsByTagName("variables"); 114 for (int i = 0; i < nl.getLength(); i++) { 115 Node n = nl.item(i); 116 vars = new Hashtable(); 117 for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { 118 // get all vraibles 119 if (child.getNodeName().equals("variable")) { 120 NamedNodeMap myAtt = child.getAttributes(); 121 Node myNode = myAtt.getNamedItem("name"); 122 String name = myNode.getNodeValue(); 123 myNode = myAtt.getNamedItem("value"); 124 String value = myNode.getNodeValue(); 125 if (name != null && value != null) { 126 // we store the name as ${name} so we have 127 // it directly the way it is used 128 String rname = "${" + name + "}"; 129 vars.put(rname, value); 130 } 131 } 132 } 133 } 134 return vars; 135 } 136 137 138 /** 139 * Description of the Method 140 * 141 *@param vars Description of the Parameter 142 *@param line Description of the Parameter 143 *@return Description of the Return Value 144 */ 145 private String replaceVariables(Hashtable vars, String line) { 146 if (vars != null) { 147 Enumeration keys = vars.keys(); 148 int pos = 0; 149 // walk through all variables 150 while (keys.hasMoreElements()) { 151 String currentKey = (String) keys.nextElement(); 152 String value = (String) vars.get((String) currentKey); 153 pos = line.indexOf(currentKey); 154 // check if we have found a variable 155 if (pos != -1) { 156 // cut the line into 2 pieces and put in the 157 // value of the variable 158 String firstPart = line.substring(0, pos); 159 String secondPart = line.substring(pos + currentKey.length()); 160 line = firstPart + value + secondPart; 161 } 162 } 163 } 164 return line; 165 } 166 167 168 /** 169 * Description of the Method 170 * 171 *@param doc Description of the Parameter 172 */ 173 private void processProperties(Document doc) { 174 String currentCategory; 175 Hashtable myvars = getVariables(doc); 176 // first we get all nodes where the element is category 177 NodeList nl = doc.getElementsByTagName("category"); 178 for (int i = 0; i < nl.getLength(); i++) { 179 // now we get every node from the list 180 Node n = nl.item(i); 181 // and get the name attribute for this category 182 NamedNodeMap curAtt = n.getAttributes(); 183 Node curNode = curAtt.getNamedItem("name"); 184 currentCategory = curNode.getNodeValue(); 185 categories.add(currentCategory); 186 // now we process all children for this category 187 for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { 188 if (child.getNodeName().equals("property")) { 189 // we have found a property element and now we grab the name and value 190 // attributes 191 NamedNodeMap myAtt = child.getAttributes(); 192 Node myNode = myAtt.getNamedItem("name"); 193 String name = myNode.getNodeValue(); 194 myNode = myAtt.getNamedItem("value"); 195 String value = myNode.getNodeValue(); 196 // if we have both then lets store it 197 if (name != null && value != null) { 198 // the key is always category/name 199 // e.g. general/congig_file 200 props.put(C_PFIX+currentCategory+C_SEP+name,replaceVariables(myvars, value)); 201 } 202 } 203 } 204 } 205 } 206 207 /** 208 * This method will read in a file and generate the properties 209 * 210 *@param is The inputstream 211 *@throws ConfigurationManagerException if the file cannot be processed 212 */ 213 public synchronized void load(InputStream is) throws ConfigurationManagerException { 214 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 215 // set the validation flag (true if it should be validated) 216 dbf.setValidating(validate); 217 DocumentBuilder db = null; 218 try { 219 db = dbf.newDocumentBuilder(); 220 } catch (ParserConfigurationException pce) { 221 throw new ConfigurationManagerException("The parser cannot create a new document builder: " + pce.getMessage()); 222 } 223 // if we have to validate then we need to define an error handler here 224 if (validate) { 225 try { 226 // Set an ErrorHandler before parsing 227 OutputStreamWriter errorWriter = 228 new OutputStreamWriter(System.err, "UTF-8"); 229 db.setErrorHandler( 230 new MyErrorHandler(new PrintWriter(errorWriter, true))); 231 } catch (Exception e) { 232 throw new ConfigurationManagerException("The parser cannot set up the error handler"); 233 } 234 } 235 Document doc = null; 236 try { 237 doc = db.parse(is); 238 } catch (SAXException se) { 239 throw new ConfigurationManagerException("The parser cannot parse the file: " + se.getMessage()); 240 } catch (IOException ioe) { 241 throw new ConfigurationManagerException("The parser cannot open the file: " + ioe.getMessage()); 242 } 243 processProperties(doc); 244 245 } 246 247 248 /** 249 * This method will read the content from an URL and generate the 250 * properties. If you have the need to use a proxy server, create 251 * jconfig.properties file and place it inside your system path. 252 * <BR /> 253 * jconfig.properties example:<BR /> 254 * # jconfig.properties file<BR /> 255 * http.proxyHost=proxy.server.url<BR /> 256 * http.proxyPort=3128 257 * 258 *@param theURL Description of the Parameter 259 *@throws ConfigurationManagerException if the file cannot be processed 260 */ 261 public synchronized void load(String theURL) throws ConfigurationManagerException { 262 java.net.URL jcfURL = null; 263 InputStream is = null; 264 try { 265 // get a jconfig.properties in classpath, if it exists 266 ClassLoader cl = this.getClass().getClassLoader(); 267 InputStream jcf = cl.getResourceAsStream( "jconfig.properties" ); 268 // it is possible that the jconfig.properties does not exist, we get null 269 if ( jcf != null ) { 270 Properties jcfProperties = new Properties(); 271 jcfProperties.load( jcf ); 272 273 // load what is set in system 274 Properties prop = System.getProperties(); 275 // if we see http.proxyHost and/or http.proxyPort inside 276 // the jconfig.properties, we can set the System.properties 277 // for use by the URLConnection object 278 if ( jcfProperties.getProperty( "http.proxyHost" ) != null ) 279 prop.put( "http.proxyHost", jcfProperties.getProperty( "http.proxyHost" ) ); 280 if ( jcfProperties.getProperty( "http.proxyPort" ) != null ) 281 prop.put( "http.proxyPort", jcfProperties.getProperty( "http.proxyPort" ) ); 282 } 283 // prepare URL, open the connection and grab stream for parsing 284 jcfURL = new java.net.URL( theURL ); 285 java.net.URLConnection con = jcfURL.openConnection(); 286 is = con.getInputStream(); 287 } 288 catch ( Exception e ) { 289 throw new ConfigurationManagerException ( "Problem with URL handling/connection/validating: " 290 + e.getMessage() ); 291 } 292 293 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 294 dbf.setValidating(validate); 295 DocumentBuilder db = null; 296 try { 297 db = dbf.newDocumentBuilder(); 298 } catch (ParserConfigurationException pce) { 299 throw new ConfigurationManagerException("The parser cannot create a new document builder: " + pce.getMessage()); 300 } 301 // if we have to validate then we need to define an error handler here 302 if (validate) { 303 try { 304 // Set an ErrorHandler before parsing 305 OutputStreamWriter errorWriter = 306 new OutputStreamWriter(System.err, "UTF-8"); 307 db.setErrorHandler( 308 new MyErrorHandler(new PrintWriter(errorWriter, true))); 309 } catch (Exception e) { 310 throw new ConfigurationManagerException("The parser cannot set up the error handler"); 311 } 312 } 313 Document doc = null; 314 try { 315 doc = db.parse(is); 316 } catch (SAXException se) { 317 throw new ConfigurationManagerException("The parser cannot parse the XML: " + se.getMessage()); 318 } catch (IOException ioe) { 319 throw new ConfigurationManagerException("The parser cannot open the file: " + ioe.getMessage()); 320 } 321 processProperties(doc); 322 323 } 324 325 /** 326 * This method will save the current categories and their properties to a 327 * file 328 * 329 *@param file the file that will be generated 330 *@exception ConfigurationManagerException Description of the Exception 331 */ 332 public void store(File file,HashMap data) throws ConfigurationManagerException { 333 // this will store the properties for a certain category 334 Iterator it = data.keySet().iterator(); 335 try { 336 // let's open the file 337 FileWriter fw = new FileWriter(file); 338 // and write the header 339 fw.write("<?xml version=\"1.0\" ?>\n"); 340 fw.write("<properties>\n"); 341 // now we walk through all categories 342 while ( it.hasNext() ) { 343 String currentCategory = (String)it.next(); 344 fw.write(" <category name='" + currentCategory + "'>\n"); 345 // now we get all properties for this category 346 Hashtable props = (Hashtable)data.get(currentCategory); 347 Enumeration lprops = props.keys(); 348 // here we walk through the properties 349 while (lprops.hasMoreElements()) { 350 String currentKey = (String) lprops.nextElement(); 351 // and write it to the file 352 fw.write(" <property name='" + currentKey + "' value='" + (String) props.get(currentKey) + "'/>\n"); 353 } 354 fw.write(" </category>\n"); 355 } 356 fw.write("</properties>\n"); 357 // close the file because we are done 358 fw.close(); 359 } catch (Exception e) { 360 throw new ConfigurationManagerException("The file cannot be saved"); 361 } 362 } 363 364 /** 365 * This method should return a <code>Vector</code> of all 366 * categories. The category names must be stored as <code>Strings</code> 367 * inside the <code>Vector</code>. 368 * 369 * @return all categories inside a <code>Vector</code> 370 */ 371 public Vector getCategories() { 372 return categories; 373 } 374 375 /** 376 * This method should return all properties that was read 377 * by the particular handler.<BR /> 378 * The key for the properties are build like:<BR /> 379 * category/property name. 380 * 381 * @return a <code>HashMap</code> with all properties. 382 */ 383 public HashMap getProperties() { 384 return props; 385 } 386 387 /** 388 * This method should store all categories and properties. 389 */ 390 public void store() throws ConfigurationManagerException { 391 store((File)initValues.get("File"),(HashMap)initValues.get("Data")); 392 } 393 394 // 395 // This is an inner class 396 // 397 // Error handler to report errors and warnings 398 // 399 /** 400 * Description of the Class 401 * 402 *@author andreas 403 *@created 27. Januar 2002 404 */ 405 private static class MyErrorHandler implements ErrorHandler { 406 407 private PrintWriter out; 408 409 410 /** 411 * Constructor for the MyErrorHandler object 412 * 413 *@param out Description of the Parameter 414 */ 415 MyErrorHandler(PrintWriter out) { 416 this.out = out; 417 } 418 419 420 /** 421 * Returns a string describing parse exception details 422 * 423 *@param spe Description of the Parameter 424 *@return The parseExceptionInfo value 425 */ 426 private String getParseExceptionInfo(SAXParseException spe) { 427 String systemId = spe.getSystemId(); 428 if (systemId == null) { 429 systemId = "null"; 430 } 431 String info = "URI=" + systemId + 432 " Line=" + spe.getLineNumber() + 433 ": " + spe.getMessage(); 434 return info; 435 } 436 437 438 // The following methods are standard SAX ErrorHandler methods. 439 // See SAX documentation for more info. 440 441 /** 442 * Description of the Method 443 * 444 *@param spe Description of the Parameter 445 *@exception SAXException Description of the Exception 446 */ 447 public void warning(SAXParseException spe) throws SAXException { 448 out.println("Warning: " + getParseExceptionInfo(spe)); 449 } 450 451 452 /** 453 * Description of the Method 454 * 455 *@param spe Description of the Parameter 456 *@exception SAXException Description of the Exception 457 */ 458 public void error(SAXParseException spe) throws SAXException { 459 String message = "Error: " + getParseExceptionInfo(spe); 460 throw new SAXException(message); 461 } 462 463 464 /** 465 * Description of the Method 466 * 467 *@param spe Description of the Parameter 468 *@exception SAXException Description of the Exception 469 */ 470 public void fatalError(SAXParseException spe) throws SAXException { 471 String message = "Fatal Error: " + getParseExceptionInfo(spe); 472 throw new SAXException(message); 473 } 474 } 475 476}