001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Elmar Grom 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package com.izforge.izpack.util; 023 024import java.io.BufferedReader; 025import java.io.File; 026import java.io.InputStream; 027import java.io.InputStreamReader; 028import java.util.StringTokenizer; 029 030/*---------------------------------------------------------------------------*/ 031/** 032 * The <code>TargetFactory</code> serves as a central mechanism to instantiate OS specific class 033 * flavors, provide OS specific file extension types, default install directories and similar 034 * functionality. In addition it provides services that are related to OS versions and flavors. For 035 * a tutorial on using some of the features in this class see the <A 036 * HREF=doc-files/TargetFactory.html>TargetFactory Tutorial</A>. 037 * 038 * @version 0.0.1 / 1/3/2002 039 * @author Elmar Grom 040 */ 041/*---------------------------------------------------------------------------*/ 042/* 043 * $ @design 044 * 045 * Reports actually observed on some systems: 046 * 047 * OS OS Name Version Architecture Native Report (ver) 048 * ---------------------------------------------------------------------------------------------------------- 049 * Windows 95 Windows 98 Windows 98 4.10 x86 Windows 98 [Version 4.10.1998] Windows-ME Windows Me 050 * 4.90 x86 Windows Millennium [Version 4.90.3000] Windows-NT 3.5 Windows-NT 4.0 Windows NT 4.0 x86 051 * Windows NT Version 4.0 Windows 2000 Windows 2000 5.0 x86 Microsoft Windows 2000 [Version 052 * 5.00.2195] Windows-XP Windows 2000 5.1 x86 Microsoft Windows XP [Version 5.1.2600] Windows-XP 053 * Windows XP 5.1 x86 Mac Mac OS-X Linux Linux 2.4.7-10 i386 Linux Linux 2.4.18-4GB i386 Solaris 054 * 055 * --------------------------------------------------------------------------- 056 */ 057public class TargetFactory 058{ 059 060 // ------------------------------------------------------------------------ 061 // Constant Definitions 062 // ------------------------------------------------------------------------ 063 064 // Basic operating systems 065 066 /** Identifies Microsoft Windows. */ 067 public static final int WINDOWS = 0; 068 069 /** Identifies generic UNIX operating systems */ 070 public static final int UNIX = 2; 071 072 /** Used to report a non specific operating system. */ 073 public static final int GENERIC = 3; 074 075 // operating system favors 076 077 /** This is the basic flavor for every operating system. */ 078 public static final int STANDARD = 0; 079 080 /** 081 * Used to identify the Windows-NT class of operating systems in terms of an OS flavor. It is 082 * reported for Windows-NT, 2000 and XP. 083 */ 084 public static final int NT = 1; 085 086 /** Used to identify the OS X flavor of the Mac OS */ 087 public static final int X = 2; 088 089 // system architecture 090 091 /** Identifies Intel X86 based processor types. */ 092 public static final int X86 = 0; 093 094 /** Nonspecific processor architecture, other than X86. */ 095 public static final int OTHER = 1; 096 097 /** 098 * The extensions used for native libraries on various operating systems. The string positions 099 * correspond to the basic operating system indexes. The following values are legal to use : 100 * <br> 101 * <br> 102 * <ul> 103 * <li>WINDOWS 104 * <li>MAC 105 * <li>UNIX 106 * <li>GENERIC 107 * </ul> 108 */ 109 static final String[] LIBRARY_EXTENSION = { "dll", "so", "", ""}; 110 111 /** 112 * The os specific class prefixes for classes that implement different versions for the various 113 * operating systems. The string positions correspond to the basic operating system indexes. The 114 * following values are legal to use : <br> 115 * <br> 116 * <ul> 117 * <li>WINDOWS 118 * <li>MAC 119 * <li>UNIX 120 * <li>GENERIC 121 * </ul> 122 */ 123 static final String[] CLASS_PREFIX = { "Win_", "Mac_", "Unix_", ""}; 124 125 /** 126 * The os favor specific class prefixes for classes the implement different versions for various 127 * os favors. The string positions correspond to the flavor indexes. The following values are 128 * legal to use : <br> 129 * <br> 130 * <ul> 131 * <li>STANDARD 132 * <li>NT 133 * <li>X 134 * </ul> 135 */ 136 static final String[] CLASS_FLAVOR_PREFIX = { "", "NT_", "X_"}; 137 138 /** 139 * The list of processor architecture specific prefixes. The string positions correspond to the 140 * architecture indexes. The following values are leegal to use : <br> 141 * <br> 142 * <ul> 143 * <li>X86 144 * <li>OTHER 145 * </ul> 146 */ 147 static final String[] CLASS_ARCHITECTURE_PREFIX = { "X86_", // Intel X86 148 // architecture 149 "U_" // unknown 150 }; 151 152 /** 153 * The list of default install path fragments. Depending on the operating system, a path 154 * fragment might represent either a part of the default install path or the entire path to use. 155 * For MS-Windows it is always only a part of the full install path. The string positions 156 * correspond to the basic operating system indexes. The following values are leegal to use : 157 * <br> 158 * <br> 159 * <ul> 160 * <li>WINDOWS 161 * <li>MAC 162 * <li>UNIX 163 * <li>GENERIC 164 * </ul> 165 */ 166 static final String[] INSTALL_PATH_FRAGMENT = { "Program Files" + File.separator, 167 "/Applications" + File.separator, "/usr/local" + File.separator, 168 File.separator + "apps" + File.separator}; 169 170 /** 171 * This is a list of keys to use when looking for resources that define the default install path 172 * to use. The list is organized as two dimensional array of <code>String</code>s. To access 173 * the array, denote the first dimension with the operating system index and the second 174 * dimension with the flavor index. For example to access the key for Windows-NT use 175 * <code>INSTALL_PATH_RESOURCE_KEY[WINDOWS][NT]</code> The array uses a sparse population, 176 * that is, not all array locations actually contain a key. Only locations for which a real 177 * operating system/flavor combination exists are populated. For example, there is no such thing 178 * as <code>INSTALL_PATH_RESOURCE_KEY[UNIX][X]</code> 179 */ 180 static final String[][] INSTALL_PATH_RESOURCE_KEY = { 181 // Standard NT X 182 { "TargetPanel.dir.windows", "TargetPanel.dir.windows", ""}, // Windows 183 { "TargetPanel.dir.mac", "", "TargetPanel.dir.macosx"}, // Mac 184 { "TargetPanel.dir.unix", "", ""}, // UNIX 185 { "TargetPanel.dir", "", ""} // Generic 186 }; 187 188 /** The delimiter characters used to tokenize version numbers */ 189 private static final String VERSION_DELIMITER = ".-"; 190 191 // ------------------------------------------------------------------------ 192 // Variable Declarations 193 // ------------------------------------------------------------------------ 194 /** 195 * The reference to the single instance of <code>TargetFactory</code>. Used in static methods 196 * in place of <code>this</code>. 197 */ 198 private static TargetFactory me = null; 199 200 /** identifies the operating system we are running on */ 201 private int os = -1; 202 203 /** identifies the operating system favor */ 204 private int osFlavor = -1; 205 206 /** identifies the hardware architecture we are running on */ 207 private int architecture = -1; 208 209 /** represents the version number of the target system */ 210 private String version = ""; 211 212 /*--------------------------------------------------------------------------*/ 213 /** 214 * Constructor 215 */ 216 /*--------------------------------------------------------------------------*/ 217 /* 218 * $ @design 219 * 220 * Identify the following about the target system: - OS type - architecture - version 221 * 222 * and store this information for later use. 223 * -------------------------------------------------------------------------- 224 */ 225 private TargetFactory() 226 { 227 version = System.getProperty("os.version"); 228 229 // ---------------------------------------------------- 230 // test for Windows 231 // ---------------------------------------------------- 232 if (OsVersion.IS_WINDOWS) 233 { 234 os = WINDOWS; 235 osFlavor = STANDARD; 236 architecture = X86; 237 String osName = OsVersion.OS_NAME.toLowerCase(); 238 239 if (osName.indexOf("nt") > -1) 240 { 241 osFlavor = NT; 242 } 243 else if (osName.indexOf("2000") > -1) 244 { 245 osFlavor = NT; 246 } 247 else if (osName.indexOf("xp") > -1) 248 { 249 osFlavor = NT; 250 } 251 } 252 // ---------------------------------------------------- 253 // test for Mac OS 254 // ---------------------------------------------------- 255 else if (OsVersion.IS_OSX) 256 { 257 os = X; 258 osFlavor = STANDARD; 259 architecture = OTHER; 260 } 261 // ---------------------------------------------------- 262 // what's left should be unix 263 // ---------------------------------------------------- 264 else 265 { 266 os = UNIX; 267 osFlavor = STANDARD; 268 architecture = OTHER; 269 String osName = OsVersion.OS_NAME.toLowerCase(); 270 271 if (osName.indexOf("x86") > -1) 272 { 273 architecture = X86; 274 } 275 } 276 } 277 278 /*--------------------------------------------------------------------------*/ 279 /** 280 * Returns an instance of <code>TargetFactory</code> to use. 281 * 282 * @return an instance of <code>TargetFactory</code>. 283 */ 284 /*--------------------------------------------------------------------------*/ 285 public static TargetFactory getInstance() 286 { 287 if (me == null) 288 { 289 me = new TargetFactory(); 290 } 291 292 return me; 293 } 294 295 /*--------------------------------------------------------------------------*/ 296 /** 297 * This method returns an OS and OS flavor specific instance of the requested class. <br> 298 * <br> 299 * <b>Class Naming Rules</b><br> 300 * Class versions must be named with the OS and OS flavor as prefix. The prefixes are simply 301 * concatenated, with the OS prefix first and the flavor prefix second. Use the following OS 302 * specific prefixes:<br> 303 * <br> 304 * <TABLE BORDER=1> 305 * <TR> 306 * <TH>Operating System</TH> 307 * <TH>Prefix</TH> 308 * </TR> 309 * <TR> 310 * <TD>Microsoft Windows</TD> 311 * <TD>Win_</TD> 312 * </TR> 313 * <TR> 314 * <TD>Mac OS</TD> 315 * <TD>Mac_</TD> 316 * </TR> 317 * <TR> 318 * <TD>UNIX</TD> 319 * <TD>UNIX_</TD> 320 * </TR> 321 * </TABLE><br> 322 * For the different OS flavors, use these prefixes:<br> 323 * <br> 324 * <TABLE BORDER=1> 325 * <TR> 326 * <TH>OS Flavor</TH> 327 * <TH>Prefix</TH> 328 * </TR> 329 * <TR> 330 * <TD>NT</TD> 331 * <TD>NT_</TD> 332 * </TR> 333 * <TR> 334 * <TD>Mac OS X</TD> 335 * <TD>X_</TD> 336 * </TR> 337 * </TABLE> <br> 338 * <br> 339 * <b>Naming Example:</b> <br> 340 * <br> 341 * For the class <code>MyClass</code>, the specific version for Windows NT must be in the 342 * same package as <code>MyClass</code> and the name must be <code>Win_NT_MyClass</code>. A 343 * version that should be instantiated for any non-NT flavor would be called 344 * <code>Win_MyClass</code>. This would also be the version instantiated on Windows NT if the 345 * version <code>Win_NT_MyClass</code> does not exist. <br> 346 * <br> 347 * <b>The Loading Process</b> <br> 348 * <br> 349 * The process is completed after the first successful attempt to load a class. <br> 350 * <ol> 351 * <li>load a version that is OS and OS-Flavor specific 352 * <li>load a version that is OS specific 353 * <li>load the base version (without OS or OS-Flavor prefix) 354 * </ol> 355 * <br> 356 * See the <A HREF=doc-files/TargetFactory.html>TargetFactory Tutorial</A> for more 357 * information.<br> 358 * <br> 359 * 360 * @param name the fully qualified name of the class to load without the extension. 361 * 362 * @return An instance of the requested class. Note that specific initialization that can not be 363 * accomplished in the default constructor still needs to be performed before the object can be 364 * used. 365 * 366 * @exception Exception if all attempts to instantiate class fail 367 */ 368 /*--------------------------------------------------------------------------*/ 369 public Object makeObject(String name) throws Exception 370 { 371 int nameStart = name.lastIndexOf('.') + 1; 372 String packageName = name.substring(0, nameStart); 373 String className = name.substring(nameStart, name.length()); 374 String actualName; 375 376 try 377 { 378 actualName = packageName + CLASS_PREFIX[os] + CLASS_FLAVOR_PREFIX[osFlavor] + className; 379 Class temp = Class.forName(actualName); 380 return temp.newInstance(); 381 } 382 catch (Throwable exception1) 383 { 384 try 385 { 386 Class temp = Class.forName(packageName + CLASS_PREFIX[os] + className); 387 return temp.newInstance(); 388 } 389 catch (Throwable exception2) 390 { 391 try 392 { 393 actualName = name; 394 Class temp = Class.forName(actualName); 395 return temp.newInstance(); 396 } 397 catch (Throwable exception3) 398 { 399 throw new Exception("can not instantiate class " + name); 400 } 401 } 402 } 403 } 404 405 /*--------------------------------------------------------------------------*/ 406 /** 407 * Returns true if the version in the parameter string is higher than the version of the target 408 * os. 409 * 410 * @param version the version number to compare to 411 * 412 * @return <code>false</code> if the version of the target system is higher, otherwise 413 * <code>true</code> 414 */ 415 /*--------------------------------------------------------------------------*/ 416 /* 417 * $ @design 418 * 419 * Version numbers are assumed to be constructed as follows: - a list of one or more numbers, 420 * separated by periods as in X.X.X. ... or periods and dashes as in X.X.X-Y. ... - the numbers 421 * follow the decimal number system - the left most number is of highest significance 422 * 423 * The process compares each set of numbers, beginning at the most significant and working down 424 * the ranks (this is working left to right). The process is stopped as soon as the pair of 425 * numbers compaired is not equal. If the numer for the target system is higher, flase is 426 * returned, otherwise true. 427 * -------------------------------------------------------------------------- 428 */ 429 public boolean versionIsHigher(String version) throws Exception 430 { 431 StringTokenizer targetVersion = new StringTokenizer(this.version, VERSION_DELIMITER); 432 StringTokenizer compareVersion = new StringTokenizer(version, VERSION_DELIMITER); 433 434 int target; 435 int compare; 436 437 while (targetVersion.hasMoreTokens() && compareVersion.hasMoreTokens()) 438 { 439 try 440 { 441 target = Integer.parseInt(targetVersion.nextToken()); 442 compare = Integer.parseInt(compareVersion.nextToken()); 443 } 444 catch (Throwable exception) 445 { 446 throw new Exception("error in version string"); 447 } 448 449 if (compare > target) 450 { 451 return true; 452 } 453 else if (target > compare) { return false; } 454 } 455 456 return false; 457 } 458 459 /*--------------------------------------------------------------------------*/ 460 /** 461 * Returns the index number for the target operating system that was detected. 462 * 463 * @return an index number for the OS 464 * 465 * @see #WINDOWS 466 * @see #UNIX 467 * @see #GENERIC 468 */ 469 /*--------------------------------------------------------------------------*/ 470 public int getOS() 471 { 472 return os; 473 } 474 475 /*--------------------------------------------------------------------------*/ 476 /** 477 * Returns the index number for the operating system flavor that was detected on the target 478 * system. 479 * 480 * @return an index for the OS flavor 481 * 482 * @see #STANDARD 483 * @see #NT 484 * @see #X 485 */ 486 /*--------------------------------------------------------------------------*/ 487 public int getOSFlavor() 488 { 489 return osFlavor; 490 } 491 492 /*--------------------------------------------------------------------------*/ 493 /** 494 * Returns an index number that identified the processor architecture of the target system. 495 * 496 * @return an index for the processor architecture 497 * 498 * @see #X86 499 * @see #OTHER 500 */ 501 /*--------------------------------------------------------------------------*/ 502 public int getArchitecture() 503 { 504 return architecture; 505 } 506 507 /*--------------------------------------------------------------------------*/ 508 /** 509 * Returns the file extension customarily used on the target OS for dynamically loadable 510 * libraries. 511 * 512 * @return a <code>String</code> containing the customary library extension for the target OS. 513 * Note that the string might be empty if there no such specific extension for the target OS. 514 */ 515 /*--------------------------------------------------------------------------*/ 516 public String getNativeLibraryExtension() 517 { 518 return LIBRARY_EXTENSION[os]; 519 } 520 521 /*--------------------------------------------------------------------------*/ 522 /** 523 * Returns the system dependent default install path. This is typically used to suggest an 524 * istall path to the end user, when performing an installation. The default install path is 525 * assembled form the OS specific path fragment specified in <code>INSTALL_PATH_FRAGMENT</code>, 526 * possibly a drive letter and the application name. The user the option to define resources 527 * that define default paths which differ from the path fragments defined here. The following 528 * resource names will be recognized by this method: <br> 529 * <br> 530 * <ul> 531 * <li><code>TargetPanel.dir.windows</code> 532 * <li><code>TargetPanel.dir.macosx</code> 533 * <li><code>TargetPanel.dir.unix</code> 534 * <li><code>TargetPanel.dir</code> plus the all lower case version of 535 * <code>System.getProperty ("os.name")</code>, with all spaces replaced by an underscore 536 * ('_'). 537 * <li><code>TargetPanel.dir</code> 538 * </ul> 539 * 540 * @param appName the name of the application to install. If no specific resource has been set, 541 * then this name will be appended to the OS specific default path fragment. 542 * 543 * @return the default install path for the target system 544 */ 545 /*--------------------------------------------------------------------------*/ 546 /* 547 * $ @design 548 * 549 * First try to read a path string from a resource file. This approach allows the user to 550 * customize the default install path that is suggested to the end user by IzPack. There are a 551 * number of choices for the naming of this resource, so we need to go through a few steps in 552 * order to exhaust the different possibilities. If this was not successful we use the default 553 * install path that is defined for the operating system we are running on. This path should be 554 * expanded by the application name to form the full path that to returne. 555 * -------------------------------------------------------------------------- 556 */ 557 public String getDefaultInstallPath(String appName) 558 { 559 String path = null; 560 InputStream input; 561 String keyFragment = "/res/" + INSTALL_PATH_RESOURCE_KEY[GENERIC][STANDARD]; 562 563 // ---------------------------------------------------- 564 // attempt to get an input stream through a resource 565 // based on a key which is specific to the target OS 566 // ---------------------------------------------------- 567 input = getClass().getResourceAsStream("/res/" + INSTALL_PATH_RESOURCE_KEY[os][osFlavor]); 568 569 // ---------------------------------------------------- 570 // attempt to get an input stream through a resource 571 // based on a key which is made specific to the target 572 // OS by using the string returned by 573 // System.getProperty ("os.name").toLowerCase () 574 // ---------------------------------------------------- 575 if (input == null) 576 { 577 String key = OsVersion.OS_NAME.toLowerCase().replace(' ', '_'); // avoid 578 // spaces 579 // in 580 // file 581 // names 582 key = keyFragment + key.toLowerCase(); // for consistency among 583 // TargetPanel res files 584 input = TargetFactory.class.getResourceAsStream(key); 585 } 586 587 // ---------------------------------------------------- 588 // attempt to get an input stream through a resource 589 // based on a key which is not specific to any target OS 590 // ---------------------------------------------------- 591 if (input == null) 592 { 593 input = TargetFactory.class.getResourceAsStream(keyFragment); 594 } 595 596 // ---------------------------------------------------- 597 // If we got an input stream try to read the path 598 // from the file 599 // ---------------------------------------------------- 600 if (input != null) 601 { 602 InputStreamReader streamReader; 603 BufferedReader reader = null; 604 String line; 605 606 try 607 { 608 streamReader = new InputStreamReader(input); 609 reader = new BufferedReader(streamReader); 610 line = reader.readLine(); 611 612 while (line != null) 613 { 614 line = line.trim(); 615 if (!line.equals("")) 616 { 617 break; 618 } 619 line = reader.readLine(); 620 } 621 path = line; 622 } 623 catch (Throwable exception) 624 {} 625 finally 626 { 627 try 628 { 629 if (reader != null) reader.close(); 630 } 631 catch (Throwable exception) 632 {} 633 } 634 } 635 636 // ---------------------------------------------------- 637 // if we were unable to obtain a path from a resource, 638 // use the default for the traget operating system. 639 // ---------------------------------------------------- 640 if (path == null || path.equals("")) 641 { 642 path = ""; 643 644 // -------------------------------------------------- 645 // if we run on windows, we need a valid drive letter 646 // to put in front of the path. The drive that 647 // contains the user's home directory is usually the 648 // drive that also contains the install directory, 649 // so this seems the best choice here. 650 // -------------------------------------------------- 651 if (os == WINDOWS) 652 { 653 String home = System.getProperty("user.home"); 654 // take everything up to and including the first '\' 655 path = home.substring(0, home.indexOf(File.separatorChar) + 1); 656 } 657 658 path = path + INSTALL_PATH_FRAGMENT[os] + appName; 659 } 660 661 return path; 662 } 663 664 /** 665 * Gets a prefix alias for the current platform. "Win_" on Windows Systems "Win_NT_" on WinNT4, 666 * 2000, XP Mac on Mac Mac_X on macosx and Unix_ 667 * 668 * @return a prefix alias for the current platform 669 */ 670 671 public static String getCurrentOSPrefix() 672 { 673 String OSName = System.getProperty("os.name").toLowerCase(); 674 String OSArch = System.getProperty("os.arch").toLowerCase(); 675 int OS = 0; 676 int OSFlavor = 0; 677 int OSarchitecture = 0; 678 // ---------------------------------------------------- 679 // test for Windows 680 // ---------------------------------------------------- 681 if (OSName.indexOf("windows") > -1) 682 { 683 OS = WINDOWS; 684 OSFlavor = STANDARD; 685 OSarchitecture = X86; 686 687 if (OSName.indexOf("nt") > -1) 688 { 689 OSFlavor = NT; 690 } 691 else if (OSName.indexOf("2000") > -1) 692 { 693 OSFlavor = NT; 694 } 695 else if (OSName.indexOf("xp") > -1) 696 { 697 OSFlavor = NT; 698 } 699 } 700 // ---------------------------------------------------- 701 // test for Mac OS 702 // ---------------------------------------------------- 703 else if (OSName.indexOf("mac") > -1) 704 { 705 OS = GENERIC; 706 OSFlavor = STANDARD; 707 OSarchitecture = OTHER; 708 709 if (OSName.indexOf("macosx") > -1) 710 { 711 OSFlavor = X; 712 } 713 } 714 // ---------------------------------------------------- 715 // what's left should be unix 716 // ---------------------------------------------------- 717 else 718 { 719 OS = UNIX; 720 OSFlavor = STANDARD; 721 OSarchitecture = OTHER; 722 723 if (OSArch.indexOf("86") > -1) 724 { 725 OSarchitecture = X86; 726 } 727 } 728 729 return (CLASS_PREFIX[OS] + CLASS_FLAVOR_PREFIX[OSFlavor]); 730 } 731 732} 733/*---------------------------------------------------------------------------*/