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* 259* @param display name to be used on window 260*/ 261protected void addMessage (String display) 262 { 263 if (debugLevel > 0) 264 { 265 System.out.println(display); 266 } 267 } 268/** 269* This function is called whenever a key is pressed. 270* <p>If the Delete or Backspace key is depressed, the 271* last character in the bottomWindow textArea is 272* removed.</p> 273* <p>If the Return key is depressed, the contents of 274* bottomWindow are sent to the remote system and 275* contents of bottomWindow are erased.</p> 276* 277* @param e event to be processed 278*/ 279protected void processKey (KeyEvent e) 280 { 281 char[] processed = { e.getKeyChar() }; 282 if (debugLevel > 2) 283 { System.out.print(processed[0]); } 284 if (processed[0] == '\n') 285 { sendMessage(); } 286 if ((processed[0] == java.awt.event.KeyEvent.VK_BACK_SPACE) || 287 (processed[0] == java.awt.event.KeyEvent.VK_DELETE)) 288 { 289 String workingText = bottomWindow.getText(); 290 bottomWindow.setText(workingText.substring(0, 291 workingText.length() - 1)); 292 } 293 else 294 { bottomWindow.append(new String(processed)); } 295 } 296/** 297* This method is called when the Send button is depressed 298* on the form 299* or the user depresses the Return key on the keyboard. 300*/ 301protected void sendMessage() 302 { 303 pw.print(bottomWindow.getText().concat("\n")); 304 pw.flush(); 305 topWindow.append("Text sent: " 306 .concat(bottomWindow.getText()) 307 .concat("\n")) ; 308 bottomWindow.setText(null); 309 // bottomWindow.requestFocus(); 310 } 311public communicationsWindow(String name) 312 { 313 windowName = name; 314 } 315/** 316* This method is used to control the amount of diagnostic 317* messages generated. 318* <p>A value of 0 means no messages are to be printed. Higher 319* values generate larger amounts of messages. 320* </p> 321* 322* @param level value to be used for debugLevel. 323*/ 324public void setDebugLevel (int level) 325 { 326 debugLevel = level; 327 } 328public int getDebugLevel () 329 { return debugLevel; } 330/** 331* This is the main method for creating the communications 332* window and setting up the communications stream. 333* 334* @param inputSock socket to be used for communications 335*/ 336public void start (Socket inputSock) 337 { 338 if (debugLevel > 1) 339 { 340 System.out.println 341 ("Running method start of communicationsWindow"); 342 } 343 windowNumber = windowNumber + 1; 344 // thisWindow = windowNumber; 345 sock = inputSock; 346 if (debugLevel > 1) 347 { System.out.println("Point A"); } 348 myFrame = new Frame(windowName); 349 java.awt.MenuBar menubar = new java.awt.MenuBar(); 350 java.awt.Menu logMenu = new java.awt.Menu("Log"); 351 java.awt.MenuItem startLogging = new java.awt.MenuItem("Start logging"); 352 java.awt.MenuItem stopLogging = new java.awt.MenuItem("Stop logging"); 353 logMenu.add(startLogging); 354 logMenu.add(stopLogging); 355 menubar.add(logMenu); 356 myFrame.setMenuBar(menubar); 357 myPanel = new Panel(); 358 startLogging.addActionListener(new ActionListener() 359 { public final void actionPerformed(ActionEvent e) 360 { 361 System.out.println("*** Start logging "); 362 if (!logFileActive) 363 { 364 try 365 { 366 logFile = new java.io.FileWriter(logFileName, true); 367 logBuffer = new java.io.BufferedWriter(logFile); 368 logWriter = new java.io.PrintWriter(logBuffer); 369 logWriter.println( (new java.util.Date()).toString()); 370 logFileActive = true; 371 } 372 catch (Exception e2) 373 { 374 System.out.println("Error when starting logging"); 375 } 376 } 377 } }); 378 stopLogging.addActionListener(new ActionListener() 379 { public final void actionPerformed(ActionEvent e) 380 { 381 System.out.println("*** Stop logging "); 382 if (logFileActive) 383 { 384 try 385 { 386 logWriter.flush(); 387 logWriter.close(); 388 logFileActive = false; 389 } 390 catch (Exception e2) 391 { 392 System.out.println("Error when stopping logging"); 393 } 394 } 395 } } ); 396 /* 397 ** setSize is (width, height) 398 */ 399 if (debugLevel > 1) 400 { System.out.println("Point B"); } 401 myFrame.setSize(800, 600); 402 layout = new FlowLayout(); 403 /* 404 * Sets up the listener for keystrokes. 405 */ 406 myPanel.addKeyListener(new KeyAdapter() 407 { public final void keyTyped (KeyEvent e) 408 { 409 processKey(e); 410 } } ); 411 /* 412 ** Listen for changes to window 413 */ 414 myPanel.addComponentListener(new ComponentListener() 415 { 416 public void componentHidden(ComponentEvent e) 417 { 418 if (debugLevel > 0) 419 { addMessage("Window hidden"); } 420 } 421 public void componentMoved (ComponentEvent e) 422 { 423 if (debugLevel > 0) 424 { addMessage("Window moved"); } 425 myFrame.repaint(); 426 } 427 public void componentResized(ComponentEvent e) 428 { 429 if (debugLevel > 2) 430 { 431 addMessage("Window resized " 432 .concat(Integer.toString(myFrame.getWidth())) 433 .concat(":") 434 .concat(Integer.toString(myFrame.getHeight()))); 435 } 436 windowResizer(); 437 } 438 public void componentShown (ComponentEvent e) 439 { 440 if (debugLevel > 0) 441 { addMessage("Window becomes visible"); } 442 } 443 } ); 444 /* 445 ** Specification for TextArea constructor is 446 ** (text, rows, columnns, scrollbars) 447 ** 448 ** topWindow 449 */ 450 topWindow = 451 new java.awt.TextArea("", 25, 80, 452 java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY); 453 topWindow.setEditable(false); 454 topWindow.addFocusListener(new FocusAdapter() 455 { public void focusGained(FocusEvent e) 456 {myPanel.requestFocus(); } } ); 457 /* 458 ** bottomWindow 459 ** I changed from SCROLLBARS_VERTICAL_ONLY to 460 ** SCROLLBARS_BOTH 461 */ 462 bottomWindow = new java.awt.TextArea("", 4, 80, 463 java.awt.TextArea.SCROLLBARS_VERTICAL_ONLY); 464 bottomWindow.setEditable(false); 465 bottomWindow.addFocusListener(new FocusAdapter() 466 { public void focusGained(FocusEvent e) 467 {myPanel.requestFocus(); } } ); 468 /* 469 ** sendButton 470 */ 471 sendButton = new Button("Send"); 472 sendButton.addActionListener (new ActionListener() 473 { public final void actionPerformed (ActionEvent e) 474 { 475 sendMessage(); 476 } } ); 477 /* 478 ** closeButton 479 */ 480 closeButton = new Button("Close"); 481 closeButton.addActionListener (new ActionListener() 482 { public final void actionPerformed (ActionEvent e) 483 {try 484 { 485 if (debugLevel > 1) 486 { System.out.println("Close button activated"); } 487 sock.close(); 488 myFrame.removeAll(); 489 return; 490 } 491 catch (Exception e2) 492 { 493 System.out.println("*** Start of error message"); 494 System.out.println("unexpected Exception while" 495 .concat(" processing Close button")); 496 e2.printStackTrace(); 497 System.out.println("*** End of error message"); 498 } 499 } } ); 500 /* 501 ** Set up the various window items and prepare them 502 ** for interaction 503 */ 504 myPanel.add(topWindow); 505 myPanel.add(bottomWindow); 506 myPanel.add(sendButton); 507 myPanel.add(closeButton); 508 myFrame.add(myPanel); 509 myPanel.setLayout(layout); 510 myFrame.doLayout(); 511 myPanel.doLayout(); 512 myFrame.setVisible(true); 513 myPanel.setVisible(true); 514 myPanel.requestFocus(); 515 // String response = ""; 516 if (debugLevel > 1) 517 { System.out.println("Creating frame"); } 518 initialized = true; 519 /* 520 ** Determine the limits for shrinking and growing 521 ** TextArea items 522 */ 523 colsCurrent = 80; 524 colsSmaller = 75; 525 colsLarger = 85; 526 widthSmaller = (topWindow.getMinimumSize(25, colsSmaller)).width; 527 widthCurrent = (topWindow.getMinimumSize(25, colsCurrent)).width; 528 widthLarger = (topWindow.getMinimumSize(25, colsLarger)).width; 529 rowsCurrent = 33; 530 rowsSmaller = 31; 531 rowsLarger = 35; 532 heightSmaller = topWindow.getMinimumSize(31, 75).height; 533 heightCurrent = topWindow.getMinimumSize(33, 75).height; 534 heightLarger = topWindow.getMinimumSize(35, 75).height; 535 if (debugLevel > 0) 536 { 537 addMessage ("Next smaller: " 538 .concat(Integer.toString(colsSmaller)) 539 .concat(" ") 540 .concat(Integer.toString(widthSmaller))); 541 addMessage ("Next larger: " 542 .concat(Integer.toString(colsLarger)) 543 .concat(" ") 544 .concat(Integer.toString(widthLarger))); 545 addMessage("Current window width: " 546 .concat(Integer.toString(myPanel.getWidth()))); 547 addMessage("Trigger heights are " 548 .concat(Integer.toString(heightSmaller)).concat(" ") 549 .concat(Integer.toString(heightCurrent)).concat(" ") 550 .concat(Integer.toString(heightLarger))); 551 } 552 553 try 554 { 555 if (System.getProperty("LOCAL_HOST") == null) 556 { System.setProperty("LOCAL_HOST", 557 sock.getLocalAddress().getHostName());} 558 pw = new PrintWriter(sock.getOutputStream(), 559 true); 560 streamReader = new InputStreamReader(sock.getInputStream(), 561 "8859_1"); 562 in = new BufferedReader(streamReader); 563 // Socket now ready for data transfer 564 /* 565 ** The problem is how to make the application close 566 ** when the socket is closed 567 ** 568 ** The method readLine waits until information is 569 ** available. However, if the connection has been 570 ** closed, it returns a null value. 571 */ 572 while (true) 573 { 574 if (inputSock.isClosed()) 575 { break; } 576 String request = in.readLine(); 577 if (request == null) 578 {break;} 579 topWindow.append(request.concat("\n")); 580 if (logFileActive) 581 { logWriter.println(request); } 582 } 583 if (logFileActive) 584 { 585 logWriter.flush(); 586 logWriter.close(); 587 } 588 } 589 catch (SocketException e) 590 { 591 System.out.println("*** Start of error message"); 592 System.out.println("SocketException while reading data"); 593 System.out.println("Message: ".concat(e.getMessage())); 594 System.out.println("Can be ignored if message is *Socket closed*" 595 .concat(" when socket is closed")); 596 e.printStackTrace(); 597 System.out.println("*** End of error message"); 598 return; 599 } 600 catch (InterruptedIOException e) 601 { 602 System.out.println("*** Start of error message"); 603 System.out.println("InterruptedIOException while reading data"); 604 System.out.println("Message: ".concat(e.getMessage())); 605 e.printStackTrace(); 606 System.out.println("*** End of error message");} 607 catch (Exception e) 608 { 609 System.out.println("*** Start of error message"); 610 System.out.println("unexpected Exception while reading data"); 611 System.out.println("Message: ".concat(e.getMessage())); 612 e.printStackTrace(); 613 } 614 // Test if socket still open 615 if (debugLevel > 1) 616 { System.out.println("Removing frame"); } 617 myFrame.removeAll(); 618 myFrame.removeNotify(); 619 try 620 {if (! sock.isClosed()) 621 {sock.close(); } 622 } 623 catch (Exception e) 624 { 625 System.out.println("*** Start of error message"); 626 System.out.println("Error when closing socket"); 627 System.out.println("Message: ".concat(e.getMessage())); 628 e.printStackTrace(); 629 System.out.println("*** End of error message"); 630 } 631 return; 632 } // end of method 633} // end of class