1 // 2 // $Id: JUploadTextArea.java 95 2007-05-02 03:27:05Z 3 // /C=DE/ST=Baden-Wuerttemberg/O=ISDN4Linux/OU=Fritz 4 // Elfert/CN=svn-felfert@isdn4linux.de/emailAddress=fritz@fritz-elfert.de $ 5 // 6 // jupload - A file upload applet. 7 // Copyright 2007 The JUpload Team 8 // 9 // Created: ? 10 // Creator: William JinHua Kwong 11 // Last modified: $Date: 2011-01-19 15:52:15 +0100 (mer., 19 janv. 2011) $ 12 // 13 // This program is free software; you can redistribute it and/or modify it under 14 // the terms of the GNU General Public License as published by the Free Software 15 // Foundation; either version 2 of the License, or (at your option) any later 16 // version. This program is distributed in the hope that it will be useful, but 17 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 19 // details. You should have received a copy of the GNU General Public License 20 // along with this program; if not, write to the Free Software Foundation, Inc., 21 // 675 Mass Ave, Cambridge, MA 02139, USA. 22 23 package wjhk.jupload2.gui; 24 25 import java.awt.Color; 26 import java.text.SimpleDateFormat; 27 import java.util.Date; 28 import java.util.concurrent.BlockingQueue; 29 import java.util.concurrent.LinkedBlockingQueue; 30 31 import javax.swing.JTextArea; 32 33 import wjhk.jupload2.policies.UploadPolicy; 34 35 /** 36 * This class represents the text area for debug output. 37 */ 38 @SuppressWarnings("serial") 39 public class JUploadTextArea extends JTextArea { 40 41 /** 42 * Maximum number of characters in the logWindow. 43 */ 44 public final static int MAX_LOG_WINDOW_LENGTH = 800000; 45 46 /** 47 * The size we truncate the output to, when the maximum size of debug output 48 * is reach. We remove 20%. 49 */ 50 public final static int SIZE_TO_TRUNCATE_TO = (int) (MAX_LOG_WINDOW_LENGTH * 0.8); 51 52 /** 53 * The current upload policy 54 */ 55 UploadPolicy uploadPolicy; 56 57 /** 58 * Indicates whether the logging in the LogMessageThread is active or not. 59 * It's marked as active before starting this thread. It's marked as 60 * non-active, when this thread is interrupted, in {@link #unload()} 61 */ 62 boolean loggingActive = false; 63 64 /** 65 * The ConcurrentLinkedQueue that'll contain the messages. 66 */ 67 private BlockingQueue<String> messages; 68 69 /** 70 * This value is logged in the debug file, and in the debug output, for each 71 * line. This allows to sort the outputed line correctly. 72 * 73 * @see #displayMsg(String, String) 74 */ 75 private int nextMessageId = 1; 76 77 /** 78 * A thread, that will be called in the EventDispatcherThread, to have a 79 * tread-safe update of the GUI. This thread is responsible to display one 80 * String. 81 */ 82 static class LogMessageThread extends Thread { 83 84 /** 85 * The text area that'll contain the messages. 86 */ 87 private JUploadTextArea textArea; 88 89 /** 90 * @param textArea 91 */ 92 LogMessageThread(JUploadTextArea textArea) { 93 this.textArea = textArea; 94 setDaemon(true); 95 } 96 97 /** The run method of the Runnable Interface */ 98 @Override 99 public void run() { 100 String nextMessage = null; 101 102 if (this.textArea.uploadPolicy.getDebugLevel() >= 30) { 103 int nextMessageIdBackup = this.textArea.nextMessageId; 104 this.textArea.nextMessageId = 0; 105 this.textArea.setText(this.textArea.formatMessageOutput( 106 "[DEBUG]", "Logging system is initialized") + "\n"); 107 this.textArea.nextMessageId = nextMessageIdBackup; 108 } 109 110 while (this.textArea.loggingActive) { 111 try { 112 nextMessage = this.textArea.messages.take() + "\n"; 113 114 // Ah, a new message has been delivered... 115 116 synchronized (this.textArea) { 117 String content = this.textArea.getText(); 118 int contentLength = content.length(); 119 // If the current content is too long, we truncate it. 120 if (contentLength > JUploadTextArea.MAX_LOG_WINDOW_LENGTH) { 121 content += nextMessage; 122 String newContent = content.substring(content 123 .length() 124 - SIZE_TO_TRUNCATE_TO, content.length()); 125 this.textArea.setText(newContent); 126 contentLength = SIZE_TO_TRUNCATE_TO; 127 } else { 128 // The result is not too long 129 this.textArea.append(nextMessage); 130 contentLength += nextMessage.length(); 131 } 132 this.textArea.setCaretPosition(contentLength - 1); 133 } // synchronized 134 } catch (InterruptedException e) { 135 // If we're not running any more, then this 'stop' is 136 // not a 137 // problem any more. We're then just notified we must 138 // stop 139 // the thread. 140 if (this.textArea.loggingActive) { 141 // This should not happen, and we can not put in the 142 // standard JUpload output, as this thread is 143 // responsible for it. 144 e.printStackTrace(); 145 } 146 }// try 147 }// while 148 } 149 } 150 151 /** 152 * The thread, that will put messages in the debug log. 153 */ 154 LogMessageThread logMessageThread = null; 155 156 /** 157 * Constructs a new empty TextArea with the specified number of rows and 158 * columns. 159 * 160 * @param rows The desired number of text rows (lines). 161 * @param columns The desired number of columns. 162 * @param uploadPolicy The current uploadPolicy 163 */ 164 public JUploadTextArea(int rows, int columns, UploadPolicy uploadPolicy) { 165 super(rows, columns); 166 this.uploadPolicy = uploadPolicy; 167 this.messages = new LinkedBlockingQueue<String>(); 168 setBackground(new Color(255, 255, 203)); 169 setEditable(false); 170 setLineWrap(true); 171 setWrapStyleWord(true); 172 173 // The queue, where messages to display will be posted. 174 this.logMessageThread = new LogMessageThread(this); 175 this.logMessageThread.setName(this.logMessageThread.getClass() 176 .getName()); 177 // NO START HERE: the logMessageThread needs to know the upload policy, 178 // to run properly. The thread is started in the setUploadPolicy method. 179 180 // The unload callback will be registered, once the uploadPolicy has 181 // been built, by DefaultJUploadContext.init(JUploadApplet) 182 } 183 184 /** 185 * Add a string to the queue of string to be added to the logWindow. This is 186 * necessary, to manage the non-thread-safe Swing environment. 187 * 188 * @param tag The tag (eg: INFO, DEBUG...) 189 * @param msg The message to add, at the end of the JUploadTextArea. 190 * @return The formatted text that was added to the log window. 191 */ 192 public final String displayMsg(String tag, String msg) { 193 String fullMessage = formatMessageOutput(tag, msg); 194 195 try { 196 // messages is a BlockingQueue. So the next line may 'block' the 197 // applet main thread. But, we're optimistic: this should not happen 198 // as we instanciate an unbound LinkedBlockingQueue. We'll be 199 // blocked at Integer.MAX_VALUE, that is ... much after an 200 // OutOfMemory is thrown ! 201 this.messages.put(fullMessage); 202 } catch (InterruptedException e) { 203 System.out.println("WARNING - [" + this.getClass().getName() 204 + "] Message lost due to " + e.getClass().getName() + " (" 205 + fullMessage + ")"); 206 } 207 return fullMessage; 208 } 209 210 /** 211 * This call must be synchronized, so that there is no interaction with the 212 * LogMessageThread thread. 213 * 214 * @see JTextArea#append(String) 215 */ 216 synchronized public void append(String t) { 217 super.append(t); 218 } 219 220 /** @see JUploadPanel#copyLogWindow() */ 221 public synchronized void copyLogWindow() { 222 selectAll(); 223 copy(); 224 } 225 226 /** 227 * This call must be synchronized, so that there is no interaction with the 228 * LogMessageThread thread. 229 * 230 * @see JTextArea#insert(String, int) 231 */ 232 synchronized public void insert(String str, int pos) { 233 super.insert(str, pos); 234 } 235 236 /** 237 * This call must be synchronized, so that there is no interaction with the 238 * LogMessageThread thread. 239 * 240 * @see JTextArea#replaceRange(String, int, int) 241 */ 242 synchronized public void replaceRange(String str, int start, int end) { 243 super.replaceRange(str, start, end); 244 } 245 246 /** 247 * This call must be synchronized, so that there is no interaction with the 248 * LogMessageThread thread. 249 * 250 * @see JTextArea#setText(String) 251 */ 252 synchronized public void setText(String t) { 253 super.setText(t); 254 } 255 256 /** 257 * @param uploadPolicy the uploadPolicy to set 258 */ 259 public void setUploadPolicy(UploadPolicy uploadPolicy) { 260 this.uploadPolicy = uploadPolicy; 261 this.uploadPolicy.getContext().registerUnload(this, "unload"); 262 // We can now start the log thread. 263 this.loggingActive = true; 264 this.logMessageThread.start(); 265 } 266 267 /** 268 * Free any used ressources. Actually close the LogMessageThread thread. 269 */ 270 public synchronized void unload() { 271 this.loggingActive = false; 272 this.logMessageThread.interrupt(); 273 } 274 275 /** 276 * Format the message, with the given tag. This method also add the time and 277 * the Thread name.<BR> 278 * e.g.:<BR> 279 * nextMessageId[tab]14:04:30.718[tab]FileUploadManagerThread[tab][DEBUG][tab] 280 * Found one reader for jpg extension 281 * 282 * @param tag The tag ([WARN], [ERROR]...) 283 * @param msg The message to format. 284 * @return The formatted message, without trailing EOL character. 285 */ 286 String formatMessageOutput(String tag, String msg) { 287 final String stamp = String.format("%1$05d", this.nextMessageId++) + " \t" 288 + new SimpleDateFormat("HH:mm:ss.SSS ").format(new Date()) 289 + "\t" + Thread.currentThread().getName() + "\t" + tag + " \t"; 290 while (msg.endsWith("\n")) { 291 msg = msg.substring(0, msg.length() - 1); 292 } 293 return (stamp + msg.replaceAll("\n", "\n" + stamp)); 294 } 295 296 }