001package net.sf.logdistiller.ant;
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
017import java.io.*;
018import java.text.*;
019import java.util.*;
020import java.util.zip.*;
021import javax.xml.parsers.*;
022
023import org.apache.tools.ant.*;
024import org.apache.tools.ant.types.*;
025import org.xml.sax.*;
026
027import net.sf.logdistiller.*;
028import net.sf.logdistiller.util.*;
029import net.sf.logdistiller.xml.*;
030
031/**
032 * Ant task to configure and launch a log distillation.
033 *
034 * <pre>
035 * &lt;logdistiller conf=&quot;&lt;i&gt;[xml configuration file]&lt;/i&gt;&quot; publish=&quot;&lt;i&gt;[all|file,mail,...]&lt;/i&gt;&quot; compressed=&quot;&lt;i&gt;[no|gzip|zip]&lt;/i&gt;&quot;&gt;
036 *  [&lt;&lt;a href=&quot;AntProperty.html&quot;&gt;property&lt;/a&gt; .../&gt;]*
037 *  &lt;fileset ...&gt;
038 * lt;/logdistiller&gt;
039 * </pre>
040 */
041public class LogDistillerTask
042    extends Task
043{
044    private File conf;
045
046    private boolean mail = true;
047
048    private boolean mailConfigured = false;
049
050    private List<String> publish;
051
052    private boolean publishConfigured = false;
053
054    private Map<String, String> properties = new HashMap<String, String>();
055
056    private AntLogEvent logevent;
057
058    private AntLogDistillation logdistillation;
059
060    private List<FileSet> filesets = new ArrayList<FileSet>();
061
062    /** @since 0.8 */
063    private String compressed = "no";
064
065    public LogDistillerTask()
066    {
067    }
068
069    public void setConf( File conf )
070    {
071        this.conf = conf;
072    }
073
074    public void setMail( boolean mail )
075    {
076        this.mail = mail;
077        mailConfigured = true;
078    }
079
080    public void setPublish( String publish )
081    {
082        publishConfigured = true;
083        publish = publish.trim();
084        if ( publish.equals( "all" ) )
085        {
086            this.publish = null;
087        }
088        else
089        {
090            this.publish = new ArrayList<String>();
091            while ( publish.length() > 0 )
092            {
093                int index = publish.indexOf( ',' );
094                if ( index > -1 )
095                {
096                    this.publish.add( publish.substring( 0, index ).trim() );
097                    publish = publish.substring( index + 1 );
098                }
099                else
100                {
101                    this.publish.add( publish );
102                    publish = "";
103                }
104            }
105        }
106    }
107
108    public void addConfiguredProperty( AntProperty property )
109    {
110        properties.put( property.getName(), property.getValue() );
111    }
112
113    public void setCompressed( Compressed attr )
114    {
115        this.compressed = attr.getValue();
116    }
117
118    /**
119     * @deprecated since LogDistiller 0.7, replaced by logtype element in rules configuration
120     */
121    public AntLogEvent createLogevent()
122    {
123        logevent = new AntLogEvent( getProject() );
124        return logevent;
125    }
126
127    /**
128     * @deprecated since LogDistiller 0.7, replaced by output element in rules configuration
129     */
130    public AntLogDistillation createLogdistillation()
131    {
132        logdistillation = new AntLogDistillation();
133        return logdistillation;
134    }
135
136    public void addFileSet( FileSet fs )
137    {
138        filesets.add( fs );
139    }
140
141    public void execute()
142        throws BuildException
143    {
144        log( "LogDistiller " + LogDistiller.getVersion() );
145        LogDistiller ld = readLogDistiller();
146        compatibilityUpdate( ld );
147
148        if ( LogTypes.getLogType( ld.getLogType().getId() ) == null )
149        {
150            throw new BuildException( "Could not find logtype '" + ld.getLogType().getId() + "': available values are "
151                + LogTypes.listAllLogTypeIds() );
152        }
153
154        LogDistillation distillation = new LogDistillation( ld );
155
156        LogEvent.Factory factory = initLogEventFactory( distillation.getLogTypeDescription() );
157
158        log( "content: " + ld.getOutput().getContent() + ", output directory: " + ld.getOutput().getDirectory() );
159
160        String step = null;
161        try
162        {
163            step = "beginning logdistillation";
164            distillation.begin();
165
166            step = "reading a log event";
167            int count = 0;
168            LogEvent le;
169            while ( ( le = factory.nextEvent() ) != null )
170            {
171                count++;
172                distillation.processLogEvent( le );
173            }
174
175            step = "ending logdistillation";
176            distillation.end();
177            log( "logevents distilled: " + count + ", time elapsed: "
178                + FormatUtil.formatPeriod( distillation.getEndTime() - distillation.getBeginTime() ) );
179
180            step = "publishing logdistillation reports";
181            int reports = new PublishHelper( distillation, publish ).publish();
182            log( "reports published: " + reports );
183        }
184        catch ( IOException ioe )
185        {
186            throw new BuildException( "I/O error when " + step + ": " + ioe.getMessage(), ioe );
187        }
188        catch ( Publisher.PublishException ppe )
189        {
190            throw new BuildException( "Error when " + step + ": " + ppe.getMessage(), ppe );
191        }
192        catch ( ParseException pe )
193        {
194            throw new BuildException( "Parsing error when " + step + ": " + pe.getMessage(), pe );
195        }
196    }
197
198    /**
199     * Make all that's possible to update latest LogDistiller model to maintain compatibility with older versions, since
200     * some elements have moved from Ant task to LogDistiller model.
201     *
202     * @param ld LogDistiller
203     */
204    private void compatibilityUpdate( LogDistiller ld )
205    {
206        if ( logevent != null )
207        {
208            if ( ld.isOld() )
209            {
210                String warning =
211                    "element 'logevent' is deprecated in Ant logdistiller task, please use element 'logtype' "
212                        + "from " + LogDistillerEntityResolver.LATEST_DTD + " in your rules file";
213                log( "WARNING: " + warning, Project.MSG_WARN );
214                ld.addWarning( warning );
215                ld.buildCompatibilityLogType( logevent.getLogtype() );
216            }
217            else
218            {
219                String warning =
220                    "element 'logevent' is deprecated in Ant logdistiller task now that you use element "
221                        + "'logtype' from " + LogDistillerEntityResolver.LATEST_DTD
222                        + " in your rules file: ignored (please remove)";
223                log( "WARNING : " + warning, Project.MSG_WARN );
224                ld.addWarning( warning );
225            }
226        }
227
228        if ( logdistillation != null )
229        {
230            if ( ld.isOld() )
231            {
232                String warning =
233                    "element 'logdistillation' is deprecated in Ant logdistiller task, please use element "
234                        + "'output' from " + LogDistillerEntityResolver.LATEST_DTD + " in your rules file";
235                log( "WARNING: " + warning, Project.MSG_WARN );
236                ld.addWarning( warning );
237                String skipgroups = logdistillation.getSkipgroups();
238                int pos = skipgroups.indexOf( ',' );
239                if ( pos >= 0 )
240                {
241                    skipgroups = skipgroups.substring( 0, pos ).trim();
242                    warning =
243                        "attribute skipgroups in logdistillation element cannot refer any more to multiple groups: "
244                            + "please create a category. Only group '" + skipgroups + "' has been kept.";
245                }
246                ld.getOutput().updateCompatibility( logdistillation.getDestdir(), logdistillation.getContent(),
247                                                    skipgroups );
248            }
249            else
250            {
251                String warning =
252                    "element 'logdistillation' is deprecated in Ant logdistiller task now that you use element "
253                        + "'output' from " + LogDistillerEntityResolver.LATEST_DTD
254                        + " in your rules file: ignored (please remove)";
255                log( "WARNING: " + warning, Project.MSG_WARN );
256                ld.addWarning( warning );
257            }
258        }
259
260        if ( mailConfigured )
261        {
262            if ( !publishConfigured )
263            {
264                setPublish( mail ? "file,mail" : "file" );
265            }
266            else
267            {
268                String warning =
269                    "attribute 'mail' is deprecated in Ant logdistiller task now that you use attribute "
270                        + "'publish': attribute ignored (please remove)";
271                log( "WARNING: " + warning, Project.MSG_WARN );
272                ld.addWarning( warning );
273            }
274        }
275    }
276
277    private LogDistiller readLogDistiller()
278    {
279        DOMConfigurator domConfigurator = new DOMConfigurator( properties );
280        try
281        {
282            log( "reading configuration " + conf );
283            return domConfigurator.read( conf );
284        }
285        catch ( IllegalArgumentException iae )
286        {
287            throw new BuildException( "error in configuration: " + iae.getMessage(), iae );
288        }
289        catch ( SAXException saxe )
290        {
291            throw new BuildException( "parsing error in logdistiller XML configuration file " + conf + ": "
292                + saxe.getMessage(), saxe );
293        }
294        catch ( IOException ioe )
295        {
296            throw new BuildException( "error while reading file " + conf + ": " + ioe.getMessage(), ioe );
297        }
298        catch ( ParserConfigurationException pce )
299        {
300            throw new BuildException( "error with XML parser configuration:" + pce.getMessage(), pce );
301        }
302    }
303
304    private FactoryMultiplexer initLogEventFactory( LogType.Description description )
305    {
306        // construct a log event multiplexer with each file added in the fileset
307        FactoryMultiplexer multiplexer = new FactoryMultiplexer( description );
308        try
309        {
310            int count = 0;
311            for ( FileSet fs : filesets )
312            {
313                DirectoryScanner ds = fs.getDirectoryScanner( getProject() );
314                String[] srcFiles = ds.getIncludedFiles();
315                int len = srcFiles.length;
316                for ( int i = 0; i < len; i++ )
317                {
318                    count += addFile( new File( ds.getBasedir(), srcFiles[i] ), description, multiplexer );
319                }
320            }
321            log( "parsing " + count + " log files" );
322        }
323        catch ( IOException ioe )
324        {
325            throw new BuildException( "error while opening log file: " + ioe.getMessage(), ioe );
326        }
327        return multiplexer;
328    }
329
330    private int addFile( File file, LogType.Description description, FactoryMultiplexer multiplexer )
331        throws IOException
332    {
333        int count = 0;
334        log( "parsing " + file.getPath() );
335        if ( "no".equals( compressed ) )
336        {
337            Reader reader = new InputStreamReader( new FileInputStream( file ) );
338            String logSource = file.toURL().toString();
339            multiplexer.addFactory( description.newFactory( reader, logSource ) );
340            count++;
341        }
342        else if ( "gzip".equals( compressed ) )
343        {
344            Reader reader = new InputStreamReader( new GZIPInputStream( new FileInputStream( file ) ) );
345            String logSource = "gzip:" + file.toURL().toString();
346            multiplexer.addFactory( description.newFactory( reader, logSource ) );
347            count++;
348        }
349        else if ( "compress".equals( compressed ) )
350        {
351            Reader reader = new InputStreamReader( new UncompressInputStream( new FileInputStream( file ) ) );
352            String logSource = "compress:" + file.toURL().toString();
353            multiplexer.addFactory( description.newFactory( reader, logSource ) );
354            count++;
355        }
356        else
357        { // zip
358            ZipFile zip = new ZipFile( file );
359            Enumeration<? extends ZipEntry> iter = zip.entries();
360            while ( iter.hasMoreElements() )
361            {
362                ZipEntry entry = iter.nextElement();
363                if ( !entry.isDirectory() )
364                {
365                    log( "   " + entry.getName() );
366                    Reader reader = new InputStreamReader( zip.getInputStream( entry ) );
367                    String logSource = "zip:" + file.toURL() + "!" + entry.getName();
368                    multiplexer.addFactory( description.newFactory( reader, logSource ) );
369                    count++;
370                }
371            }
372        }
373        return count;
374    }
375
376    public static class Compressed
377        extends EnumeratedAttribute
378    {
379        public String[] getValues()
380        {
381            return new String[] { "no", "gzip", "zip", "compress" };
382        }
383    }
384}