1 // 2 package server; 3 import java.net.*; 4 import java.io.*; 5 import java.util.*; 6 import java.applet.*; 7 import java.text.*; 8 import dx.protocol.server.*; 9 10 public class DXServer extends Object 11 { 12 13 private static ServerSocket serverSocket = null; 14 private static int java_id = 1; 15 public static Vector actions; 16 private static Vector users; 17 private static Date started_at; 18 private static CleanUpDaemon Cud; 19 private static boolean debug = false; 20 private static String output_dir = null; 21 private static String output_url = null; 22 private static int max_sessions = 1; 23 private static String max_session_str = null; 24 25 // 26 // How long in seconds should we let a copy of dxexec hang around 27 // with no users before killing it. We never flush cache or flush 28 // dictionary because we assume that every copy of dxexec has 29 // important state internally that we would rather not lose. 30 // 31 private final static int DXIdleTime = 600; 32 33 private static Hashtable CachedConns = null; 34 35 // 36 // control (re)reading of the hosts file 37 // 38 private static String HostFileUsed = null; 39 private static long HostFileModified = 0; 40 private static Vector HostNames = null; 41 private static Enumeration HostEnum; 42 DXLSend( long dxl, String value )43 public static native int DXLSend( long dxl, String value ); DXLStartDX( String cmdstr, String host )44 private static native long DXLStartDX( String cmdstr, String host ); DXLExitDX( long dxl )45 private static native int DXLExitDX( long dxl ); 46 getStartDate()47 public static String getStartDate() 48 { 49 return DateFormat.getDateInstance().format(started_at); 50 } 51 getServedCount()52 public static int getServedCount() 53 { 54 return java_id -1; 55 } 56 57 private static int maxThreads; getMaxThreads()58 public static int getMaxThreads() 59 { 60 return DXServer.maxThreads; 61 } 62 63 private final static int Port = 4655; 64 65 final static int MajorVersion = 2; 66 final static int MinorVersion = 0; 67 final static int MicroVersion = 0; 68 69 // 70 // Minor version bumped: 71 // 3: dx.protocol package, frame threads 72 // 4: changed hardcoded strings to class names 73 // 74 75 public final static String VersionString = 76 "" + MajorVersion + "." + MinorVersion + "." + MicroVersion; 77 78 79 public final static int STARTDX = 1; 80 public final static int STATUS = 4; 81 public final static int SHUTDOWN = 5; 82 getOutputDir()83 public static String getOutputDir() 84 { 85 return output_dir; 86 } 87 getOutputUrl()88 public static String getOutputUrl() 89 { 90 return output_url; 91 } 92 93 main( String[] args )94 public static void main( String[] args ) 95 { 96 97 try { 98 serverSocket = new ServerSocket( DXServer.Port ); 99 } 100 101 catch ( IOException e ) { 102 System.out.println( "Could not listen on port: " + DXServer.Port + ", " + e ); 103 System.exit( 1 ); 104 } 105 106 String dbgmsgs = System.getProperty( "DXServer.debug" ); 107 108 if ( dbgmsgs != null ) debug = true; 109 else debug = false; 110 111 users = new Vector( 50 ); 112 113 DXServerThread.ClassInitialize(); 114 115 DXServer.HostFileUsed = null; 116 117 DXServer.HostFileModified = 0; 118 119 DXServer.HostNames = new Vector( 20 ); 120 121 DXServer.HostEnum = null; 122 123 DXServer.ReadHostFile(); 124 125 DXServer.CachedConns = new Hashtable(); 126 127 DXServer.Cud = new CleanUpDaemon(); 128 129 DXServer.Cud.start(); 130 131 DXServer.maxThreads = 1; 132 133 String mxstr = System.getProperty ( "DXServer.dxsessions" ); 134 135 if ( mxstr != null ) { 136 try { 137 DXServer.maxThreads = ( new Integer( mxstr ) ).intValue(); 138 } 139 140 catch ( Exception e ) {} 141 142 } 143 144 output_dir = System.getProperty( "DXServer.outDir" ); 145 146 if ( output_dir == null ) output_dir = "./"; 147 148 output_url = System.getProperty( "DXServer.outUrl" ); 149 150 if ( output_url == null ) output_url = "./"; 151 152 if ( ( output_dir == null ) || ( output_url == null ) ) { 153 System.out.println ( "DXServer requires output file name and url." ); 154 System.exit( 1 ); 155 } 156 157 actions = new Vector( 10 ); 158 159 try { 160 actions.addElement ( ( Object ) 161 new ServerCommand( startMsg.GetCommand(), DXServer.STARTDX ) ); 162 actions.addElement ( ( Object ) 163 new ServerCommand( statusMsg.GetCommand(), DXServer.STATUS ) ); 164 actions.addElement ( ( Object ) 165 new ServerCommand( shutdownMsg.GetCommand(), DXServer.SHUTDOWN ) ); 166 } 167 168 catch ( ClassNotFoundException cnfe ) { 169 cnfe.printStackTrace(); 170 System.exit( 1 ); 171 } 172 173 System.out.println( "Starting DXServer... ready for clients" ); 174 started_at = new Date(); 175 176 while ( true ) { 177 try { 178 Socket clientSocket = serverSocket.accept(); 179 ConnectThread ct = new ConnectThread( clientSocket ); 180 ct.start(); 181 ct = null; 182 } 183 184 catch ( IOException e ) { 185 System.out.println( "Accept failed: " + DXServer.Port + ", " + e ); 186 break; 187 } 188 189 catch ( Exception e ) { 190 System.out.println( "Unknown error" ); 191 break; 192 } 193 } 194 195 shutdown ( null, null ); 196 197 } 198 versionMatch( String client_version )199 public static boolean versionMatch ( String client_version ) 200 { 201 boolean retval = false; 202 203 serverVersionMsg svm = new serverVersionMsg( client_version ); 204 205 // 206 // We require a server whose Major,Minor are at least what ours are 207 // If Micro is less than ours, we'll proceed. 208 // 209 210 if ( ( svm.getMajor() >= DXServer.MajorVersion ) && 211 ( svm.getMinor() >= DXServer.MinorVersion ) ) 212 retval = true; 213 214 if ( debug ) { 215 System.out.println ( "DXServer: versionMatch(" + retval + ") " + 216 client_version ); 217 } 218 219 return retval; 220 } 221 getUserCount()222 public synchronized static int getUserCount() 223 { 224 return users.size(); 225 } 226 startDX( dx.protocol.server.serverMsg msg, Socket csock )227 public synchronized static boolean startDX( dx.protocol.server.serverMsg msg, Socket csock ) 228 { 229 if ( debug ) { 230 System.out.println ( "DXServer: " + msg.toString() ); 231 } 232 233 DXServerThread dxst = new DXServerThread( csock, java_id ); 234 dxst.start(); 235 users.addElement( ( Object ) dxst ); 236 java_id++; 237 return true; 238 } 239 status( dx.protocol.server.serverMsg msg, Socket csock )240 public static boolean status ( dx.protocol.server.serverMsg msg, Socket csock ) 241 { 242 StatusThread st = new StatusThread( csock ); 243 st.start(); 244 return true; 245 } 246 shutdown( dx.protocol.server.serverMsg msg, Socket csock )247 public static boolean shutdown ( dx.protocol.server.serverMsg msg, Socket csock ) 248 { 249 if ( debug ) 250 System.out.println ( "DXServer: " + msg.toString() ); 251 252 // 253 // DXServer should always go this way when exiting. It cleans up files. 254 // 255 DXServer.Cud.interrupt(); 256 257 DXServer.CleanUp(); 258 259 Enumeration enum1 = users.elements(); 260 261 while ( enum1.hasMoreElements() ) { 262 DXServerThread dxst = ( DXServerThread ) enum1.nextElement(); 263 264 if ( dxst.isAlive() == true ) { 265 if ( debug ) 266 System.out.println ( " ***** DXServer: requesting thread shutdown" ); 267 268 dxst.cleanUp(); 269 270 dxst.interrupt(); 271 } 272 } 273 274 // 275 // don't return from this point. 276 // 277 System.exit( 0 ); 278 279 return true; 280 } 281 282 // 283 // Check every thread for alive-ness 284 // 285 CleanUp()286 public synchronized static void CleanUp() 287 { 288 Vector dead_users; 289 dead_users = new Vector( 10 ); 290 Enumeration enum1 = users.elements(); 291 292 while ( enum1.hasMoreElements() ) { 293 DXServerThread old_dxst = ( DXServerThread ) enum1.nextElement(); 294 295 if ( old_dxst.isAlive() == false ) 296 dead_users.addElement( old_dxst ); 297 } 298 299 enum1 = dead_users.elements(); 300 301 while ( enum1.hasMoreElements() ) { 302 DXServerThread dead_user = ( DXServerThread ) enum1.nextElement(); 303 users.removeElement( ( Object ) dead_user ); 304 } 305 306 synchronized ( DXServer.CachedConns ) { 307 Vector killed = new Vector( 4 ); 308 enum1 = DXServer.CachedConns.elements(); 309 310 while ( enum1.hasMoreElements() ) { 311 ConnectionEntry existing = ( ConnectionEntry ) enum1.nextElement(); 312 313 if ( existing.getUserCount() == 0 ) { 314 if ( existing.getIdleTime() > DXServer.DXIdleTime ) { 315 Long dxlcon = existing.getDXLConnection(); 316 DXServer.DXLExitDX ( dxlcon.longValue() ); 317 killed.addElement( dxlcon ); 318 319 if ( debug ) 320 System.out.println ( "DXServer: terminate Data Explorer session" ); 321 } 322 } 323 } 324 325 enum1 = killed.elements(); 326 327 while ( enum1.hasMoreElements() ) { 328 Long dxlcon = ( Long ) enum1.nextElement(); 329 ConnectionEntry ce = ( ConnectionEntry ) DXServer.CachedConns.get( dxlcon ); 330 ce.deleteFiles(); 331 DXServer.CachedConns.remove( dxlcon ); 332 } 333 334 killed.removeAllElements(); 335 } 336 } 337 338 GetThread( int java_id )339 public synchronized static DXServerThread GetThread( int java_id ) 340 { 341 DXServerThread dxst = null; 342 Enumeration enum1 = users.elements(); 343 344 while ( enum1.hasMoreElements() ) { 345 dxst = ( DXServerThread ) enum1.nextElement(); 346 347 if ( dxst.isAlive() ) { 348 int id = dxst.getIdDX(); 349 350 if ( id == java_id ) 351 break; 352 } 353 } 354 355 return dxst; 356 } 357 EndConnection( Long dxl, DXServerThread dxst )358 public static void EndConnection( Long dxl, DXServerThread dxst ) 359 { 360 synchronized ( DXServer.CachedConns ) { 361 ConnectionEntry ce = ( ConnectionEntry ) DXServer.CachedConns.get( dxl ); 362 ce.detachUser( dxst ); 363 } 364 } 365 createNet( String path, DXServerThread dxst )366 public synchronized static String createNet( String path, DXServerThread dxst ) 367 { 368 ConnectionEntry ce = ( ConnectionEntry ) CachedConns.get( dxst.getDXLConnection() ); 369 return ce.getNetName( dxst ); 370 } 371 LockConnection( DXServerThread dxst )372 public static void LockConnection( DXServerThread dxst ) 373 { 374 ConnectionEntry ce = ( ConnectionEntry ) CachedConns.get( dxst.getDXLConnection() ); 375 ce.getConnectionLock(); 376 } 377 UnlockConnection( DXServerThread dxst )378 public static void UnlockConnection( DXServerThread dxst ) 379 { 380 ConnectionEntry ce = ( ConnectionEntry ) CachedConns.get( dxst.getDXLConnection() ); 381 ce.releaseConnectionLock(); 382 } 383 NewConnection( DXServerThread dxst, String path )384 public static Long NewConnection( DXServerThread dxst, String path ) 385 { 386 ConnectionEntry ce = null; 387 388 synchronized ( DXServer.CachedConns ) { 389 if ( DXServer.CachedConns.size() > 0 ) { 390 ConnectionEntry minEntry = null; 391 Enumeration enum1 = CachedConns.elements(); 392 393 while ( enum1.hasMoreElements() ) { 394 ConnectionEntry existing = ( ConnectionEntry ) enum1.nextElement(); 395 396 if ( existing.isFailed() ) {} 397 else if ( minEntry == null ) { 398 minEntry = existing; 399 } 400 401 else if ( existing.getUserCount() < minEntry.getUserCount() ) { 402 minEntry = existing; 403 } 404 405 else if ( existing.getUserCount() == minEntry.getUserCount() ) { 406 if ( existing.getUserTime() < minEntry.getUserTime() ) { 407 minEntry = existing; 408 } 409 } 410 411 if ( ( minEntry != null ) && ( minEntry.getUserCount() == 0 ) ) 412 break; 413 } 414 415 ce = minEntry; 416 } 417 418 boolean more_capacity = false; 419 420 if ( DXServer.GetMaxSessions() > DXServer.CachedConns.size() ) 421 more_capacity = true; 422 423 if ( ( ce != null ) && ( ce.getUserCount() >= 1 ) && ( more_capacity ) ) 424 ce = null; 425 426 if ( ce == null ) { 427 String host = DXServer.GetNextHost(); 428 long dxlcon = 0; 429 try { 430 dxlcon = 431 DXServer.DXLStartDX( "dx -optimize memory -execonly -processors 1 -highlight off", host ); 432 } catch (Exception e) { 433 System.out.println ( "Problem starting one of your hosts." ); 434 System.out.println ("Startup was: dx -optimize memory -execonly -processors 1 -highlight off :" + host); 435 System.exit( 1 ); 436 } 437 if (dxlcon == 0) { 438 System.out.println ( "Problem starting one of your hosts." ); 439 System.out.println ("Startup was: dx -optimize memory -execonly -processors 1 -highlight off :" + host); 440 System.exit( 1 ); 441 } 442 443 Long C = new Long( dxlcon ); 444 ce = new ConnectionEntry( C ); 445 CachedConns.put( C, ce ); 446 } 447 448 if ( ce == null ) return null; 449 450 ce.attachUser( dxst, path ); 451 } 452 453 return ce.getDXLConnection(); 454 } 455 GetMaxSessions()456 public static int GetMaxSessions() 457 { 458 if ( DXServer.max_session_str == null ) { 459 DXServer.max_session_str = System.getProperty( "DXServer.max_sessions" ); 460 461 if ( DXServer.max_session_str == null ) DXServer.max_session_str = "1"; 462 463 try { 464 Integer I = new Integer( DXServer.max_session_str ); 465 DXServer.max_sessions = I.intValue(); 466 } 467 468 catch ( NumberFormatException nfe ) { 469 DXServer.max_session_str = "1"; 470 DXServer.max_sessions = 1; 471 } 472 } 473 474 return DXServer.max_sessions; 475 } 476 477 // 478 // Read the contents of dxserver.hosts 479 // Look in: 480 // 1) /etc 481 // 2) current working dir 482 // 3) environment variable DXServer.hostsFile 483 // Don't look for multiple copies of the file. Stop after reading the first one found. 484 // N.B. The caller is responsible for locking before both of these class methods. 485 // GetNextHost()486 private static String GetNextHost() 487 { 488 if ( DXServer.HostEnum.hasMoreElements() == false ) 489 DXServer.HostEnum = HostNames.elements(); 490 491 return ( String ) DXServer.HostEnum.nextElement(); 492 } 493 ReadHostFile()494 private static void ReadHostFile() 495 { 496 boolean file_found = false; 497 boolean file_read = false; 498 Vector paths; 499 500 paths = new Vector( 5 ); 501 502 if ( DXServer.HostFileUsed != null ) 503 paths.addElement( ( Object ) DXServer.HostFileUsed ); 504 505 paths.addElement( ( Object ) File.separator + "etc" + File.separator + "dxserver.hosts" ); 506 507 paths.addElement( ( Object ) "." + File.separator + "dxserver.hosts" ); 508 509 paths.addElement( ( Object ) System.getProperty( "DXServer.hostsFile" ) ); 510 511 synchronized ( DXServer.HostNames ) { 512 try { 513 514 Enumeration enum1 = paths.elements(); 515 516 while ( ( !file_read ) && ( enum1.hasMoreElements() ) ) { 517 String path = ( String ) enum1.nextElement(); 518 File hf = new File( path ); 519 520 if ( hf.exists() == false ) continue; 521 522 if ( hf.canRead() == false ) continue; 523 524 if ( hf.isFile() == false ) continue; 525 526 file_found = true; 527 528 long modified = hf.lastModified(); 529 530 // 531 // If we've already read an up-to-date hosts file, then 532 // do nothing. 533 // 534 if ( ( DXServer.HostFileUsed != null ) && 535 ( DXServer.HostFileUsed.equals( path ) ) && 536 ( DXServer.HostFileModified >= modified ) ) 537 break; 538 539 DXServer.HostNames.removeAllElements(); 540 541 FileReader hfr = new FileReader( hf ); 542 543 BufferedReader hbr = new BufferedReader ( hfr ); 544 545 String host; 546 547 while ( ( host = hbr.readLine() ) != null ) { 548 host = host.trim(); 549 550 if ( host.startsWith( "//" ) ) continue; 551 552 if ( host.startsWith( "!#" ) ) continue; 553 554 if ( host.startsWith( "#" ) ) continue; 555 556 if ( host.length() == 0) continue; 557 558 HostNames.addElement( ( Object ) host ); 559 } 560 561 DXServer.HostFileUsed = hf.getPath(); 562 DXServer.HostFileModified = modified; 563 file_read = true; 564 } 565 } 566 567 catch ( Exception e ) { 568 e.printStackTrace(); 569 } 570 571 if ( !file_found ) { 572 DXServer.HostFileUsed = null; 573 DXServer.HostNames.removeAllElements(); 574 } 575 576 if ( DXServer.HostNames.size() == 0 ) 577 DXServer.HostNames.addElement( ( Object ) "localhost" ); 578 579 if ( ( !file_found ) || ( file_read ) ) 580 DXServer.HostEnum = DXServer.HostNames.elements(); 581 } 582 } 583 } // end DXServer 584 585 586 587