View Javadoc

1   package net.sf.logdistiller.xml;
2   
3   /*
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  import java.io.*;
18  import java.util.*;
19  import javax.xml.parsers.DocumentBuilder;
20  import javax.xml.parsers.DocumentBuilderFactory;
21  import javax.xml.parsers.ParserConfigurationException;
22  import org.w3c.dom.Document;
23  import org.w3c.dom.Element;
24  import org.xml.sax.InputSource;
25  import org.xml.sax.SAXException;
26  
27  import net.sf.logdistiller.*;
28  import net.sf.logdistiller.util.PropertiesReplacer;
29  import org.xml.sax.ErrorHandler;
30  
31  /**
32   * This class parses an XML rules configuration file and creates corresponding LogDistiller object.
33   *
34   * @see net.sf.logdistiller.LogDistiller
35   */
36  public class DOMConfigurator
37  {
38      private final Map overriddenProperties;
39  
40      private PropertiesReplacer replacer = null;
41  
42      private boolean workInProgress = false;
43  
44      private boolean old;
45  
46      private List warnings = null;
47  
48      private LogDistiller ld = null;
49  
50      public DOMConfigurator()
51      {
52          overriddenProperties = null;
53      }
54  
55      public DOMConfigurator( Map overriddenProperties )
56      {
57          this.overriddenProperties = overriddenProperties;
58      }
59  
60      synchronized public LogDistiller read( File f )
61          throws ParserConfigurationException, IOException, SAXException
62      {
63          return read( f, null );
64      }
65  
66      synchronized public LogDistiller read( File f, ErrorHandler errorHandler )
67          throws ParserConfigurationException, IOException, SAXException
68      {
69          return read( new FileInputStream( f ), errorHandler );
70      }
71  
72      synchronized public LogDistiller read( InputStream in )
73          throws ParserConfigurationException, IOException, SAXException
74      {
75          return read( in, null );
76      }
77  
78      synchronized public LogDistiller read( InputStream in, ErrorHandler errorHandler )
79          throws ParserConfigurationException, IOException, SAXException
80      {
81          return read( new InputSource( in ), errorHandler );
82      }
83  
84      synchronized public LogDistiller read( InputSource inputSource )
85          throws ParserConfigurationException, IOException, SAXException
86      {
87          return read( inputSource, null );
88      }
89  
90      /**
91       * begin configuration, if not currently in progress
92       *
93       * @return boolean true if really begin, false if configuration is already in progress
94       */
95      protected boolean begin()
96      {
97          if ( workInProgress )
98          {
99              // 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 }