001package net.sf.logdistiller.gui; 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.awt.Font; 018import java.awt.LayoutManager; 019import java.awt.event.ActionEvent; 020import java.awt.event.KeyEvent; 021import java.io.*; 022import java.lang.reflect.*; 023import java.text.MessageFormat; 024import java.text.ParseException; 025import java.util.*; 026import javax.swing.*; 027import javax.xml.parsers.ParserConfigurationException; 028 029import org.xml.sax.ErrorHandler; 030import org.xml.sax.SAXException; 031import org.xml.sax.SAXParseException; 032 033import com.jgoodies.forms.builder.DefaultFormBuilder; 034import com.jgoodies.forms.factories.ButtonBarFactory; 035import com.jgoodies.forms.factories.DefaultComponentFactory; 036import com.jgoodies.forms.layout.FormLayout; 037import com.sun.syndication.io.XmlReader; 038 039import org.apache.commons.io.IOUtils; 040 041import net.sf.logdistiller.LogDistillation; 042import net.sf.logdistiller.LogDistiller; 043import net.sf.logdistiller.LogEvent; 044import net.sf.logdistiller.LogType; 045import net.sf.logdistiller.LogTypes; 046import net.sf.logdistiller.reports.TextReport; 047import net.sf.logdistiller.util.FormatUtil; 048import net.sf.logdistiller.util.PropertiesReplacer; 049import net.sf.logdistiller.xml.DOMConfigurator; 050import net.sf.logdistiller.xml.LogDistillerEntityResolver; 051 052/** 053 * Main gui panel. 054 */ 055public class MainPanel 056 implements Runnable 057{ 058 private static final String TITLE = "LogDistiller " + LogDistiller.getVersion() + " interactive console"; 059 060 private static final String ABOUT = TITLE + "\n\nCopyright (C) 2004-2009 Hervé Boutemy. All rights reserved."; 061 062 private final JFrame frame; 063 064 public MainPanel( JFrame frame ) 065 { 066 this.frame = frame; 067 frame.setTitle( TITLE ); 068 } 069 070 private Thread reloadThread; 071 072 protected JPanel panel = new JPanel(); 073 074 private JTextField tfRules = new JTextField( "open or create a rules configuration file..." ); 075 076 private JTextField tfSourceFile = new JTextField(); 077 078 private JTextField tfSourceStats = new JTextField(); 079 080 private JTextField tfOutputDir = new JTextField(); 081 082 private JButton btRun = new JButton( "run" ); 083 084 private JButton btBatch = new JButton( "batch" ); 085 086 private JButton btSourceFile = new JButton( "..." ); 087 088 private JButton btOutputDir = new JButton( "..." ); 089 090 private JTextArea taResult = new JTextArea(); 091 092 private LongProgressBar progress = new LongProgressBar(); 093 094 private void initComponents() 095 { 096 tfSourceFile.setEditable( false ); 097 btSourceFile.addActionListener( SwingAdapter.getActionListener( this, "btSourceFileActionPerformed" ) ); 098 tfSourceStats.setEditable( false ); 099 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}