001 /* 002 * Copyright 2004-2005 Stephen J. McConnell. 003 * Copyright 2004 Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. 015 * 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 package net.dpml.transit; 021 022 import java.io.File; 023 import java.io.IOException; 024 import java.io.BufferedReader; 025 import java.io.InputStreamReader; 026 027 import java.util.Properties; 028 import java.util.Enumeration; 029 030 031 /** 032 * Encapsulates operating system and shell specific access to environment 033 * variables. 034 * 035 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 036 * @version 1.0.3 037 */ 038 public class Environment extends Properties 039 { 040 /** 041 * os.name System property 042 */ 043 public static final String OSNAME = System.getProperty( "os.name" ); 044 045 /** 046 * user.name System property 047 */ 048 public static final String USERNAME = System.getProperty( "user.name" ); 049 050 /** 051 * the user's platform specific shell executable 052 */ 053 private static String m_SHELL = null; 054 055 /** 056 * the last Env instance created 057 */ 058 private static Environment m_INSTANCE = null; 059 060 /** 061 * Creates a snapshot of the current shell environment variables for a user. 062 * 063 * @throws EnvironmentException if there is an error accessing the environment 064 */ 065 public Environment() throws EnvironmentException 066 { 067 Properties properties = getEnvVariables(); 068 Enumeration list = properties.propertyNames(); 069 while ( list.hasMoreElements() ) 070 { 071 String key = ( String ) list.nextElement(); 072 setProperty( key, properties.getProperty( key ) ); 073 } 074 m_INSTANCE = this; 075 } 076 077 /** 078 * Gets a copy of the last Environment instance without parsing the user's shell 079 * environment. Use this method if you do not want to reparse the 080 * environment every time an environment variable is accessed. If an 081 * environment has not been created yet one is created then cloned 082 * and a copy is returned instead of returning null. 083 * 084 * @return a copy of the last Environment object created 085 * @throws EnvironmentException if there is an error accessing the environment 086 */ 087 Environment getLastEnv() throws EnvironmentException 088 { 089 if( m_INSTANCE == null ) 090 { 091 m_INSTANCE = new Environment(); 092 } 093 094 // return cloned copy so there is no cross interference 095 return ( Environment ) m_INSTANCE.clone(); 096 } 097 098 099 /** 100 * Gets the value of a shell environment variable. 101 * 102 * @param name the name of variable 103 * @return the String representation of an environment variable value 104 * @throws EnvironmentException if there is a problem accessing the environment 105 */ 106 public static String getEnvVariable( String name ) 107 throws EnvironmentException 108 { 109 String osName = System.getProperty( "os.name" ); 110 111 if( isUnix() ) 112 { 113 Properties properties = getUnixShellVariables(); 114 return properties.getProperty( name ); 115 } 116 else if( isWindows() ) 117 { 118 return getWindowsShellVariable( name ); 119 } 120 121 throw new EnvironmentException( name, 122 "Unrecognized operating system: " + osName ); 123 } 124 125 /** 126 * Checks to see if the operating system is a UNIX variant. 127 * 128 * @return true of the OS is a UNIX variant, false otherwise 129 */ 130 public static boolean isUnix() 131 { 132 if( -1 != OSNAME.indexOf( "Linux" ) 133 || -1 != OSNAME.indexOf( "SunOS" ) 134 || -1 != OSNAME.indexOf( "Solaris" ) 135 || -1 != OSNAME.indexOf( "MPE/iX" ) 136 || -1 != OSNAME.indexOf( "AIX" ) 137 || -1 != OSNAME.indexOf( "FreeBSD" ) 138 || -1 != OSNAME.indexOf( "Irix" ) 139 || -1 != OSNAME.indexOf( "Digital Unix" ) 140 || -1 != OSNAME.indexOf( "HP-UX" ) 141 || -1 != OSNAME.indexOf( "Mac OS X" ) ) 142 { 143 return true; 144 } 145 146 return false; 147 } 148 149 150 /** 151 * Checks to see if the operating system is a Windows variant. 152 * 153 * @return true of the OS is a Windows variant, false otherwise 154 */ 155 public static boolean isWindows() 156 { 157 return ( -1 != OSNAME.indexOf( "Windows" ) ); 158 } 159 160 /** 161 * Checks to see if the operating system is NetWare. 162 * 163 * @return true of the OS is NetWare, false otherwise 164 */ 165 public static boolean isNetWare() 166 { 167 return ( -1 != OSNAME.indexOf( "netware" ) ); 168 } 169 170 /** 171 * Checks to see if the operating system is OpenVMS. 172 * 173 * @return true of the OS is a NetWare variant, false otherwise 174 */ 175 public static boolean isOpenVMS() 176 { 177 return ( -1 != OSNAME.indexOf( "openvms" ) ); 178 } 179 180 /** 181 * Gets all environment variables within a Properties instance where the 182 * key is the environment variable name and value is the value of the 183 * property. 184 * 185 * @return the environment variables and values as Properties 186 * @throws EnvironmentException if os is not recognized 187 */ 188 public static Properties getEnvVariables() throws EnvironmentException 189 { 190 if( isUnix() ) 191 { 192 return getUnixShellVariables(); 193 } 194 195 if( isWindows() ) 196 { 197 return getWindowsShellVariables(); 198 } 199 200 throw new EnvironmentException( 201 new UnsupportedOperationException( "Environment operations not " 202 + "supported on unrecognized operatings system" ) ); 203 } 204 205 206 /** 207 * Gets the user's shell executable. 208 * 209 * @return the shell executable for the user 210 * @throws EnvironmentException the there is a problem accessing shell 211 * information 212 */ 213 public static String getUserShell() throws EnvironmentException 214 { 215 if( -1 != OSNAME.indexOf( "Mac OS X" ) ) 216 { 217 return getMacUserShell(); 218 } 219 220 if( isWindows() ) 221 { 222 return getWindowsUserShell(); 223 } 224 225 throw new EnvironmentException( 226 new UnsupportedOperationException( "Environment operations not " 227 + "supported on unrecognized operatings system" ) ); 228 } 229 230 // ------------------------------------------------------------------------ 231 // Private UNIX Shell Operations 232 // ------------------------------------------------------------------------ 233 234 /** 235 * Gets the default login shell used by a mac user. 236 * 237 * @return the Mac user's default shell as referenced by cmd: 238 * 'nidump passwd /' 239 * @throws EnvironmentException if os information is not resolvable 240 */ 241 private static String getMacUserShell() 242 throws EnvironmentException 243 { 244 Process process = null; 245 BufferedReader reader = null; 246 247 if( null != m_SHELL ) 248 { 249 return m_SHELL; 250 } 251 252 try 253 { 254 String entry = null; 255 String [] args = {"nidump", "passwd", "/"}; 256 process = Runtime.getRuntime().exec( args ); 257 reader = new BufferedReader( 258 new InputStreamReader( process.getInputStream() ) ); 259 260 while ( null != ( entry = reader.readLine() ) ) 261 { 262 // Skip entries other than the one for this username 263 if( !entry.startsWith( USERNAME ) ) 264 { 265 continue; 266 } 267 268 // Get the shell part of the passwd entry 269 int index = entry.lastIndexOf( ':' ); 270 271 if( index == -1 ) 272 { 273 throw new EnvironmentException( 274 "passwd database contains malformed user entry for " 275 + USERNAME ); 276 } 277 278 m_SHELL = entry.substring( index + 1 ); 279 return m_SHELL; 280 } 281 282 process.waitFor(); 283 reader.close(); 284 } 285 catch( Throwable t ) 286 { 287 throw new EnvironmentException( t ); 288 } 289 finally 290 { 291 if( process != null ) 292 { 293 process.destroy(); 294 } 295 296 try 297 { 298 if( null != reader ) 299 { 300 reader.close(); 301 } 302 } 303 catch( IOException e ) 304 { 305 // do nothing 306 final boolean ignorable = true; 307 } 308 } 309 310 throw new EnvironmentException( "User " + USERNAME 311 + " is not present in the passwd database" ); 312 } 313 314 315 /** 316 * Adds a set of Windows variables to a set of properties. 317 * @return the environment properties 318 * @exception EnvironmentException if an error occurs 319 */ 320 private static Properties getUnixShellVariables() 321 throws EnvironmentException 322 { 323 Process process = null; 324 Properties properties = new Properties(); 325 326 // Read from process here 327 BufferedReader reader = null; 328 329 // fire up the shell and get echo'd results on stdout 330 try 331 { 332 String [] args = {getUnixEnv()}; 333 process = Runtime.getRuntime().exec( args ); 334 reader = new BufferedReader( 335 new InputStreamReader( process.getInputStream() ) ); 336 337 String line = null; 338 while ( null != ( line = reader.readLine() ) ) 339 { 340 int index = line.indexOf( '=' ); 341 342 if( -1 == index ) 343 { 344 if( line.length() != 0 ) 345 { 346 System.err.println( 347 "Skipping line - could not find '=' in" 348 + " line: '" + line + "'" ); 349 } 350 continue; 351 } 352 353 String name = line.substring( 0, index ); 354 String value = line.substring( index + 1, line.length() ); 355 properties.setProperty( name, value ); 356 } 357 358 process.waitFor(); 359 reader.close(); 360 } 361 catch( Throwable t ) 362 { 363 throw new EnvironmentException( "NA", t ); 364 } 365 finally 366 { 367 process.destroy(); 368 369 try 370 { 371 if( null != reader ) 372 { 373 reader.close(); 374 } 375 } 376 catch( IOException e ) 377 { 378 // ignore 379 final boolean ignorable = true; 380 } 381 } 382 383 // Check that we exited normally before returning an invalid output 384 if( 0 != process.exitValue() ) 385 { 386 throw new EnvironmentException( 387 "Environment process failed " 388 + " with non-zero exit code of " 389 + process.exitValue() ); 390 } 391 392 return properties; 393 } 394 395 396 /** 397 * Gets the UNIX env executable path. 398 * 399 * @return the absolute path to the env program 400 * @throws EnvironmentException if it cannot be found 401 */ 402 private static String getUnixEnv() throws EnvironmentException 403 { 404 File env = new File( "/bin/env" ); 405 406 if( env.exists() && env.canRead() && env.isFile() ) 407 { 408 return env.getAbsolutePath(); 409 } 410 411 env = new File( "/usr/bin/env" ); 412 if( env.exists() && env.canRead() && env.isFile() ) 413 { 414 return env.getAbsolutePath(); 415 } 416 417 throw new EnvironmentException( 418 "Could not find the UNIX env executable" ); 419 } 420 421 422 // ------------------------------------------------------------------------ 423 // Private Windows Shell Operations 424 // ------------------------------------------------------------------------ 425 426 427 /** 428 * Gets the shell used by the Windows user. 429 * 430 * @return the shell: cmd.exe or command.com. 431 */ 432 private static String getWindowsUserShell() 433 { 434 if( null != m_SHELL ) 435 { 436 return m_SHELL; 437 } 438 439 if( -1 != OSNAME.indexOf( "98" ) 440 || -1 != OSNAME.indexOf( "95" ) 441 || -1 != OSNAME.indexOf( "Me" ) ) 442 { 443 m_SHELL = "command.com"; 444 return m_SHELL; 445 } 446 447 m_SHELL = "cmd.exe"; 448 return m_SHELL; 449 } 450 451 452 /** 453 * Adds a set of Windows variables to a set of properties. 454 * @return the environment properties 455 * @exception EnvironmentException if an error occurs 456 */ 457 private static Properties getWindowsShellVariables() 458 throws EnvironmentException 459 { 460 String line = null; 461 Process process = null; 462 BufferedReader reader = null; 463 Properties properties = new Properties(); 464 465 // build the command based on the shell used: cmd.exe or command.com 466 StringBuffer buffer = new StringBuffer( getWindowsUserShell() ); 467 buffer.append( " /C SET" ); 468 469 // fire up the shell and get echo'd results on stdout 470 try 471 { 472 process = Runtime.getRuntime().exec( buffer.toString() ); 473 reader = new BufferedReader( 474 new InputStreamReader( process.getInputStream() ) ); 475 while ( null != ( line = reader.readLine() ) ) 476 { 477 int index = line.indexOf( '=' ); 478 479 if( -1 == index ) 480 { 481 System.err.println( "Skipping line - could not find '=' in" 482 + " line: '" + line + "'" ); 483 continue; 484 } 485 486 String name = line.substring( 0, index ); 487 String value = line.substring( index + 1, line.length() ); 488 properties.setProperty( name, value ); 489 } 490 491 process.waitFor(); 492 reader.close(); 493 } 494 catch( Throwable t ) 495 { 496 throw new EnvironmentException( t ); 497 } 498 finally 499 { 500 process.destroy(); 501 502 try 503 { 504 if( null != reader ) 505 { 506 reader.close(); 507 } 508 } 509 catch( IOException e ) 510 { 511 // ignore 512 final boolean ignorable = true; 513 } 514 } 515 516 if( 0 != process.exitValue() ) 517 { 518 throw new EnvironmentException( "Environment process failed" 519 + " with non-zero exit code of " + process.exitValue() ); 520 } 521 522 return properties; 523 } 524 525 526 /** 527 * Gets the value for a windows command shell environment variable. 528 * 529 * @param name the name of the variable 530 * @return the value of the variable 531 * @throws EnvironmentException if there is an error accessing the value 532 */ 533 private static String getWindowsShellVariable( String name ) 534 throws EnvironmentException 535 { 536 String value = null; 537 Process process = null; 538 BufferedReader reader = null; 539 540 StringBuffer buffer = new StringBuffer( getWindowsUserShell() ); 541 buffer.append( " /C echo %" ); 542 buffer.append( name ); 543 buffer.append( '%' ); 544 545 // fire up the shell and get echo'd results on stdout 546 try 547 { 548 process = Runtime.getRuntime().exec( buffer.toString() ); 549 reader = new BufferedReader( 550 new InputStreamReader( process.getInputStream() ) ); 551 value = reader.readLine(); 552 process.waitFor(); 553 reader.close(); 554 } 555 catch( Throwable t ) 556 { 557 throw new EnvironmentException( name, t ); 558 } 559 finally 560 { 561 process.destroy(); 562 563 try 564 { 565 if( null != reader ) 566 { 567 reader.close(); 568 } 569 } 570 catch( IOException e ) 571 { 572 // ignore 573 final boolean ignorable = true; 574 } 575 } 576 577 if( 0 == process.exitValue() ) 578 { 579 // Handle situations where the env property does not exist. 580 if( value.startsWith( "%" ) && value.endsWith( "%" ) ) 581 { 582 return null; 583 } 584 585 return value; 586 } 587 588 throw new EnvironmentException( 589 name, 590 "Environment process failed" 591 + " with non-zero exit code of " 592 + process.exitValue() ); 593 } 594 }