Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jpicedt/ui/util/SystemOutUtilities.java


1   /*  jPicEdt version 1.3.2, a picture editor for LaTeX.
2       Copyright (C) 1999-2002  Sylvain Reynal
3   
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8   
9       This program is distributed in the hope that it will be useful,
10      but WITHOUT ANY WARRANTY; without even the implied warranty of
11      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12      GNU General Public License for more details.
13  
14      You should have received a copy of the GNU General Public License
15      along with this program; if not, write to the Free Software
16      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  
18      Sylvain Reynal
19      Département de Physique
20      Ecole Nationale Supérieure de l'Electronique et de ses Applications (ENSEA)
21      6, avenue du Ponceau
22      95014 CERGY CEDEX
23      FRANCE
24  
25      Tel : 00 +33 130 736 245
26      Fax : 00 +33 130 736 667
27      e-mail : reynal@ensea.fr
28      jPicEdt web page : http://trashx.ensea.fr/jpicedt/
29  */
30  
31  package jpicedt.ui.util;
32  
33  import java.awt.*;
34  import java.awt.event.*;
35  import javax.swing.*;
36  import java.io.*;
37  import java.util.*;
38  
39  /**
40   * A class that allow redirection of stderr and/or stdout to a log file.
41   * Invokation of this class must be done with "SystemOutUtilities.instance()" which return a reference to the singleton.
42   *
43   * @author Sylvain Reynal
44   * @since PicEdt 1.3
45   */
46  public class SystemOutUtilities  {
47  
48    /** doesn't redirect stdout to a file, i.e. redirect to console ; instead open a frame and displays the error message in it */
49    public static final int STANDARD = 1;
50  
51    /** redirects stdout to a file named "jPicedtError.log" */
52    public static final int FILE = 2;
53  
54  
55    /* DEBUG flag (this amongst other thing redirects only System.err, so that we can print debug messages to System.out) */
56    private static final boolean DEBUG=false;
57  
58  
59  
60  
61  
62  
63  
64  
65  
66    ////////////////////////////////////////
67    //// PUBLIC STATIC METHODS
68    ////////////////////////////////////////
69    private static SystemOutUtilities singleton = null;
70  
71    /**
72     * intanciates singleton if it's null, then returns it
73     */
74    public static SystemOutUtilities instance(){
75  
76      if (singleton == null) singleton = new SystemOutUtilities();
77      return singleton;
78    }
79  
80    //////////////////////////////////////////////////////////////////////
81    //// PUBLIC NON-STATIC METHODS (accessed through a call to instance())
82    //////////////////////////////////////////////////////////////////////
83    private int currentRedir = STANDARD; // when this class get accessed for the first time, System.out = standard out
84  
85    /**
86     * Redirect to the stream of the given type
87     * @param type one of the predefinite SystemOutUtilities's redirection types
88     */
89    public void redirect(int type){
90  
91      if (DEBUG) System.out.println("SystemOutUtilities.Redirecting System.out : type = " + (type==FILE ? "FILE" : "STANDARD"));
92  
93      // first close open streams and release resource (currentRedir = old redir type)
94      switch(currentRedir){
95      case FILE :
96        stopSystemOutToFileRedirection();
97        break;
98      default:
99        // this was standard out => nothing to close
100       break;
101     }
102     // now process redirection :
103     currentRedir = type;
104     switch(currentRedir){
105     case FILE :
106       redirectSystemOutToFile();
107       break;
108     default:
109       // standard out
110       break;
111     }
112   }
113 
114 
115   /**
116    * Sets the "displayDialog" flag, i.e. what must happen when an error message comes up and redir=FILE : do we open a JDialog
117    * or simply write the error message to the log file ?
118    * (obviously, calling this method if redir=STANDARD simply makes no sense)
119    *
120    * @param state if currentRedir = FILE and :
121    * - state==TRUE, enables opening a dialog box whenever some String is written to System.out
122    *   A "watchdog" Thread is created for that purpose : System.out is redirected to a pipe, which the Thread watches periodically.
123    * - state==FALSE, redirect System.out directly to the "toFilePrintStream" FOS, and kill the watchdog Thread if it's still alive.
124    *
125    * Otherwise (i.e. currentRedir = STANDARD), we do nothing.
126    */
127   public void displayDialog(boolean state){
128 
129     if (DEBUG) System.out.println("SystemOutUtilities.displayDialog(" + state + ")");
130 
131     if (currentRedir==STANDARD) return; // this may also happen if "redirectSystemOutToFile" previously failed
132 
133     // flag to signals Dialog enabling
134     displayDialog = state;
135     // redirect System.out to pipe
136     if (state==true){
137       try{
138         // create output pipe for System.out
139         PipedOutputStream pos = new PipedOutputStream(); // not yet connected
140         newSystemOut = new PrintStream(new BufferedOutputStream(pos,1024), true); // autoflush = true
141         // create input pipe (we'll read from it and send date to file/JTextPane/etc...)
142         PipedInputStream pis = new PipedInputStream();
143         bufReader = new BufferedReader(new InputStreamReader(pis),1024);
144         // connect pipes
145         pos.connect(pis);
146         // start watchdog timer
147         lt = new ListenerThread();
148         lt.start();
149       }
150       catch(IOException e){
151         System.err.println("SystemOutUtilities.Cannot redirect System.out and System.err to pipe !");
152         e.printStackTrace();
153         newSystemOut = null;
154         return;
155       }
156       // set System.out and System.err to the previously created pipe, from which we'll read (through "pis") text
157       // and then send it to a JTextArea :
158       if (!DEBUG) System.setOut(newSystemOut);
159       System.setErr(newSystemOut);
160     }
161 
162     // we switch back to "redirection to toFilePrintStream"
163     else{
164       newSystemOut = toFilePrintStream;
165       if (!DEBUG) System.setOut(newSystemOut);
166       System.setErr(newSystemOut);
167       lt = null; // stop thread
168     }
169   }
170 
171 
172 
173 
174 
175 
176 
177 
178   ////////////////////////////////////////////////////////////
179   //// PRIVATE METHODS AND FIELDS
180   ///////////////////////////////////////////////////////////
181 
182   /**
183    * constructor is protected since the only way to access this class is by using SystemOutUtilities.instance()
184    */
185   private SystemOutUtilities() {
186 
187     // remember default System.out and System.err
188     oldSystemOut = System.out;
189     oldSystemErr = System.err;
190   }
191 
192   /* private variables */
193   private PrintStream oldSystemOut; // save old System.out
194   private PrintStream newSystemOut; // new System.out (with redirection)
195   private PrintStream oldSystemErr; // save old System.err
196   private BufferedReader bufReader; // a buffer reader that is connected, via a pipe, to System.out, and allow us to display error message in a JTextArea
197   private PrintStream toFilePrintStream; // a print stream that writes to the log file
198   private ListenerThread lt; // a watchdog thread that listen to incoming error messages from System.out, via a pipe
199   private boolean displayDialog = false; // if TRUE, a dialog box opens whenever a message comes up (or within a short delay)
200   private SystemOutToFrame systemOutToFrameInstance=null; // an instance of the JFrame used to display messages ; if it's null, we create a new one; otherwise we use the existing one, so as to avoid opening more than one frame.
201 
202   /**
203    * Redirect System.out to a file named "jPicEdtError.log" located in user's home directory
204    * - displayDialog = false by default (i.e. no dialog box gets opened when an error message occurs)
205    * - we init "toFilePrintStream" so that it points to "user's setting dir/error.log"
206    * - we set System.out to this stream (redirection)
207    */
208   private void redirectSystemOutToFile(){
209 
210     if (DEBUG) System.out.println("SystemOutUtilities.redirectSystemOutToFile()...");
211     try{
212       // create output stream to log file
213       String fileName = getErrorLogFile();
214       toFilePrintStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(fileName), 1024), true); // autoflush = true
215       newSystemOut = toFilePrintStream;
216     }
217     // if an exception occurs, we switch back to "redirect to console"
218     catch(IOException e){
219       System.err.println("Can not redirect System.out and System.err to a file !");
220       e.printStackTrace();
221       newSystemOut = null;
222       currentRedir = STANDARD;
223       return;
224     }
225     // and redirect System.out if success
226     if (!DEBUG) System.setOut(newSystemOut);
227     System.setErr(newSystemOut);
228 
229   }
230 
231   /**
232    * Return the path to error.log
233    */
234   public static String getErrorLogFile(){
235     return jpicedt.JPicEdt.getUserSettingsDirectory() + File.separator + "error.log";
236   }
237 
238   /**
239    * After a redirection to a pipe, sets back System.out and System.err to their default value
240    */
241   private void stopSystemOutToFileRedirection(){
242 
243     if (DEBUG) System.out.println("SystemOutUtilities.stopSystemOutToFileRedirection() : closing previous streams...");
244 
245     if (newSystemOut == null) return;
246     if (lt!= null) lt.interrupt(); // flush bufReader
247 
248     if (!DEBUG) System.setOut(oldSystemOut); // restore System.out default PrintStream
249     System.setErr(oldSystemErr); // restore System.err default PrintStream
250     newSystemOut.flush();
251 
252     // flush'n close previous stream (so that pending messages get written to jPicEdtError.log)
253     toFilePrintStream.flush();
254     toFilePrintStream.close();
255 
256     // kill thread
257     lt = null;
258   }
259 
260   /**
261    * the watchdog thread that periodically listens to incoming error messages (currently set to 3")
262    * then writes them to file and/or to a JTextArea (see inner class SystemOutToFrame)
263    * kill it by setting "lt = null"
264    */
265   class ListenerThread extends Thread {
266 
267     public ListenerThread()  {
268       super("SystemOutUtilities.ListenerThread");
269       if (DEBUG) System.out.println("SystemOutUtilities : new ListenerThread()");
270       setPriority(Thread.MIN_PRIORITY);
271       //      start(); // now done by caller (due to contention pbs)
272     }
273 
274     public void run(){
275 
276       if (DEBUG) System.out.println("SystemOutUtilities.ListenerThread started...");
277 
278       Thread c = Thread.currentThread();
279       while(c == lt){ // i.e. while this Thread is still a live
280         if (DEBUG) System.out.println("SystemOutUtilities.ListenerThread is alive !");
281         try{
282           if (DEBUG) System.out.println("SystemOutUtilities : there's nothing in System.out pipe !");
283           // if there's nothing in bufReader (i.e. no incoming message)
284           while(bufReader != null && !bufReader.ready()){
285             sleep(2999);
286           }
287         }
288         catch(InterruptedException ie){ie.printStackTrace();}
289         catch(IOException ioe){ioe.printStackTrace();}
290 
291         // if something has been written to System.out...
292         if (bufReader != null){
293           String str;
294           StringBuffer buf = new StringBuffer(100);
295           try{
296             // read as much line as possible from bufReader (that is, from the pipe)
297             // then, 1°) write them to file 2°) display them in a JDialog
298             while(bufReader.ready() && (str = bufReader.readLine()) != null){
299               if (DEBUG) System.out.println("SystemOutUtilities.Writing to jPicEdtError.log : " + str);
300               toFilePrintStream.println(str);
301               buf.append(str);
302               buf.append("\n");
303             }
304           }
305         catch(Exception e){e.printStackTrace();}
306           if (displayDialog) {
307             if (systemOutToFrameInstance == null) systemOutToFrameInstance = new SystemOutToFrame();
308             systemOutToFrameInstance.println(buf.toString());
309           }
310         }
311       }
312       if (DEBUG) System.out.println("SystemOutUtilities.ListenerThread killed...");
313     }
314   }
315 
316   /**
317    * A JDialog box which is used to display error message in a JTextPane or to a file.
318    */
319   class SystemOutToFrame extends JDialog {
320 
321     JTextArea streamTA = new JTextArea(50,20);
322     JCheckBox dontShowAgainCB;
323 
324     /**
325      * build then asyncrhonously show a JDialog
326      * this method is thread-safe regarding Swing paint mechanism
327      */
328     SystemOutToFrame(){
329       super();
330       setTitle("jPicEdt Error Log");
331       setModal(true);
332       if (DEBUG) System.out.println("SystemOutUtilities.Opening a new SystemOutToFrame...");
333       enableEvents(AWTEvent.WINDOW_EVENT_MASK);
334 
335       JPanel panelStream = new JPanel(new BorderLayout(5,5));
336       panelStream.setBorder(BorderFactory.createEtchedBorder());
337 
338       // init text area :
339       streamTA.setEditable(false);
340       JScrollPane scrollStream = new JScrollPane(streamTA);
341       panelStream.add(scrollStream, BorderLayout.CENTER);
342 
343       // init widgets
344       JPanel p = new JPanel(new GridLayout(2,1,5,5));
345       p.add(new JTextArea("Beta release BUG REPORT : please send "
346                           + System.getProperty("user.home") + System.getProperty("file.separator")
347                           + "jPicEdtError.log to reynal@ensea.fr, it'll be very helpful !\n"
348                           + "Getting annoyed ? Well, launching jpicedt with '-redir=standard' as the first arg simply writes these messages to the console."));
349       dontShowAgainCB = new JCheckBox("Pretty boring indeed, so please don't show me the same message again and again :-((");
350       dontShowAgainCB.addActionListener(new ActionListener(){
351                                           public void actionPerformed(ActionEvent e){SystemOutUtilities.instance().displayDialog(false);}});
352       p.add(dontShowAgainCB);
353       panelStream.add(p,BorderLayout.SOUTH);
354 
355       // add panelStream to the frame :
356       getContentPane().add(panelStream,BorderLayout.NORTH);
357       // ensure calling SystemOutToFrame is thread-safe
358       SwingUtilities.invokeLater(new Runnable(){
359                                    public void run(){
360                                      pack();
361                                      setVisible(true);}});
362     }
363 
364     /**
365      * print out the given text in this JDialog
366      */
367     public void println(String text){
368 
369       streamTA.append(text);
370       streamTA.append("\n");
371     }
372 
373     /**
374      * dispose this frame
375      */
376     protected void processWindowEvent(WindowEvent e) {
377 
378       super.processWindowEvent(e);
379       if (e.getID() == WindowEvent.WINDOW_CLOSING) {
380         dispose();
381         systemOutToFrameInstance = null;
382       }
383     }
384   } //SystemOutToFrame
385 
386 } // class SystemOutUtilities
387 
388 
389