View Javadoc
1   package net.sf.logdistiller.gui;
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.awt.Font;
18  import java.awt.LayoutManager;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.KeyEvent;
21  import java.io.*;
22  import java.lang.reflect.*;
23  import java.text.MessageFormat;
24  import java.text.ParseException;
25  import java.util.*;
26  import javax.swing.*;
27  import javax.xml.parsers.ParserConfigurationException;
28  
29  import org.xml.sax.ErrorHandler;
30  import org.xml.sax.SAXException;
31  import org.xml.sax.SAXParseException;
32  
33  import com.jgoodies.forms.builder.DefaultFormBuilder;
34  import com.jgoodies.forms.factories.ButtonBarFactory;
35  import com.jgoodies.forms.factories.DefaultComponentFactory;
36  import com.jgoodies.forms.layout.FormLayout;
37  import com.sun.syndication.io.XmlReader;
38  
39  import org.apache.commons.io.IOUtils;
40  
41  import net.sf.logdistiller.LogDistillation;
42  import net.sf.logdistiller.LogDistiller;
43  import net.sf.logdistiller.LogEvent;
44  import net.sf.logdistiller.LogType;
45  import net.sf.logdistiller.LogTypes;
46  import net.sf.logdistiller.reports.TextReport;
47  import net.sf.logdistiller.util.FormatUtil;
48  import net.sf.logdistiller.util.PropertiesReplacer;
49  import net.sf.logdistiller.xml.DOMConfigurator;
50  import net.sf.logdistiller.xml.LogDistillerEntityResolver;
51  
52  /**
53   * Main gui panel.
54   */
55  public class MainPanel
56      implements Runnable
57  {
58      private static final String TITLE = "LogDistiller " + LogDistiller.getVersion() + " interactive console";
59  
60      private static final String ABOUT = TITLE + "\n\nCopyright (C) 2004-2009 Hervé Boutemy. All rights reserved.";
61  
62      private final JFrame frame;
63  
64      public MainPanel( JFrame frame )
65      {
66          this.frame = frame;
67          frame.setTitle( TITLE );
68      }
69  
70      private Thread reloadThread;
71  
72      protected JPanel panel = new JPanel();
73  
74      private JTextField tfRules = new JTextField( "open or create a rules configuration file..." );
75  
76      private JTextField tfSourceFile = new JTextField();
77  
78      private JTextField tfSourceStats = new JTextField();
79  
80      private JTextField tfOutputDir = new JTextField();
81  
82      private JButton btRun = new JButton( "run" );
83  
84      private JButton btBatch = new JButton( "batch" );
85  
86      private JButton btSourceFile = new JButton( "..." );
87  
88      private JButton btOutputDir = new JButton( "..." );
89  
90      private JTextArea taResult = new JTextArea();
91  
92      private LongProgressBar progress = new LongProgressBar();
93  
94      private void initComponents()
95      {
96          tfSourceFile.setEditable( false );
97          btSourceFile.addActionListener( SwingAdapter.getActionListener( this, "btSourceFileActionPerformed" ) );
98          tfSourceStats.setEditable( false );
99          tfOutputDir.setEditable( false );
100         btOutputDir.addActionListener( SwingAdapter.getActionListener( this, "btOutputDirActionPerformed" ) );
101         tfRules.setEditable( false );
102         taResult.setEditable( false );
103         btRun.addActionListener( SwingAdapter.getActionListener( this, "btRunActionPerformed" ) );
104         btRun.setEnabled( false );
105         btBatch.addActionListener( SwingAdapter.getActionListener( this, "btBatchActionPerformed" ) );
106         btBatch.setEnabled( false );
107 
108         taResult.setFont( new Font( "Monospaced", Font.PLAIN, taResult.getFont().getSize() ) );
109 
110         progress.setStringPainted( true );
111         progress.setFont( new Font( "Monospaced", Font.PLAIN, progress.getFont().getSize() ) );
112     }
113 
114     private void addLabel( String label, String id )
115     {
116         panel.add( new JLabel( label ), id );
117     }
118 
119     private void addSeparator( String label, String id )
120     {
121         panel.add( DefaultComponentFactory.getInstance().createSeparator( label ), id );
122     }
123 
124     public final static SimpleLayoutConstraintsManager SIMPLE_LAYOUT_CONSTRAINTS_MANAGER =
125         SimpleLayoutConstraintsManager.getLayoutConstraintsManager( MainPanel.class.getResourceAsStream( "gui.xml" ) );
126 
127     public LayoutManager createLayout( String layoutId )
128     {
129         return MainPanel.SIMPLE_LAYOUT_CONSTRAINTS_MANAGER.getLayout( layoutId );
130     }
131 
132     public JComponent buildPanel()
133     {
134         initComponents();
135         buildMenuBar();
136 
137         panel.setBorder( com.jgoodies.forms.factories.Borders.DIALOG_BORDER );
138         panel.setLayout( createLayout( "mainPanel" ) );
139 
140         /*
141          * org.mlc.swing.layout.LayoutConstraintsManager layoutConstraintsManager =
142          * org.mlc.swing.layout.LayoutConstraintsManager
143          * .getLayoutConstraintsManager(MainPanel.class.getResourceAsStream("gui.xml"));
144          * panel.setLayout(layoutConstraintsManager.createLayout("mainPanel", panel)); new
145          * org.mlc.swing.layout.LayoutFrame(layoutConstraintsManager).setVisible(true);
146          */
147 
148         panel.add( tfRules, "tfRules" );
149 
150         addSeparator( "logs", "sep2" );
151         addLabel( "log file:", "lblSourceFile" );
152         panel.add( tfSourceFile, "tfSourceFile" );
153         panel.add( btSourceFile, "btSourceFile" );
154         panel.add( tfSourceStats, "tfSourceStats" );
155 
156         addLabel( "output directory:", "lblOutputDir" );
157         panel.add( tfOutputDir, "tfOutputDir" );
158         panel.add( btOutputDir, "btOutputDir" );
159 
160         JPanel bbar = ButtonBarFactory.buildRightAlignedBar( new JButton[] { btRun, btBatch } );
161         FormLayout layout = new FormLayout( "default:grow, 3dlu, pref" );
162         DefaultFormBuilder builder = new DefaultFormBuilder( layout );
163         builder.append( progress, bbar );
164         panel.add( builder.getPanel(), "pRun" );
165 
166         addSeparator( "result", "sep3" );
167         panel.add( new JScrollPane( taResult ), "taResult" );
168 
169         reloadThread = new Thread( this );
170         reloadThread.setDaemon( true );
171         reloadThread.setPriority( Thread.MIN_PRIORITY );
172         reloadThread.start();
173         return panel;
174     }
175 
176     private void buildMenuBar()
177     {
178         JMenuBar menuBar = new JMenuBar();
179         // FILE MENU
180         JMenu fileMenu = new JMenu( "File" );
181         fileMenu.setMnemonic( KeyEvent.VK_F );
182 
183         // Open file
184         JMenuItem fileOpenMenuItem = new JMenuItem( "Open rules file...", KeyEvent.VK_O );
185         fileOpenMenuItem.addActionListener( SwingAdapter.getActionListener( this, "btOpenActionPerformed" ) );
186         fileMenu.add( fileOpenMenuItem );
187 
188         // New file
189         JMenuItem fileNewMenuItem = new JMenuItem( "New rules file...", KeyEvent.VK_N );
190         fileNewMenuItem.addActionListener( SwingAdapter.getActionListener( this, "btNewActionPerformed" ) );
191         fileMenu.add( fileNewMenuItem );
192 
193         // exit
194         JMenuItem exitMenuItem = new JMenuItem( "Exit", KeyEvent.VK_X );
195         exitMenuItem.addActionListener( SwingAdapter.getActionListener( this, "exit" ) );
196         fileMenu.add( exitMenuItem );
197 
198         // HELP MENU
199         JMenu helpMenu = new JMenu( "Help" );
200         helpMenu.setMnemonic( KeyEvent.VK_H );
201 
202         // About
203         JMenuItem aboutMenuItem = new JMenuItem( "About...", KeyEvent.VK_A );
204         aboutMenuItem.addActionListener( SwingAdapter.getActionListener( this, "about" ) );
205         helpMenu.add( aboutMenuItem );
206 
207         menuBar.add( fileMenu );
208         menuBar.add( helpMenu );
209         frame.setJMenuBar( menuBar );
210     }
211 
212     public void about( ActionEvent ae )
213     {
214         JOptionPane.showMessageDialog( frame, ABOUT, "About", JOptionPane.INFORMATION_MESSAGE );
215     }
216 
217     public void exit( ActionEvent ae )
218     {
219         System.exit( 0 );
220     }
221 
222     private File currentDir = new File( "." );
223 
224     private File configurationFile = null;
225 
226     private long configurationFileLastModified;
227 
228     public void btOpenActionPerformed( ActionEvent ae )
229     {
230         reloadConfigurationFileEnabled = false;
231         JFileChooser fc = new JFileChooser( currentDir );
232         int ret = fc.showOpenDialog( frame );
233         if ( ret == JFileChooser.APPROVE_OPTION )
234         {
235             selectConfigurationFile( fc.getSelectedFile() );
236         }
237         reloadConfigurationFileEnabled = true;
238     }
239 
240     private LogDistiller ld = null;
241 
242     public void selectConfigurationFile( File newConfiguration )
243     {
244         initProgress();
245         configurationFile = newConfiguration;
246         configurationFileLastModified = configurationFile.lastModified();
247         currentDir = configurationFile.getParentFile();
248         frame.setTitle( TITLE + ": " + configurationFile.getName() );
249 
250         tfRules.setText( "reading " + configurationFile.getName() );
251         try
252         {
253             taResult.setText( IOUtils.toString( new XmlReader( configurationFile ) ) );
254             DOMConfigurator domConfigurator = new DOMConfigurator( new HashMap<String, String>() );
255             ld = domConfigurator.read( configurationFile, new GuiSAXErrorHandler() );
256             compatibilityUpdate( ld );
257 
258             LogDistiller.LogType logtype = ld.getLogType();
259             String logeventType = ( logtype != null ) ? logtype.getId() : null;
260 
261             // select logevent type and description according to file content
262             if ( logeventType != null )
263             {
264                 if ( LogTypes.getLogType( logeventType ) == null )
265                 {
266                     taResult.setText( "Unknown '" + logeventType + "' logtype.\n" + "Valid values: "
267                         + LogTypes.listAllLogTypeIds() );
268                     logeventType += " (unknown)";
269                 }
270             }
271             else
272             {
273                 logeventType = "undefined";
274                 taResult.setText( "Undefined logtype.\n" + "Valid values: " + LogTypes.listAllLogTypeIds() );
275             }
276             try
277             {
278                 LogDistillation distillation = new LogDistillation( ld );
279                 logtypeDescription = distillation.getLogTypeDescription();
280             }
281             catch ( Exception e )
282             {
283                 displayException( e );
284             }
285 
286             // set the destination directory
287             selectOutputDir( new File( ld.getOutput().getDirectory() ) );
288 
289             // set the rules text to a description of the rules read
290             Object[] args =
291                 new Object[] { ld.getId(), ld.getDescription(), new Integer( ld.getGroups().length ),
292                     new Integer( ld.getCategories().length ), logeventType };
293             MessageFormat mf =
294                 new MessageFormat( "{0} - {1}: {2,choice,0#no group|1#one group|1<{2,number,integer} groups}"
295                     + " {3,choice,0#with no category|1#in one category|1<in {3,number,integer} categories}"
296                     + ", logtype: {4}." );
297             tfRules.setText( mf.format( args ) );
298         }
299         catch ( RuntimeException re )
300         {
301             ld = null;
302             displayException( re );
303         }
304         catch ( IOException ioe )
305         {
306             ld = null;
307             displayException( ioe );
308         }
309         catch ( ParserConfigurationException pce )
310         {
311             ld = null;
312             displayException( pce );
313         }
314         catch ( SAXException se )
315         {
316             ld = null;
317             displayException( se );
318         }
319         updateBtRunState();
320     }
321 
322     /**
323      * Make all that's possible to update latest LogDistiller model to maintain compatibility with older versions, since
324      * some elements have moved from Ant task to LogDistiller model.
325      *
326      * @param ld LogDistiller
327      */
328     private void compatibilityUpdate( LogDistiller ld )
329     {
330         if ( ld.getLogType() != null )
331         {
332             // ok, no compatibility upgrade to consider
333             return;
334         }
335         String logeventType = (String) ld.getOutput().getParams().get( "logevent.type" );
336         if ( logeventType != null )
337         {
338             String warning =
339                 "parameter 'logevent.type' is deprecated, please use element 'logtype' from "
340                     + LogDistillerEntityResolver.LATEST_DTD + " in your rules file";
341             ld.addWarning( warning );
342             ld.buildCompatibilityLogType( logeventType );
343         }
344     }
345 
346     private final Runnable runnableReloadConfigurationFile = SwingAdapter.getRunnable( this, "reloadConfigurationFile" );
347 
348     private boolean reloadConfigurationFileEnabled = true;
349 
350     public void reloadConfigurationFile()
351     {
352         selectConfigurationFile( configurationFile );
353         initProgress();
354     }
355 
356     public class GuiSAXErrorHandler
357         implements ErrorHandler
358     {
359         public void error( SAXParseException spe )
360         {
361             taResult.append( "Parsing error on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber()
362                 + ": " + spe.getMessage() + "\n" );
363             Exception e = spe.getException();
364             if ( e != null )
365             {
366                 displayException( e );
367             }
368         }
369 
370         public void fatalError( SAXParseException spe )
371         {
372             error( spe );
373         }
374 
375         public void warning( SAXParseException spe )
376         {
377             taResult.append( "Parsing warning on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber()
378                 + ": " + spe.getMessage() + "\n" );
379             Exception e = spe.getException();
380             if ( e != null )
381             {
382                 displayException( e );
383             }
384         }
385     }
386 
387     private void displayException( Throwable t )
388     {
389         StringWriter sw = new StringWriter();
390         PrintWriter out = new PrintWriter( sw );
391         t.printStackTrace( out );
392         out.close();
393         taResult.append( sw.toString() );
394     }
395 
396     private LogType.Description logtypeDescription = null;
397 
398     public void btNewActionPerformed( ActionEvent ae )
399     {
400         NewDialog dlg = new NewDialog( currentDir );
401         File newConfiguration = dlg.showNewConfigurationDialog( frame );
402         taResult.setText( "" );
403         if ( newConfiguration != null )
404         {
405             selectConfigurationFile( newConfiguration );
406         }
407     }
408 
409     private File sourceFile = null;
410 
411     private long sourceFileLastModified;
412 
413     public void btSourceFileActionPerformed( ActionEvent ae )
414     {
415         JFileChooser fc = new JFileChooser( ( sourceFile == null ) ? currentDir : sourceFile.getParentFile() );
416         int ret = fc.showOpenDialog( frame );
417         if ( ret == JFileChooser.APPROVE_OPTION )
418         {
419             selectSourceFile( fc.getSelectedFile() );
420         }
421     }
422 
423     public void selectSourceFile( File newSourceFile )
424     {
425         sourceFile = newSourceFile;
426         tfSourceFile.setText( sourceFile.getName() );
427         initProgress();
428         if ( !sourceFile.canRead() )
429         {
430             sourceFileLastModified = -1;
431             taResult.setText( "file not found: " + sourceFile.getAbsolutePath() );
432             return;
433         }
434         sourceFileLastModified = sourceFile.lastModified();
435         tfSourceStats.setText( "file size: " + FormatUtil.formatSize( sourceFile.length() ) );
436         progress.setMinimum( 0 );
437         progress.setLongMaximum( sourceFile.length() );
438         updateBtRunState();
439     }
440 
441     private final Runnable runnableReloadSourceFile = SwingAdapter.getRunnable( this, "reloadSourceFile" );
442 
443     public void reloadSourceFile()
444     {
445         setResultText( "" );
446         selectSourceFile( sourceFile );
447     }
448 
449     private File outputDir = null;
450 
451     public void btOutputDirActionPerformed( ActionEvent ae )
452     {
453         JFileChooser fc = new JFileChooser( ( outputDir == null ) ? currentDir : outputDir.getParentFile() );
454         fc.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
455         int ret = fc.showOpenDialog( frame );
456         if ( ret == JFileChooser.APPROVE_OPTION )
457         {
458             selectOutputDir( fc.getSelectedFile() );
459         }
460     }
461 
462     private void selectOutputDir( File output )
463     {
464         outputDir = output;
465         tfOutputDir.setText( outputDir.getAbsolutePath() );
466         initProgress();
467         updateBtRunState();
468     }
469 
470     private void updateBtRunState()
471     {
472         boolean enabled =
473             ( ld != null ) && ( logtypeDescription != null ) && ( sourceFile != null ) && ( outputDir != null );
474         btRun.setEnabled( enabled );
475         btBatch.setEnabled( enabled );
476     }
477 
478     private String resultText = "";
479 
480     public void setResultText()
481     {
482         taResult.setText( resultText );
483     }
484 
485     private final Runnable runnableSetResultText = SwingAdapter.getRunnable( this, "setResultText" );
486 
487     /**
488      * set the result text, taking into account if we are on the event dispatch thread or not.
489      *
490      * @param text the text to display
491      */
492     private void setResultText( String text )
493     {
494         this.resultText = text;
495         if ( SwingUtilities.isEventDispatchThread() )
496         {
497             taResult.setText( resultText );
498         }
499         else
500         {
501             SwingUtilities.invokeLater( runnableSetResultText );
502         }
503     }
504 
505     private long progressValue = 0;
506 
507     private int progressCount = 0;
508 
509     private String progressText = null;
510 
511     private final Runnable runnableUpdateProgress = SwingAdapter.getRunnable( this, "updateProgress" );
512 
513     public void updateProgress()
514     {
515         if ( progressText == null )
516         {
517             progress.setValue( progress.getMaximum() );
518             progress.setString( "processed " + progressValue + " logevents" );
519         }
520         else
521         {
522             progress.setLongValue( progressValue );
523             progress.setString( progressCount + " - " + progressText );
524         }
525     }
526 
527     private void initProgress()
528     {
529         progress.setString( "0%" );
530         progress.setValue( 0 );
531     }
532 
533     private final Runnable runnableRunLogDistillation = SwingAdapter.getRunnable( this, "runLogDistillation" );
534 
535     public void runLogDistillation()
536     {
537         try
538         {
539             LogEvent.Factory factory =
540                 logtypeDescription.newFactory( new InputStreamReader( new FileInputStream( sourceFile ) ),
541                                                sourceFile.toURL().toString() );
542             ld.getOutput().setDirectory( outputDir.getAbsolutePath() );
543             ld.getOutput().setContent( "interactive run" );
544             LogDistillation distillation = new LogDistillation( ld );
545             distillation.begin();
546             LogEvent le;
547             long lastProgress = System.currentTimeMillis();
548             while ( ( le = factory.nextEvent() ) != null )
549             {
550                 distillation.processLogEvent( le );
551                 long time = System.currentTimeMillis();
552                 if ( ( time - lastProgress ) > 100 )
553                 {
554                     progressValue = distillation.getEventBytes();
555                     progressCount = distillation.getEventCount();
556                     progressText = le.getTimestamp();
557                     SwingUtilities.invokeLater( runnableUpdateProgress );
558                     lastProgress = time;
559                 }
560             }
561             distillation.end();
562             progressValue = distillation.getEventCount();
563             progressText = null;
564             SwingUtilities.invokeLater( runnableUpdateProgress );
565 
566             StringWriter sw = new StringWriter();
567             new TextReport().report( distillation, sw );
568             setResultText( sw.toString() );
569         }
570         catch ( RuntimeException re )
571         {
572             ld = null;
573             displayException( re );
574         }
575         catch ( IOException ioe )
576         {
577             ld = null;
578             displayException( ioe );
579         }
580         catch ( ParseException pe )
581         {
582             ld = null;
583             displayException( pe );
584         }
585         setAllButtonsEnabled( true );
586         reloadConfigurationFileEnabled = true;
587     }
588 
589     public void btRunActionPerformed( ActionEvent ae )
590     {
591         reloadConfigurationFileEnabled = false;
592         setAllButtonsEnabled( false );
593         taResult.setText( "" );
594         new Thread( runnableRunLogDistillation ).start();
595     }
596 
597     public void btBatchActionPerformed( ActionEvent ae )
598     {
599         taResult.setText( "" );
600         initProgress();
601         try
602         {
603             String text = IOUtils.toString( MainPanel.class.getResourceAsStream( "reference-build.xml" ), "UTF-8" );
604             Map<String, String> properties = new HashMap<String, String>();
605             properties.put( "id", ld.getId() );
606             properties.put( "configurationFile", configurationFile.getPath() );
607             properties.put( "sourceDir", sourceFile.getAbsoluteFile().getParentFile().getPath() );
608             properties.put( "sourceFile", sourceFile.getName() + "*" );
609             properties.put( "destDir", outputDir.getPath() );
610             PropertiesReplacer repl = new PropertiesReplacer( properties, "#(", ")" );
611             taResult.setText( repl.replaceProperties( text ) );
612         }
613         catch ( Exception e )
614         {
615             displayException( e );
616         }
617     }
618 
619     public void setAllButtonsEnabled( boolean enable )
620     {
621         btBatch.setEnabled( enable );
622         btOutputDir.setEnabled( enable );
623         btRun.setEnabled( enable );
624         btSourceFile.setEnabled( enable );
625         if ( enable )
626         {
627             updateBtRunState();
628         }
629     }
630 
631     /**
632      * Reload task: checks every second if configuration file or source file has changed.
633      */
634     public void run()
635     {
636         while ( true )
637         {
638             try
639             {
640                 Thread.sleep( 1000 );
641             }
642             catch ( InterruptedException ie )
643             {
644                 ie.printStackTrace();
645             }
646             if ( reloadConfigurationFileEnabled && ( configurationFile != null )
647                 && ( configurationFileLastModified != configurationFile.lastModified() ) )
648             {
649                 try
650                 {
651                     SwingUtilities.invokeAndWait( runnableReloadConfigurationFile );
652                 }
653                 catch ( InvocationTargetException ite )
654                 {
655                     displayException( ite );
656                 }
657                 catch ( InterruptedException ie )
658                 {
659                     displayException( ie );
660                 }
661             }
662             if ( ( sourceFile != null ) && ( sourceFileLastModified != sourceFile.lastModified() ) )
663             {
664                 try
665                 {
666                     SwingUtilities.invokeAndWait( runnableReloadSourceFile );
667                 }
668                 catch ( InvocationTargetException ite )
669                 {
670                     displayException( ite );
671                 }
672                 catch ( InterruptedException ie )
673                 {
674                     displayException( ie );
675                 }
676             }
677         }
678     }
679 }