View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
4    * 
5    *  Licensed to the Apache Software Foundation (ASF) under one
6    *  or more contributor license agreements.  See the NOTICE file
7    *  distributed with this work for additional information
8    *  regarding copyright ownership.  The ASF licenses this file
9    *  to you under the Apache License, Version 2.0 (the
10   *  "License"); you may not use this file except in compliance
11   *  with the License.  You may obtain a copy of the License at
12   *
13   *    http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing,
16   *  software distributed under the License is distributed on an
17   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   *  KIND, either express or implied.  See the License for the
19   *  specific language governing permissions and limitations
20   *  under the License.
21   *
22   *  @author vlads
23   *  @version $Id: Main.java 2607 2008-12-17 23:51:33Z skarzhevskyy $
24   */
25  package net.sf.bluecove.awt;
26  
27  import java.awt.Dimension;
28  import java.awt.EventQueue;
29  import java.awt.Font;
30  import java.awt.Frame;
31  import java.awt.Menu;
32  import java.awt.MenuBar;
33  import java.awt.MenuItem;
34  import java.awt.MenuShortcut;
35  import java.awt.Rectangle;
36  import java.awt.TextArea;
37  import java.awt.Toolkit;
38  import java.awt.event.ActionEvent;
39  import java.awt.event.ActionListener;
40  import java.awt.event.KeyEvent;
41  import java.awt.event.KeyListener;
42  import java.awt.event.WindowAdapter;
43  import java.awt.event.WindowEvent;
44  import java.io.File;
45  import java.io.FileWriter;
46  import java.io.OutputStreamWriter;
47  import java.text.SimpleDateFormat;
48  import java.util.Date;
49  import java.util.Enumeration;
50  import java.util.Vector;
51  
52  import javax.bluetooth.BluetoothStateException;
53  import javax.bluetooth.DiscoveryAgent;
54  
55  import net.sf.bluecove.Configuration;
56  import net.sf.bluecove.Consts;
57  import net.sf.bluecove.Switcher;
58  import net.sf.bluecove.TestConcurrent;
59  import net.sf.bluecove.se.BlueCoveSpecific;
60  import net.sf.bluecove.se.FileStorage;
61  import net.sf.bluecove.se.JavaSECommon;
62  import net.sf.bluecove.se.LocalDeviceManager;
63  import net.sf.bluecove.se.RemoteDeviceManager;
64  import net.sf.bluecove.se.UIHelper;
65  
66  import org.bluecove.tester.log.Logger;
67  import org.bluecove.tester.log.LoggerAppender;
68  import org.bluecove.tester.util.IOUtils;
69  import org.bluecove.tester.util.RuntimeDetect;
70  import org.bluecove.tester.util.TimeUtils;
71  
72  import com.intel.bluetooth.BlueCoveImpl;
73  
74  /**
75   *
76   * 
77   */
78  public class Main extends Frame implements LoggerAppender {
79  
80  	private static final long serialVersionUID = 1L;
81  
82  	private TextArea output = null;
83  
84  	private int outputLines = 0;
85  
86  	private Vector logLinesQueue = new Vector();
87  
88  	private boolean logUpdaterRunning = false;
89  
90  	int lastKeyCode;
91  
92  	MenuItem debugOn;
93  
94  	public static void main(String[] args) {
95  		// System.setProperty("bluecove.debug", "true");
96  		// System.getProperties().put("bluecove.debug", "true");
97  
98  		// BlueCoveImpl.instance().getBluetoothPeer().enableNativeDebug(true);
99  		JavaSECommon.initOnce();
100 		Configuration.storage = new FileStorage();
101 
102 		Main app = new Main();
103 		app.setVisible(true);
104 		Logger.debug("Stated app");
105 		Logger.debug("OS:" + System.getProperty("os.name") + "|" + System.getProperty("os.version") + "|"
106 				+ System.getProperty("os.arch"));
107 		Logger.debug("Java:" + System.getProperty("java.vendor") + " " + System.getProperty("java.version"));
108 
109 		for (int i = 0; i < args.length; i++) {
110 			if (args[i].equalsIgnoreCase("--stack")) {
111 				// This is used in WebStart when system properties can't be
112 				// defined.
113 				i++;
114 				try {
115 					BlueCoveImpl.instance().setBluetoothStack(args[i]);
116 				} catch (BluetoothStateException e) {
117 					Logger.error("can't init stack", e);
118 				}
119 				app.updateTitle();
120 			} else if (args[i].equalsIgnoreCase("--runonce")) {
121 				int rc = Switcher.runClient();
122 				Logger.debug("Finished app " + rc);
123 				System.exit(rc);
124 			}
125 		}
126 	}
127 
128 	public Main() {
129 		addWindowListener(new WindowAdapter() {
130 			public void windowClosing(WindowEvent w) {
131 				quit();
132 			}
133 		});
134 
135 		Logger.addAppender(this);
136 		BlueCoveSpecific.addAppender(this);
137 
138 		Logger.debug("Stating app");
139 
140 		this.setTitle("BlueCove tester");
141 
142 		final MenuBar menuBar = new MenuBar();
143 		Menu menuBluetooth = new Menu("Bluetooth");
144 
145 		final MenuItem serverStart = addMenu(menuBluetooth, "Server Start", new ActionListener() {
146 			public void actionPerformed(ActionEvent e) {
147 				Switcher.startServer();
148 				updateTitle();
149 			}
150 		}, KeyEvent.VK_5);
151 
152 		final MenuItem serverStop = addMenu(menuBluetooth, "Server Stop", new ActionListener() {
153 			public void actionPerformed(ActionEvent e) {
154 				Switcher.serverShutdown();
155 			}
156 		}, KeyEvent.VK_6);
157 
158 		final MenuItem serverClientsStop = addMenu(menuBluetooth, "Server Clients close", new ActionListener() {
159 			public void actionPerformed(ActionEvent e) {
160 				Switcher.closeServerClientConnections();
161 			}
162 		});
163 
164 		final MenuItem clientStart = addMenu(menuBluetooth, "Client Start", new ActionListener() {
165 			public void actionPerformed(ActionEvent e) {
166 				Switcher.startClient();
167 				updateTitle();
168 			}
169 		}, KeyEvent.VK_2);
170 
171 		final MenuItem clientStop = addMenu(menuBluetooth, "Client Stop", new ActionListener() {
172 			public void actionPerformed(ActionEvent e) {
173 				Switcher.clientShutdown();
174 			}
175 		}, KeyEvent.VK_3);
176 
177 		final MenuItem tckStart;
178 		if (Configuration.likedTCKAgent) {
179 			tckStart = addMenu(menuBluetooth, "Start TCK Agent", new ActionListener() {
180 				public void actionPerformed(ActionEvent e) {
181 					Switcher.startTCKAgent();
182 				}
183 			});
184 		} else {
185 			tckStart = null;
186 		}
187 
188 		addMenu(menuBluetooth, "Discovery", new ActionListener() {
189 			public void actionPerformed(ActionEvent e) {
190 				Switcher.startDiscovery();
191 				updateTitle();
192 			}
193 		}, KeyEvent.VK_MULTIPLY);
194 
195 		addMenu(menuBluetooth, "Services Search", new ActionListener() {
196 			public void actionPerformed(ActionEvent e) {
197 				Switcher.startServicesSearch();
198 				updateTitle();
199 			}
200 		}, KeyEvent.VK_7);
201 
202 		addMenu(menuBluetooth, "Client Stress Start", new ActionListener() {
203 			public void actionPerformed(ActionEvent e) {
204 				Switcher.startClientStress();
205 				updateTitle();
206 			}
207 		});
208 
209 		addMenu(menuBluetooth, "Client selectService Start", new ActionListener() {
210 			public void actionPerformed(ActionEvent e) {
211 				Switcher.startClientSelectService();
212 				updateTitle();
213 			}
214 		});
215 
216 		addMenu(menuBluetooth, "Client Last service Start", new ActionListener() {
217 			public void actionPerformed(ActionEvent e) {
218 				Switcher.startClientLastURl();
219 				updateTitle();
220 			}
221 		});
222 
223 		addMenu(menuBluetooth, "Client Last device Start", new ActionListener() {
224 			public void actionPerformed(ActionEvent e) {
225 				Switcher.startClientLastDevice();
226 				updateTitle();
227 			}
228 		});
229 
230 		final MenuItem stop = addMenu(menuBluetooth, "Stop all work", new ActionListener() {
231 			public void actionPerformed(ActionEvent e) {
232 				Switcher.clientShutdown();
233 				Switcher.serverShutdown();
234 			}
235 		}, KeyEvent.VK_S);
236 
237 		addMenu(menuBluetooth, "Quit", new ActionListener() {
238 			public void actionPerformed(ActionEvent e) {
239 				quit();
240 			}
241 		}, KeyEvent.VK_X);
242 
243 		menuBar.add(menuBluetooth);
244 
245 		Menu menuLogs = new Menu("Logs");
246 
247 		debugOn = addMenu(menuLogs, "BlueCove Debug ON", new ActionListener() {
248 			public void actionPerformed(ActionEvent e) {
249 				boolean dbg = BlueCoveSpecific.changeDebug();
250 				if (dbg) {
251 					debugOn.setLabel("BlueCove Debug OFF");
252 				} else {
253 					debugOn.setLabel("BlueCove Debug ON");
254 				}
255 			}
256 		});
257 
258 		addMenu(menuLogs, "Clear Log", new ActionListener() {
259 			public void actionPerformed(ActionEvent e) {
260 				clear();
261 
262 			}
263 		}, KeyEvent.VK_Z);
264 
265 		addMenu(menuLogs, "Print FailureLog", new ActionListenerRunnable() {
266 			public void run() {
267 				UIHelper.printFailureLog();
268 			}
269 		}, KeyEvent.VK_4);
270 
271 		addMenu(menuLogs, "Clear Stats", new ActionListenerRunnable() {
272 			public void run() {
273 				UIHelper.clearStats();
274 			}
275 		});
276 
277 		if (JavaSECommon.isJava5()) {
278 			addMenu(menuLogs, "ThreadDump", new ActionListenerRunnable() {
279 				public void run() {
280 					JavaSECommon.threadDump();
281 				}
282 			});
283 		}
284 
285 		addMenu(menuLogs, "Save to File", new ActionListenerRunnable() {
286 			public void run() {
287 				logSaveToFile();
288 			}
289 		});
290 
291 		menuBar.add(menuLogs);
292 
293 		Menu menuMore = new Menu("More");
294 
295 		addMenu(menuMore, "Configuration", new ActionListener() {
296 			public void actionPerformed(ActionEvent e) {
297 				(new ConfigurationDialog(Main.this)).setVisible(true);
298 			}
299 		});
300 
301 		addMenu(menuMore, "Client Connection", new ActionListener() {
302 			public void actionPerformed(ActionEvent e) {
303 				(new ClientConnectionDialog(Main.this)).setVisible(true);
304 			}
305 		});
306 
307 		addMenu(menuMore, "OBEX Client Connection", new ActionListener() {
308 			public void actionPerformed(ActionEvent e) {
309 				(new ObexClientConnectionDialog(Main.this)).setVisible(true);
310 			}
311 		});
312 
313 		addMenu(menuMore, "Two Clients Start", new ActionListener() {
314 			public void actionPerformed(ActionEvent e) {
315 				Switcher.startTwoClients();
316 				updateTitle();
317 			}
318 		});
319 
320 		addMenu(menuMore, "Concurrent Services Search", new ActionListener() {
321 			public void actionPerformed(ActionEvent e) {
322 				TestConcurrent.startConcurrentServicesSearchClients();
323 			}
324 		});
325 
326 		Menu menuSpeedTests = new Menu("Speed tests");
327 		addMenu(menuSpeedTests, "RFCOMM Read test", new ActionListenerRunnable() {
328 			public void run() {
329 				UIHelper.configurationForSpeedTest(Consts.TRAFFIC_GENERATOR_WRITE, false);
330 				Switcher.startClient();
331 			}
332 		});
333 
334 		addMenu(menuSpeedTests, "RFCOMM Write test", new ActionListenerRunnable() {
335 			public void run() {
336 				UIHelper.configurationForSpeedTest(Consts.TRAFFIC_GENERATOR_READ, false);
337 				Switcher.startClient();
338 			}
339 		});
340 		addMenu(menuSpeedTests, "L2CAP Read test", new ActionListenerRunnable() {
341 			public void run() {
342 				UIHelper.configurationForSpeedTest(Consts.TRAFFIC_GENERATOR_WRITE, true);
343 				Switcher.startClient();
344 			}
345 		});
346 
347 		addMenu(menuSpeedTests, "L2CAP Write test", new ActionListenerRunnable() {
348 			public void run() {
349 				UIHelper.configurationForSpeedTest(Consts.TRAFFIC_GENERATOR_READ, true);
350 				Switcher.startClient();
351 			}
352 		});
353 
354 		menuMore.add(menuSpeedTests);
355 
356 		Menu menuLocalDevice = new Menu("LocalDevice");
357 		addMenu(menuLocalDevice, "Get discoverable", new ActionListener() {
358 			public void actionPerformed(ActionEvent e) {
359 				LocalDeviceManager.getDiscoverable();
360 			}
361 		});
362 
363 		addMenu(menuLocalDevice, "Set NOT discoverable", new ActionListener() {
364 			public void actionPerformed(ActionEvent e) {
365 				LocalDeviceManager.setNotDiscoverable();
366 			}
367 		});
368 
369 		addMenu(menuLocalDevice, "Set discoverable GIAC", new ActionListener() {
370 			public void actionPerformed(ActionEvent e) {
371 				LocalDeviceManager.setDiscoverableGIAC();
372 			}
373 		});
374 
375 		addMenu(menuLocalDevice, "Set discoverable LIAC", new ActionListener() {
376 			public void actionPerformed(ActionEvent e) {
377 				LocalDeviceManager.setDiscoverableLIAC();
378 			}
379 		});
380 
381 		menuMore.add(menuLocalDevice);
382 
383 		Menu menuRemoteDevice = new Menu("RemoteDevice");
384 		addMenu(menuRemoteDevice, "Retrieve CACHED", new ActionListener() {
385 			public void actionPerformed(ActionEvent e) {
386 				RemoteDeviceManager.retrieveDevices(DiscoveryAgent.CACHED);
387 			}
388 		});
389 		addMenu(menuRemoteDevice, "Retrieve PREKNOWN", new ActionListener() {
390 			public void actionPerformed(ActionEvent e) {
391 				RemoteDeviceManager.retrieveDevices(DiscoveryAgent.PREKNOWN);
392 			}
393 		});
394 		menuMore.add(menuRemoteDevice);
395 
396 		Menu threadLocalStack = new Menu("ThreadLocalStack");
397 
398 		MenuItem menuWinsock = addMenu(threadLocalStack, "Set 'winsock'", new ActionListener() {
399 			public void actionPerformed(ActionEvent e) {
400 				LocalDeviceManager.setUseWINSOCK();
401 				updateTitle();
402 			}
403 		});
404 		menuWinsock.setEnabled(Configuration.windows);
405 
406 		MenuItem menuWidcomm = addMenu(threadLocalStack, "Set 'widcomm'", new ActionListener() {
407 			public void actionPerformed(ActionEvent e) {
408 				LocalDeviceManager.setUseWIDCOMM();
409 				updateTitle();
410 			}
411 		});
412 		menuWidcomm.setEnabled(Configuration.windows);
413 
414 		addMenu(threadLocalStack, "Set 'deviceID=0'", new ActionListener() {
415 			public void actionPerformed(ActionEvent e) {
416 				LocalDeviceManager.setUseDevice(0);
417 				updateTitle();
418 			}
419 		});
420 
421 		addMenu(threadLocalStack, "Set 'deviceID=1'", new ActionListener() {
422 			public void actionPerformed(ActionEvent e) {
423 				LocalDeviceManager.setUseDevice(1);
424 				updateTitle();
425 			}
426 		});
427 
428 		if (Configuration.linux) {
429 			try {
430 				Vector ids = BlueCoveImpl.getLocalDevicesID();
431 				for (Enumeration en = ids.elements(); en.hasMoreElements();) {
432 					final int id = Integer.parseInt((String) en.nextElement());
433 					if (id > 1) {
434 						Configuration.hasManyDevices = true;
435 					}
436 					if (id >= 2) {
437 						addMenu(threadLocalStack, "Set 'deviceID=" + id + "'", new ActionListener() {
438 							public void actionPerformed(ActionEvent e) {
439 								LocalDeviceManager.setUseDevice(id);
440 								updateTitle();
441 							}
442 						});
443 					}
444 				}
445 			} catch (Throwable e) {
446 				Logger.debug("get device list error", e);
447 			}
448 		}
449 
450 		addMenu(threadLocalStack, "shutdown", new ActionListener() {
451 			public void actionPerformed(ActionEvent e) {
452 				LocalDeviceManager.shutdownThreadLocal();
453 			}
454 		});
455 
456 		menuMore.add(threadLocalStack);
457 
458 		addMenu(menuMore, "Shutdown BlueCove", new ActionListener() {
459 			public void actionPerformed(ActionEvent e) {
460 				LocalDeviceManager.shutdown();
461 			}
462 		});
463 
464 		menuBar.add(menuMore);
465 
466 		setMenuBar(menuBar);
467 
468 		// Create a scrolled text area.
469 		output = new TextArea("");
470 		output.setEditable(false);
471 		this.add(output);
472 
473 		Runnable statusUpdateRunnable = new Runnable() {
474 			public void run() {
475 				while (true) {
476 					try {
477 						Thread.sleep(1000);
478 					} catch (InterruptedException e) {
479 						break;
480 					}
481 					if (isMainFrameActive()) {
482 						serverStop.setEnabled(Switcher.isRunningServer());
483 						serverStart.setEnabled(!Switcher.isRunningServer());
484 						serverClientsStop.setEnabled(Switcher.isRunningServerClients());
485 
486 						clientStop.setEnabled(Switcher.isRunningClient());
487 						clientStart.setEnabled(!Switcher.isRunningClient());
488 						stop.setEnabled(Switcher.isRunningClient() || Switcher.isRunningServer());
489 						if (tckStart != null) {
490 							tckStart.setEnabled(!Switcher.isTCKRunning());
491 						}
492 					}
493 				}
494 			}
495 		};
496 		Thread statusUpdate = RuntimeDetect.cldcStub.createNamedThread(statusUpdateRunnable, "StatusUpdate");
497 		statusUpdate.start();
498 
499 		output.addKeyListener(new KeyListener() {
500 
501 			public void keyPressed(KeyEvent e) {
502 				// Logger.debug("key:" + e.getKeyCode() + " " +
503 				// KeyEvent.getKeyText(e.getKeyCode()));
504 				Main.this.keyPressed(e.getKeyCode());
505 			}
506 
507 			public void keyReleased(KeyEvent e) {
508 			}
509 
510 			public void keyTyped(KeyEvent e) {
511 			}
512 		});
513 
514 		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
515 		if (screenSize.height < 400) {
516 			Configuration.screenSizeSmall = true;
517 		}
518 		Font logFont = new Font("Monospaced", Font.PLAIN, Configuration.screenSizeSmall ? 9 : 12);
519 		output.setFont(logFont);
520 
521 		if (screenSize.width > 600) {
522 			screenSize.setSize(600, 420);
523 		}
524 		if (this.isResizable()) {
525 			Rectangle b = this.getBounds();
526 			b.x = Integer.valueOf(Configuration.getStorageData("main.x", "0")).intValue();
527 			b.y = Integer.valueOf(Configuration.getStorageData("main.y", "0")).intValue();
528 			b.height = Integer.valueOf(Configuration.getStorageData("main.height", String.valueOf(screenSize.height)))
529 					.intValue();
530 			b.width = Integer.valueOf(Configuration.getStorageData("main.width", String.valueOf(screenSize.width)))
531 					.intValue();
532 			this.setBounds(b);
533 		}
534 	}
535 
536 	boolean isMainFrameActive() {
537 		try {
538 			return isActive();
539 		} catch (Throwable j13) {
540 			return true;
541 		}
542 	}
543 
544 	private void updateTitle() {
545 		this.setTitle(UIHelper.getMainWindowTitle());
546 	}
547 
548 	private MenuItem addMenu(Menu menu, String name, ActionListener l) {
549 		return addMenu(menu, name, l, 0);
550 	}
551 
552 	private MenuItem addMenu(Menu menu, String name, ActionListener l, int key) {
553 		MenuItem menuItem = new MenuItem(name);
554 		menuItem.addActionListener(l);
555 		menu.add(menuItem);
556 		if (key != 0) {
557 			menuItem.setShortcut(new MenuShortcut(key, false));
558 		}
559 		return menuItem;
560 	}
561 
562 	protected void keyPressed(int keyCode) {
563 		switch (keyCode) {
564 		case '1':
565 			// printStats();
566 			break;
567 		case '4':
568 			UIHelper.printFailureLog();
569 			break;
570 		case '0':
571 			// logScrollX = 0;
572 			// setLogEndLine();
573 			break;
574 		case '*':
575 		case KeyEvent.VK_MULTIPLY:
576 		case 119:
577 			Switcher.startDiscovery();
578 			break;
579 		case '7':
580 			Switcher.startServicesSearch();
581 			break;
582 		case '2':
583 			Switcher.startClient();
584 			break;
585 		case '3':
586 			Switcher.clientShutdown();
587 			break;
588 		case '5':
589 			Switcher.startServer();
590 			break;
591 		case '6':
592 			Switcher.serverShutdown();
593 			break;
594 		case '8':
595 			// startSwitcher();
596 			break;
597 		case '9':
598 			// stopSwitcher();
599 			break;
600 		case '#':
601 		case 120:
602 			if (lastKeyCode == keyCode) {
603 				quit();
604 			}
605 			clear();
606 			break;
607 		default:
608 			// Logger.debug("keyCode " + keyCode);
609 		}
610 		lastKeyCode = keyCode;
611 	}
612 
613 	private void clear() {
614 		if (output == null) {
615 			return;
616 		}
617 		synchronized (logLinesQueue) {
618 			logLinesQueue.clear();
619 		}
620 		output.setText("");
621 		outputLines = 0;
622 	}
623 
624 	private void quit() {
625 		Logger.debug("quit");
626 		Switcher.clientShutdown();
627 		Switcher.serverShutdownOnExit();
628 
629 		Rectangle b = this.getBounds();
630 		Configuration.storeData("main.x", String.valueOf(b.x));
631 		Configuration.storeData("main.y", String.valueOf(b.y));
632 		Configuration.storeData("main.height", String.valueOf(b.height));
633 		Configuration.storeData("main.width", String.valueOf(b.width));
634 
635 		Logger.removeAppender(this);
636 		BlueCoveSpecific.removeAppender();
637 
638 		// this.dispose();
639 		System.exit(0);
640 	}
641 
642 	private void logSaveToFile() {
643 		SimpleDateFormat fmt = new SimpleDateFormat("MM-dd_HH-mm-ss");
644 		OutputStreamWriter out = null;
645 		try {
646 			File file = new File("BlueCoveTester-" + fmt.format(new Date()) + ".log");
647 			out = new FileWriter(file);
648 			out.write(output.getText());
649 			out.flush();
650 			out.close();
651 			out = null;
652 			Logger.info("Log saved to file " + file.getAbsolutePath());
653 		} catch (Throwable ignore) {
654 		} finally {
655 			IOUtils.closeQuietly(out);
656 		}
657 	}
658 
659 	public void appendLog(int level, String message, Throwable throwable) {
660 		if (output == null) {
661 			return;
662 		}
663 		final StringBuffer buf = new StringBuffer();
664 
665 		if (Configuration.logTimeStamp) {
666 			String time = TimeUtils.timeNowToString();
667 			buf.append(time).append(" ");
668 		}
669 
670 		switch (level) {
671 		case Logger.ERROR:
672 			// errorCount ++;
673 			buf.append("e.");
674 			break;
675 		case Logger.WARN:
676 			buf.append("w.");
677 			break;
678 		case Logger.INFO:
679 			buf.append("i.");
680 			break;
681 		}
682 		buf.append(message);
683 		if (throwable != null) {
684 			buf.append(' ');
685 			String className = throwable.getClass().getName();
686 			buf.append(className.substring(1 + className.lastIndexOf('.')));
687 			if (throwable.getMessage() != null) {
688 				buf.append(':');
689 				buf.append(throwable.getMessage());
690 			}
691 		}
692 		buf.append("\n");
693 		boolean createUpdater = false;
694 		synchronized (logLinesQueue) {
695 			if (logLinesQueue.isEmpty()) {
696 				createUpdater = true;
697 			} else {
698 				createUpdater = !logUpdaterRunning;
699 			}
700 			logLinesQueue.addElement(buf.toString());
701 		}
702 		if (createUpdater) {
703 			try {
704 				EventQueue.invokeLater(new AwtLogUpdater(true));
705 			} catch (NoSuchMethodError java1) {
706 				(new AwtLogUpdater(false)).run();
707 			}
708 		}
709 	}
710 
711 	private class AwtLogUpdater implements Runnable {
712 
713 		boolean canRestart;
714 
715 		AwtLogUpdater(boolean canRestart) {
716 			this.canRestart = canRestart;
717 		}
718 
719 		private String getNextLines() {
720 			synchronized (logLinesQueue) {
721 				if (logLinesQueue.isEmpty()) {
722 					return null;
723 				}
724 				StringBuffer buf = new StringBuffer();
725 				for (int i = 0; i < 40; i++) {
726 					try {
727 						buf.append((String) logLinesQueue.firstElement());
728 					} catch (Exception e) {
729 						// NoSuchElementException
730 						break;
731 					}
732 					logLinesQueue.removeElementAt(0);
733 
734 				}
735 				return buf.toString();
736 			}
737 		}
738 
739 		public void run() {
740 			int oneCallCount = 0;
741 			boolean restart = false;
742 			synchronized (logLinesQueue) {
743 				logUpdaterRunning = true;
744 			}
745 			String lines;
746 			try {
747 				while ((lines = getNextLines()) != null) {
748 					if (outputLines > 5000) {
749 						clear();
750 					}
751 					output.append(lines);
752 					outputLines++;
753 					oneCallCount++;
754 					if (oneCallCount > 10) {
755 						restart = true;
756 						break;
757 					}
758 				}
759 			} finally {
760 				if (restart && canRestart) {
761 					EventQueue.invokeLater(new AwtLogUpdater(true));
762 				} else {
763 					synchronized (logLinesQueue) {
764 						logUpdaterRunning = false;
765 					}
766 				}
767 			}
768 		}
769 	}
770 
771 }