001package bradleyross.common; 002import java.io.*; 003import java.net.*; 004import java.awt.*; 005import java.awt.Button; 006import java.awt.event.*; 007/** 008* This class opens a window for communications 009* with a port on another system. 010* 011* @author Bradley Ross 012*/ 013public class communicationsWindow { 014/** 015* Determines amount of diagnostic material to be 016* printed. 017*/ 018private int debugLevel = 0; 019private static int windowNumber = 0; 020// private int thisWindow; 021private FlowLayout layout; 022private Socket sock = null; 023private PrintWriter pw = null; 024private BufferedReader in = null; 025private InputStreamReader streamReader = null; 026private String logFileName="communicationsWindowLog.txt"; 027/** 028* Determines whether messages are written to the log file. 029*/ 030private boolean logFileActive = false; 031private java.io.FileWriter logFile; 032private java.io.BufferedWriter logBuffer; 033/** 034* Used for writing the communicated text to a log file. 035*/ 036private java.io.PrintWriter logWriter; 037/* 038** The following items are used to determine when the 039** items in the window should be resized. 040*/ 041/** 042* The number of columns in the TextArea objects if 043* the window is made narrower. 044*/ 045protected int colsSmaller; 046/** 047* The number of columns in the TextArea objects 048* currently. 049*/ 050protected int colsCurrent; 051/** 052* The number of columns in the TextArea objects if 053* the window is made wider. 054*/ 055protected int colsLarger; 056/** 057* The width of the TextArea objects in pixels when 058* they are colsSmaller columns wide. 059*/ 060protected int widthSmaller; 061/** 062* The width of the TextArea objects in pixels when 063* they are colsCurrent columns wide. 064*/ 065protected int widthCurrent; 066/** 067* The width of the TextArea objects in pixels when 068* they are colsLarger columns wide. 069*/ 070protected int widthLarger; 071/** 072* Used to determine the number of rows that should 073* be in the TextArea objects when the height of 074* TextArea objects is made slightly smaller. 075* 076* <p>The TextArea.getMinimumSize method is 077* applied to an object eight rows taller than 078* the topWindow object. This allows room for 079* bottomWindow and the buttons.</p> 080*/ 081protected int rowsSmaller; 082protected int rowsCurrent; 083protected int rowsLarger; 084/** 085* This is the height in pixels returned by the 086* TextArea.getMinimumSize method for a 087* TextArea block rowsSmaller rows in height. 088*/ 089protected int heightSmaller; 090/** 091* This is the height in pixels returned by the 092* TextArea.getMinimumSize method for a 093* TextArea block rowsCurrent rows in height. 094*/ 095protected int heightCurrent; 096/** 097* This is the height in pixels returned by the 098* TextArea.getMinimumSize method for a 099* TextArea block rowsLarger rows in height. 100*/ 101protected int heightLarger; 102/** 103* Set to true when the objects in the window 104* have been initialized. 105* 106* <p>Some events related to window movement and 107* resizing may be generated before the objects 108* fully initialized. These should not be 109* responded to as the sizes and locations have 110* not yet been set.</p> 111*/ 112protected boolean initialized = false; 113/** 114* The bottomWindow TextArea is used to hold information 115* being prepared for transmission. 116*/ 117private java.awt.TextArea bottomWindow; 118/** 119* The topWindow TextArea holds the information sent from 120* the other end of the connection. 121*/ 122private java.awt.TextArea topWindow; 123/** 124* The sendButton Button object causes the material in the 125* lower window to be sent over the communication line. 126* <p>The data is also sent when the user depresses the Return 127* key on the keyboard. This button on the form may be 128* removed at a later date.</p> 129*/ 130private java.awt.Button sendButton; 131/** 132* The closeButton Button can be used to 133* close the connection when it fails 134* to close normally. 135*/ 136private java.awt.Button closeButton; 137/** 138* Object for the frame in which the application 139* is displayed. 140* 141* <p>The menu bar is attached to the frame.</p> 142*/ 143private Frame myFrame; 144/** 145* The myPanel panel provides a place to attach 146* key listeners. 147* <p>It appears that a Panel is a container, but 148* a Frame isn't. Since listeners must be 149* attached to containers, it was necessary to 150* create a panel within the frame.</p> 151* <p>After the object is created, a listener is 152* created with an anonymous inner class to 153* react to changes in the size of the panel.</p> 154*/ 155private Panel myPanel; 156private String windowName; 157/** 158* This method is called when the window is resized. 159* 160* <p>It examines the width and height of the windows 161* to determine whether the TextArea objects should 162* have their height and width changed.</p> 163*/ 164protected void windowResizer() 165 { 166 if (!initialized) { return; } 167 if (debugLevel > 2) 168 { addMessage("Entering windowResizer"); } 169 int windowHeight = myPanel.getHeight(); 170 int windowWidth = myPanel.getWidth(); 171 if (debugLevel > 2) 172 { addMessage("Current panel width is " 173 .concat(Integer.toString(myPanel.getWidth())) 174 .concat(" Range: ") 175 .concat(Integer.toString(widthSmaller)) 176 .concat("-") 177 .concat(Integer.toString(widthLarger))); } 178 if (windowWidth > widthLarger + 15) 179 { 180 if (debugLevel > 0) 181 { addMessage("Expanding TextArea objects " 182 .concat(Integer.toString(colsLarger)) 183 .concat(" columns")); } 184 topWindow.setColumns(colsLarger); 185 bottomWindow.setColumns(colsLarger); 186 colsCurrent = colsLarger; 187 widthCurrent = widthLarger; 188 colsSmaller = colsCurrent - 5; 189 colsLarger = colsCurrent + 5; 190 widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width; 191 widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width; 192 myPanel.repaint(); 193 } 194 else if (widthCurrent + 15 > windowWidth) 195 { 196 if (debugLevel > 0) 197 { addMessage("Shrinking TextArea items " 198 .concat(Integer.toString(colsSmaller)) 199 .concat(" columns")); } 200 topWindow.setColumns(colsSmaller); 201 bottomWindow.setColumns(colsSmaller); 202 colsCurrent = colsSmaller; 203 widthCurrent = widthSmaller; 204 colsSmaller = colsCurrent - 5; 205 colsLarger = colsCurrent + 5; 206 widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width; 207 widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width; 208 myPanel.repaint(); 209 } 210 if (windowHeight > heightLarger + 15) 211 { 212 rowsCurrent = rowsLarger; 213 heightCurrent = heightLarger; 214 if (debugLevel > 0) 215 { addMessage("Increasing height of topWindow to " 216 .concat(Integer.toString(rowsCurrent - 8))); } 217 if (rowsCurrent > 10) 218 { 219 topWindow.setRows(rowsCurrent - 8); 220 rowsLarger = rowsCurrent + 2; 221 rowsSmaller = rowsCurrent - 2; 222 heightSmaller = topWindow.getMinimumSize(rowsSmaller, 75).height; 223 heightLarger = topWindow.getMinimumSize(rowsLarger, 75).height; 224 } 225 else 226 { 227 addMessage("Trying to reduce height below 0" 228 .concat(" while increasing height")); 229 } 230 myPanel.repaint(); 231 } 232 else if (heightCurrent > windowHeight) 233 { 234 rowsCurrent = rowsSmaller; 235 heightCurrent = heightSmaller; 236 if (debugLevel > 0) 237 { addMessage("Decreasing height of topWindow to " 238 .concat(Integer.toString(rowsCurrent - 8))); } 239 rowsLarger = rowsCurrent + 2; 240 rowsSmaller = rowsCurrent - 2; 241 if (rowsCurrent > 10) 242 { 243 topWindow.setRows(rowsCurrent - 8); 244 heightSmaller = topWindow.getMinimumSize(rowsSmaller, 75).height; 245 heightLarger = topWindow.getMinimumSize(rowsLarger, 75).height; 246 } 247 else 248 { 249 addMessage("Trying to reduce height below 0" 250 .concat(" while decreasing height")); 251 } 252 myPanel.repaint(); 253 } 254 } 255/** 256* This method generates a message and adds it to the 257* log and the Sys.output character stream. 258*/ 259protected void addMessage (String display) 260 { 261 if (debugLevel > 0) 262 { 263 System.out.println(display); 264 } 265 } 266/** 267* This function is called whenever a key is pressed. 268* <p>If the Delete or Backspace key is depressed, the 269* last character in the bottomWindow textArea is 270* removed.</p> 271* <p>If the Return key is depressed, the contents of 272* bottomWindow are sent to the remote system and 273* contents of bottomWindow are erased.</p> 274*/ 275protected void processKey (KeyEvent e) 276 { 277 char[] processed = { e.getKeyChar() }; 278 if (debugLevel > 2) 279 { System.out.print(processed[0]); } 280 if (processed[0] == '\n') 281 { sendMessage(); } 282 if ((processed[0] == java.awt.event.KeyEvent.VK_BACK_SPACE) || 283 (processed[0] == java.awt.event.KeyEvent.VK_DELETE)) 284 { 285 String workingText = bottomWindow.getText(); 286 bottomWindow.setText(workingText.substring(0, 287 workingText.length() - 1)); 288 } 289 else 290 { bottomWindow.append(new String(processed)); } 291 } 292/** 293* This method is called when the Send button is depressed 294* on the form 295* or the user depresses the Return key on the keyboard. 296*/ 297protected void sendMessage() 298 { 299 pw.print(bottomWindow.getText().concat("\n")); 300 pw.flush(); 301 topWindow.append("Text sent: " 302 .concat(bottomWindow.getText()) 303 .concat("\n")) ; 304 bottomWindow.setText(null); 305 // bottomWindow.requestFocus(); 306 } 307public communicationsWindow(String name) 308 { 309 windowName = name; 310 } 311/** 312* This method is used to control the amount of diagnostic 313* messages generated. 314* <p>A value of 0 means no messages are to be printed. Higher 315* values generate larger amounts of messages. 316* </p> 317*/ 318public void setDebugLevel (int level) 319 { 320 debugLevel = level; 321 } 322public int getDebugLevel () 323 { return debugLevel; } 324/** 325* This is the main method for creating the communications 326* window and setting up the communications stream. 327*/ 328public void start (Socket inputSock) 329 { 330 if (debugLevel > 1) 331 { 332 System.out.println 333 ("Running method start of communicationsWindow"); 334 } 335 windowNumber = windowNumber + 1; 336 // thisWindow = windowNumber; 337 sock = inputSock; 338 if (debugLevel > 1) 339 { System.out.println("Point A"); } 340 myFrame = new Frame(windowName); 341 java.awt.MenuBar menubar = new java.awt.MenuBar(); 342 java.awt.Menu logMenu = new java.awt.Menu("Log"); 343 java.awt.MenuItem startLogging = new java.awt.MenuItem("Start logging"); 344 java.awt.MenuItem stopLogging = new java.awt.MenuItem("Stop logging"); 345 logMenu.add(startLogging); 346 logMenu.add(stopLogging); 347 menubar.add(logMenu); 348 myFrame.setMenuBar(menubar); 349 myPanel = new Panel(); 350 startLogging.addActionListener(new ActionListener() 351 { public final void actionPerformed(ActionEvent e) 352 { 353 System.out.println("*** Start logging "); 354 if (!logFileActive) 355 { 356 try 357 { 358 logFile = new java.io.FileWriter(logFileName, true); 359 logBuffer = new java.io.BufferedWriter(logFile); 360 logWriter = new java.io.PrintWriter(logBuffer); 361 logWriter.println( (new java.util.Date()).toString()); 362 logFileActive = true; 363 } 364 catch (Exception e2) 365 { 366 System.out.println("Error when starting logging"); 367 } 368 } 369 } }); 370 stopLogging.addActionListener(new ActionListener() 371 { public final void actionPerformed(ActionEvent e) 372 { 373 System.out.println("*** Stop logging "); 374 if (logFileActive) 375 { 376 try 377 { 378 logWriter.flush(); 379 logWriter.close(); 380 logFileActive = false; 381 } 382 catch (Exception e2) 383 { 384 System.out.println("Error when stopping logging"); 385 } 386 } 387 } } ); 388 /* 389 ** setSize is (width, height) 390 */ 391 if (debugLevel > 1) 392 { System.out.println("Point B"); } 393 myFrame.setSize(800, 600); 394 layout = new FlowLayout(); 395 /* 396 * Sets up the listener for keystrokes. 397 */ 398 myPanel.addKeyListener(new KeyAdapter() 399 { public final void keyTyped (KeyEvent e) 400 { 401 processKey(e); 402 } } ); 403 /* 404 ** Listen for changes to window 405 */ 406 myPanel.addComponentListener(new ComponentListener() 407 { 408 public void componentHidden(ComponentEvent e) 409 { 410 if (debugLevel > 0) 411 { addMessage("Window hidden"); } 412 } 413 public void componentMoved (ComponentEvent e) 414 { 415 if (debugLevel > 0) 416 { addMessage("Window moved"); } 417 myFrame.repaint(); 418 } 419 public void componentResized(ComponentEvent e) 420 { 421 if (debugLevel > 2) 422 { 423 addMessage("Window resized " 424 .concat(Integer.toString(myFrame.getWidth())) 425 .concat(":") 426 .concat(Integer.toString(myFrame.getHeight()))); 427 } 428 windowResizer(); 429 } 430 public void componentShown (ComponentEvent e) 431 { 432 if (debugLevel > 0) 433 { addMessage("Window becomes visible"); } 434 } 435 } ); 436 /* 437 ** Specification for TextArea constructor is 438 ** (text, rows, columnns, scrollbars) 439 ** 440 ** topWindow 441 */ 442 topWindow = 443 new java.awt.TextArea("", 25, 80, 444 java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY); 445 topWindow.setEditable(false); 446 topWindow.addFocusListener(new FocusAdapter() 447 { public void focusGained(FocusEvent e) 448 {myPanel.requestFocus(); } } ); 449 /* 450 ** bottomWindow 451 ** I changed from SCROLLBARS_VERTICAL_ONLY to 452 ** SCROLLBARS_BOTH 453 */ 454 bottomWindow = new java.awt.TextArea("", 4, 80, 455 java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY); 456 bottomWindow.setEditable(false); 457 bottomWindow.addFocusListener(new FocusAdapter() 458 { public void focusGained(FocusEvent e) 459 {myPanel.requestFocus(); } } ); 460 /* 461 ** sendButton 462 */ 463 sendButton = new Button("Send"); 464 sendButton.addActionListener (new ActionListener() 465 { public final void actionPerformed (ActionEvent e) 466 { 467 sendMessage(); 468 } } ); 469 /* 470 ** closeButton 471 */ 472 closeButton = new Button("Close"); 473 closeButton.addActionListener (new ActionListener() 474 { public final void actionPerformed (ActionEvent e) 475 {try 476 { 477 if (debugLevel > 1) 478 { System.out.println("Close button activated"); } 479 sock.close(); 480 myFrame.removeAll(); 481 return; 482 } 483 catch (Exception e2) 484 { 485 System.out.println("*** Start of error message"); 486 System.out.println("unexpected Exception while" 487 .concat(" processing Close button")); 488 e2.printStackTrace(); 489 System.out.println("*** End of error message"); 490 } 491 } } ); 492 /* 493 ** Set up the various window items and prepare them 494 ** for interaction 495 */ 496 myPanel.add(topWindow); 497 myPanel.add(bottomWindow); 498 myPanel.add(sendButton); 499 myPanel.add(closeButton); 500 myFrame.add(myPanel); 501 myPanel.setLayout(layout); 502 myFrame.doLayout(); 503 myPanel.doLayout(); 504 myFrame.setVisible(true); 505 myPanel.setVisible(true); 506 myPanel.requestFocus(); 507 // String response = ""; 508 if (debugLevel > 1) 509 { System.out.println("Creating frame"); } 510 initialized = true; 511 /* 512 ** Determine the limits for shrinking and growing 513 ** TextArea items 514 */ 515 colsCurrent = 80; 516 colsSmaller = 75; 517 colsLarger = 85; 518 widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width; 519 widthCurrent = (topWindow.getMinimumSize(25, colsCurrent)).width; 520 widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width; 521 rowsCurrent = 33; 522 rowsSmaller = 31; 523 rowsLarger = 35; 524 heightSmaller = topWindow.getMinimumSize(31, 75).height; 525 heightCurrent = topWindow.getMinimumSize(33, 75).height; 526 heightLarger = topWindow.getMinimumSize(35, 75).height; 527 if (debugLevel > 0) 528 { 529 addMessage ("Next smaller: " 530 .concat(Integer.toString(colsSmaller)) 531 .concat(" ") 532 .concat(Integer.toString(widthSmaller))); 533 addMessage ("Next larger: " 534 .concat(Integer.toString(colsLarger)) 535 .concat(" ") 536 .concat(Integer.toString(widthLarger))); 537 addMessage("Current window width: " 538 .concat(Integer.toString(myPanel.getWidth()))); 539 addMessage("Trigger heights are " 540 .concat(Integer.toString(heightSmaller)).concat(" ") 541 .concat(Integer.toString(heightCurrent)).concat(" ") 542 .concat(Integer.toString(heightLarger))); 543 } 544 545 try 546 { 547 if (System.getProperty("LOCAL_HOST") == null) 548 { System.setProperty("LOCAL_HOST", 549 sock.getLocalAddress().getHostName());} 550 pw = new PrintWriter(sock.getOutputStream(), 551 true); 552 streamReader = new InputStreamReader(sock.getInputStream(), 553 "8859_1"); 554 in = new BufferedReader(streamReader); 555 // Socket now ready for data transfer 556 /* 557 ** The problem is how to make the application close 558 ** when the socket is closed 559 ** 560 ** The method readLine waits until information is 561 ** available. However, if the connection has been 562 ** closed, it returns a null value. 563 */ 564 while (true) 565 { 566 if (inputSock.isClosed()) 567 { break; } 568 String request = in.readLine(); 569 if (request == null) 570 {break;} 571 topWindow.append(request.concat("\n")); 572 if (logFileActive) 573 { logWriter.println(request); } 574 } 575 if (logFileActive) 576 { 577 logWriter.flush(); 578 logWriter.close(); 579 } 580 } 581 catch (SocketException e) 582 { 583 System.out.println("*** Start of error message"); 584 System.out.println("SocketException while reading data"); 585 System.out.println("Message: ".concat(e.getMessage())); 586 System.out.println("Can be ignored if message is *Socket closed*" 587 .concat(" when socket is closed")); 588 e.printStackTrace(); 589 System.out.println("*** End of error message"); 590 return; 591 } 592 catch (InterruptedIOException e) 593 { 594 System.out.println("*** Start of error message"); 595 System.out.println("InterruptedIOException while reading data"); 596 System.out.println("Message: ".concat(e.getMessage())); 597 e.printStackTrace(); 598 System.out.println("*** End of error message");} 599 catch (Exception e) 600 { 601 System.out.println("*** Start of error message"); 602 System.out.println("unexpected Exception while reading data"); 603 System.out.println("Message: ".concat(e.getMessage())); 604 e.printStackTrace(); 605 } 606 // Test if socket still open 607 if (debugLevel > 1) 608 { System.out.println("Removing frame"); } 609 myFrame.removeAll(); 610 myFrame.removeNotify(); 611 try 612 {if (! sock.isClosed()) 613 {sock.close(); } 614 } 615 catch (Exception e) 616 { 617 System.out.println("*** Start of error message"); 618 System.out.println("Error when closing socket"); 619 System.out.println("Message: ".concat(e.getMessage())); 620 e.printStackTrace(); 621 System.out.println("*** End of error message"); 622 } 623 return; 624 } // end of method 625} // end of class