001 package net.sf.logdistiller.xml; 002 003 /* 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 import java.io.*; 018 import java.util.*; 019 import javax.xml.parsers.DocumentBuilder; 020 import javax.xml.parsers.DocumentBuilderFactory; 021 import javax.xml.parsers.ParserConfigurationException; 022 import org.w3c.dom.Document; 023 import org.w3c.dom.Element; 024 import org.xml.sax.InputSource; 025 import org.xml.sax.SAXException; 026 027 import net.sf.logdistiller.*; 028 import net.sf.logdistiller.util.PropertiesReplacer; 029 import org.xml.sax.ErrorHandler; 030 031 /** 032 * This class parses an XML rules configuration file and creates corresponding LogDistiller object. 033 * 034 * @see net.sf.logdistiller.LogDistiller 035 */ 036 public class DOMConfigurator 037 { 038 private final Map overriddenProperties; 039 040 private PropertiesReplacer replacer = null; 041 042 private boolean workInProgress = false; 043 044 private boolean old; 045 046 private List warnings = null; 047 048 private LogDistiller ld = null; 049 050 public DOMConfigurator() 051 { 052 overriddenProperties = null; 053 } 054 055 public DOMConfigurator( Map overriddenProperties ) 056 { 057 this.overriddenProperties = overriddenProperties; 058 } 059 060 synchronized public LogDistiller read( File f ) 061 throws ParserConfigurationException, IOException, SAXException 062 { 063 return read( f, null ); 064 } 065 066 synchronized public LogDistiller read( File f, ErrorHandler errorHandler ) 067 throws ParserConfigurationException, IOException, SAXException 068 { 069 return read( new FileInputStream( f ), errorHandler ); 070 } 071 072 synchronized public LogDistiller read( InputStream in ) 073 throws ParserConfigurationException, IOException, SAXException 074 { 075 return read( in, null ); 076 } 077 078 synchronized public LogDistiller read( InputStream in, ErrorHandler errorHandler ) 079 throws ParserConfigurationException, IOException, SAXException 080 { 081 return read( new InputSource( in ), errorHandler ); 082 } 083 084 synchronized public LogDistiller read( InputSource inputSource ) 085 throws ParserConfigurationException, IOException, SAXException 086 { 087 return read( inputSource, null ); 088 } 089 090 /** 091 * begin configuration, if not currently in progress 092 * 093 * @return boolean true if really begin, false if configuration is already in progress 094 */ 095 protected boolean begin() 096 { 097 if ( workInProgress ) 098 { 099 // parse() method is called by read() 100 return false; 101 } 102 workInProgress = true; 103 warnings = new ArrayList(); 104 old = false; 105 return true; 106 } 107 108 protected void end( boolean really ) 109 { 110 if ( !really ) 111 { 112 return; 113 } 114 workInProgress = false; 115 if ( ld != null ) 116 { 117 Iterator iter = warnings.iterator(); 118 while ( iter.hasNext() ) 119 { 120 ld.addWarning( (String) iter.next() ); 121 } 122 } 123 warnings = null; 124 ld = null; 125 } 126 127 protected void addWarning( String warning ) 128 { 129 warnings.add( warning ); 130 } 131 132 synchronized public LogDistiller read( InputSource inputSource, ErrorHandler errorHandler ) 133 throws ParserConfigurationException, IOException, SAXException 134 { 135 boolean beginning = begin(); 136 try 137 { 138 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 139 dbf.setValidating( true ); 140 DocumentBuilder docBuilder = dbf.newDocumentBuilder(); 141 docBuilder.setErrorHandler( ( errorHandler == null ) ? new SAXErrorHandler() : errorHandler ); 142 LogDistillerEntityResolver resolver = new LogDistillerEntityResolver(); 143 docBuilder.setEntityResolver( resolver ); 144 Document doc = docBuilder.parse( inputSource ); 145 146 if ( resolver.getCurrentDtd() == null ) 147 { 148 addWarning( "no DTD used: you should use " + LogDistillerEntityResolver.LATEST_DTD_URL ); 149 } 150 else if ( !LogDistillerEntityResolver.LATEST_DTD.equals( resolver.getCurrentDtd() ) ) 151 { 152 addWarning( "using old DTD " + resolver.getCurrentDtd() + ": you should consider upgrading to " 153 + LogDistillerEntityResolver.LATEST_DTD + " (please read upgrade documentation)" ); 154 } 155 156 ld = parse( doc.getDocumentElement() ); 157 return ld; 158 } 159 finally 160 { 161 end( beginning ); 162 } 163 } 164 165 synchronized public LogDistiller parse( Element element ) 166 { 167 boolean beginning = begin(); 168 try 169 { 170 if ( !"logdistiller".equals( element.getNodeName() ) ) 171 { 172 throw new IllegalArgumentException( "document root element is '" + element.getNodeName() 173 + "' where it should be 'logdistiller': see " + LogDistillerEntityResolver.LATEST_DTD_URL ); 174 } 175 176 String id = element.getAttribute( "id" ); 177 Map properties = parseProperties( DOMUtils.getChildElementsByTagName( element, "property" ) ); 178 replacer = new PropertiesReplacer( properties ); 179 String description = replacer.replaceProperties( DOMUtils.getPCDATAByTagName( element, "description" ) ); 180 LogDistiller.LogType logtype = null; 181 LogDistiller.Output output = null; 182 183 Iterator iter = DOMUtils.getChildElementsByTagName( element, "logtype" ); 184 if ( iter.hasNext() ) 185 { // logtype element appeared in logdistiller-1_3.dtd only 186 logtype = parseLogType( (Element) iter.next() ); 187 output = parseOutput( (Element) DOMUtils.getChildElementsByTagName( element, "output" ).next() ); 188 } 189 else 190 { 191 old = true; 192 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 193 String logsUrl = (String) params.get( "logs.url" ); 194 output = 195 new LogDistiller.Output( "logs", logsUrl, "default", "", params, buildCompatibilityReports( params ) ); 196 } 197 198 LogDistiller.Category[] categories = 199 parseCategories( DOMUtils.getChildElementsByTagName( element, "category" ) ); 200 LogDistiller.Group[] groups = 201 parseGroups( DOMUtils.getChildElementsByTagName( element, "group" ), categories ); 202 return new LogDistiller( id, description, logtype, output, categories, groups, old ); 203 } 204 finally 205 { 206 end( beginning ); 207 } 208 } 209 210 protected Map parseProperties( Iterator iter ) 211 { 212 Map map = new HashMap(); 213 while ( iter.hasNext() ) 214 { 215 Element param = (Element) iter.next(); 216 String name = param.getAttribute( "name" ); 217 String value = param.getAttribute( "value" ); 218 if ( map.put( name, value ) != null ) 219 { 220 addWarning( "multiple values were defined for '" + name 221 + "' property: the last one will override other values." ); 222 } 223 } 224 if ( overriddenProperties != null ) 225 { 226 // override properties set by this configurator 227 Iterator iter2 = overriddenProperties.keySet().iterator(); 228 while ( iter2.hasNext() ) 229 { 230 String name = (String) iter2.next(); 231 if ( map.put( name, overriddenProperties.get( name ) ) == null ) 232 { 233 throw new IllegalArgumentException( "unable to override property '" + name + "': unknown property" ); 234 } 235 } 236 } 237 return map; 238 } 239 240 protected LogDistiller.LogType parseLogType( Element element ) 241 { 242 String id = element.getAttribute( "id" ); 243 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 244 String paramAttributes = (String)params.get( "attributes" ); 245 246 Attributes attributes = null; 247 Iterator iter = DOMUtils.getChildElementsByTagName( element, "attributes" ); 248 if ( iter.hasNext() ) 249 { // attributes element appeared in logdistiller-1_4.dtd only 250 attributes = parseAttributes( paramAttributes, (Element) iter.next() ); 251 } 252 else 253 { 254 attributes = new Attributes( paramAttributes, Attributes.NO_EXTENSIONS ); 255 addWarning( "no logtype/attributes element defined: please consider adding this element (new in LogDistiller 1.1)." ); 256 } 257 return new LogDistiller.LogType( id, params, attributes ); 258 } 259 260 protected Attributes parseAttributes( String paramAttributes, Element element ) 261 { 262 String provided = DOMUtils.getPCDATAByTagName( element, "provided" ); 263 if ( provided == null ) 264 { 265 addWarning( "logtype 'attributes' param has been replaced by <provided> element: please consider using this element (new in LogDistiller 1.1)" ); 266 provided = paramAttributes; 267 } 268 Attributes.Extension[] extensions = parseExtensions( DOMUtils.getChildElementsByTagName( element, "extension" ) ); 269 return new Attributes( provided, extensions ); 270 } 271 272 protected Attributes.Extension[] parseExtensions( Iterator iter ) 273 { 274 List extensions = new ArrayList(); 275 while ( iter.hasNext() ) 276 { 277 Element extension = (Element) iter.next(); 278 extensions.add( parseExtension( extension ) ); 279 } 280 return (Attributes.Extension[]) extensions.toArray( new Attributes.Extension[extensions.size()] ); 281 } 282 283 protected Attributes.Extension parseExtension( Element element ) 284 { 285 String source = element.getAttribute( "source" ); 286 String provides = element.getAttribute( "provides" ); 287 String regexp = DOMUtils.getPCDATA( element ); 288 return new Attributes.Extension( source, provides, regexp ); 289 } 290 291 protected LogDistiller.Output parseOutput( Element element ) 292 { 293 String directory = replacer.replaceProperties( element.getAttribute( "directory" ) ); 294 String url = replacer.replaceProperties( element.getAttribute( "url" ) ); 295 String content = replacer.replaceProperties( element.getAttribute( "content" ) ); 296 String skip = replacer.replaceProperties( element.getAttribute( "skip" ) ); 297 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 298 299 String paramLogsUrl = (String) params.get( "logs.url" ); 300 if ( paramLogsUrl != null ) 301 { 302 if ( url == null ) 303 { 304 url = paramLogsUrl; 305 addWarning( "in output element, param logs.url is now deprecated: please use url attribute (<output url=\"...\">)" ); 306 } 307 else 308 { 309 addWarning( "in output element, attribute url and deprecated param logs.url have both been set: ignoring param" ); 310 } 311 } 312 313 LogDistiller.Report[] reports = parseReports( DOMUtils.getChildElementsByTagName( element, "report" ) ); 314 315 return new LogDistiller.Output( directory, url, content, skip, params, reports ); 316 } 317 318 protected LogDistiller.Report[] parseReports( Iterator iter ) 319 { 320 List reports = new ArrayList(); 321 while ( iter.hasNext() ) 322 { 323 Element report = (Element) iter.next(); 324 reports.add( parseReport( report ) ); 325 } 326 return (LogDistiller.Report[]) reports.toArray( new LogDistiller.Report[reports.size()] ); 327 } 328 329 protected LogDistiller.Report parseReport( Element element ) 330 { 331 String publisher = replacer.replaceProperties( element.getAttribute( "publisher" ) ); 332 String format = replacer.replaceProperties( element.getAttribute( "format" ) ); 333 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 334 return new LogDistiller.Report( publisher, format, params ); 335 } 336 337 protected LogDistiller.Report[] buildCompatibilityReports( Map params ) 338 { 339 LogDistiller.Report fileReport = new LogDistiller.Report( "file", "txt", new HashMap() ); 340 String to = (String) params.get( "mail.to" ); 341 if ( to == null ) 342 { 343 return new LogDistiller.Report[] { fileReport }; 344 } 345 Map newParams = new HashMap(); 346 newParams.put( "to", replacer.replaceProperties( to ) ); 347 String cc = (String) params.get( "mail.cc" ); 348 if ( cc != null ) 349 { 350 newParams.put( "cc", replacer.replaceProperties( cc ) ); 351 } 352 LogDistiller.Report mailReport = new LogDistiller.Report( "mail", "txt", newParams ); 353 return new LogDistiller.Report[] { fileReport, mailReport }; 354 } 355 356 protected Map parseParams( Iterator iter ) 357 { 358 Map params = new HashMap(); 359 while ( iter.hasNext() ) 360 { 361 Element param = (Element) iter.next(); 362 String name = param.getAttribute( "name" ); 363 String value = replacer.replaceProperties( DOMUtils.getPCDATA( param ) ); 364 if ( params.put( name, value ) != null ) 365 { 366 addWarning( "multiple values were defined for '" + name 367 + "' parameter: the last one will override other values." ); 368 } 369 } 370 return params; 371 } 372 373 protected LogDistiller.Category[] parseCategories( Iterator iter ) 374 { 375 List categories = new ArrayList(); 376 while ( iter.hasNext() ) 377 { 378 Element category = (Element) iter.next(); 379 String id = category.getAttribute( "id" ); 380 String description = DOMUtils.getPCDATA( category ); 381 categories.add( new LogDistiller.Category( id, description ) ); 382 } 383 return (LogDistiller.Category[]) categories.toArray( new LogDistiller.Category[categories.size()] ); 384 } 385 386 protected LogDistiller.Group[] parseGroups( Iterator iter, LogDistiller.Category[] categories ) 387 { 388 Map mapCategories = new HashMap(); 389 for ( int i = 0; i < categories.length; i++ ) 390 { 391 LogDistiller.Category category = categories[i]; 392 mapCategories.put( category.getId(), category ); 393 } 394 List groups = new ArrayList(); 395 while ( iter.hasNext() ) 396 { 397 Element group = (Element) iter.next(); 398 groups.add( parseGroup( group, mapCategories ) ); 399 } 400 return (LogDistiller.Group[]) groups.toArray( new LogDistiller.Group[groups.size()] ); 401 } 402 403 protected LogDistiller.Group parseGroup( Element element, Map categories ) 404 { 405 String id = element.getAttribute( "id" ); 406 String description = replacer.replaceProperties( DOMUtils.getPCDATAByTagName( element, "description" ) ); 407 String attr = replacer.replaceProperties( element.getAttribute( "continueProcessing" ) ); 408 boolean continueProcessing = Boolean.valueOf( ( attr == null ) ? "false" : attr ).booleanValue(); 409 attr = element.getAttribute( "save" ); 410 if ( attr == null ) 411 { 412 attr = "true"; 413 } 414 boolean save = !"false".equals( attr ); 415 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 416 Condition[] conditions = parseConditions( DOMUtils.getChildElementsByTagName( element, "condition" ) ); 417 LogDistiller.Report[] reports = parseReports( DOMUtils.getChildElementsByTagName( element, "report" ) ); 418 LogDistiller.Plugin[] plugins = parsePlugins( DOMUtils.getChildElementsByTagName( element, "plugin" ) ); 419 if ( old ) 420 { 421 reports = buildCompatibilityReports( params ); 422 } 423 LogDistiller.Category category = (LogDistiller.Category) categories.get( element.getAttribute( "category" ) ); 424 return new LogDistiller.Group( id, description, continueProcessing, save, params, conditions, reports, plugins, 425 category ); 426 } 427 428 protected Condition[] parseConditions( Iterator iter ) 429 { 430 List conditions = new ArrayList(); 431 while ( iter.hasNext() ) 432 { 433 Element condition = (Element) iter.next(); 434 conditions.add( parseCondition( condition ) ); 435 } 436 return (Condition[]) conditions.toArray( new Condition[conditions.size()] ); 437 } 438 439 protected Condition parseCondition( Element element ) 440 { 441 String tags = element.getAttribute( "tags" ); 442 Match[] matches = parseMatchs( DOMUtils.getChildElementsByTagName( element, "match" ) ); 443 return new Condition( tags, matches ); 444 } 445 446 protected Match[] parseMatchs( Iterator iter ) 447 { 448 List matchs = new ArrayList(); 449 while ( iter.hasNext() ) 450 { 451 Element match = (Element) iter.next(); 452 matchs.add( parseMatch( match ) ); 453 } 454 return (Match[]) matchs.toArray( new Match[matchs.size()] ); 455 } 456 457 protected Match parseMatch( Element element ) 458 { 459 String attribute = element.getAttribute( "attribute" ); 460 String type = element.getAttribute( "type" ); 461 String reference = replacer.replaceProperties( DOMUtils.getPCDATA( element ) ); 462 return new Match( attribute, type, reference ); 463 } 464 465 protected LogDistiller.Plugin[] parsePlugins( Iterator iter ) 466 { 467 List plugins = new ArrayList(); 468 while ( iter.hasNext() ) 469 { 470 Element plugin = (Element) iter.next(); 471 plugins.add( parsePlugin( plugin ) ); 472 } 473 return (LogDistiller.Plugin[]) plugins.toArray( new LogDistiller.Plugin[plugins.size()] ); 474 } 475 476 protected LogDistiller.Plugin parsePlugin( Element element ) 477 { 478 String type = element.getAttribute( "type" ); 479 if ( Plugins.getPlugin( type ) == null ) 480 { 481 throw new PluginConfigException( "plugin type '" + type + "' unknown, valid values: " 482 + Plugins.listAllPluginIds() ); 483 } 484 String attr = element.getAttribute( "globalReport" ); 485 boolean globalReport = Boolean.valueOf( ( attr == null ) ? "true" : attr ).booleanValue(); 486 attr = element.getAttribute( "groupReport" ); 487 boolean groupReport = Boolean.valueOf( ( attr == null ) ? "true" : attr ).booleanValue(); 488 Map params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ) ); 489 return new LogDistiller.Plugin( type, globalReport, groupReport, params ); 490 } 491 }