1 21 22 package com.izforge.izpack.uninstaller; 23 24 import java.io.BufferedOutputStream ; 25 import java.io.BufferedReader ; 26 import java.io.File ; 27 import java.io.FileOutputStream ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.io.InputStreamReader ; 31 import java.io.OutputStream ; 32 import java.io.PrintStream ; 33 import java.io.PrintWriter ; 34 import java.io.RandomAccessFile ; 35 import java.lang.reflect.Method ; 36 import java.lang.reflect.Modifier ; 37 import java.net.URI ; 38 import java.net.URL ; 39 import java.text.CharacterIterator ; 40 import java.text.SimpleDateFormat ; 41 import java.text.StringCharacterIterator ; 42 import java.util.Date ; 43 import java.util.Enumeration ; 44 import java.util.jar.JarFile ; 45 import java.util.zip.ZipEntry ; 46 47 import com.izforge.izpack.util.OsVersion; 48 49 132 public class SelfModifier 133 { 134 135 136 public static final String BASE_KEY = "self.mod.base"; 137 138 139 public static final String JAR_KEY = "self.mod.jar"; 140 141 142 public static final String CLASS_KEY = "self.mod.class"; 143 144 145 public static final String METHOD_KEY = "self.mod.method"; 146 147 148 public static final String PHASE_KEY = "self.mod.phase"; 149 150 151 private String prefix = "izpack"; 152 153 154 private Method method = null; 155 156 157 private File logFile = null; 158 159 160 private File sandbox = null; 161 162 163 private File jarFile = null; 164 165 166 private int phase = 0; 167 168 169 private SimpleDateFormat isoPoint = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss.SSS"); 170 171 private Date date = new Date (); 172 173 public static void test(String [] args) 174 { 175 try 180 { 181 File sandbox = new File (System.getProperty(BASE_KEY) + ".d"); 182 File randFile = new File (sandbox, "RandomAccess.tmp"); 183 RandomAccessFile rand = new RandomAccessFile (randFile, "rw"); 184 rand.writeChars("Just a test: The jvm has to close 'cuz I won't!\n"); 185 186 System.err.print("Deleting sandbox: "); 187 deleteTree(sandbox); 188 System.err.println(sandbox.exists() ? "FAILED" : "SUCCEEDED"); 189 } 190 catch (Exception x) 191 { 192 System.err.println(x.getMessage()); 193 x.printStackTrace(); 194 } 195 } 196 197 public static void main(String [] args) 198 { 199 203 try 204 { 205 SelfModifier selfModifier = new SelfModifier(); 207 208 if (selfModifier.phase == 2) 210 selfModifier.invoke2(args); 211 212 else if (selfModifier.phase == 3) selfModifier.invoke3(args); 214 } 215 catch (IOException ioe) 216 { 217 System.err.println("Error invoking a secondary phase"); 218 System.err.println("Note that this program is only intended as a secondary process"); 219 ioe.printStackTrace(); 220 } 221 } 222 223 229 private SelfModifier() throws IOException 230 { 231 phase = Integer.parseInt(System.getProperty(PHASE_KEY)); 232 233 String cName = System.getProperty(CLASS_KEY); 234 String tName = System.getProperty(METHOD_KEY); 235 236 jarFile = new File (System.getProperty(JAR_KEY)); 237 logFile = new File (System.getProperty(BASE_KEY) + ".log"); 238 sandbox = new File (System.getProperty(BASE_KEY) + ".d"); 239 240 try 242 { 243 Class clazz = Class.forName(cName); 244 Method method = clazz.getMethod(tName, new Class [] { String [].class}); 245 246 initMethod(method); 247 } 248 catch (ClassNotFoundException x1) 249 { 250 log("No class found for " + cName); 251 } 252 catch (NoSuchMethodException x2) 253 { 254 log("No method " + tName + " found in " + cName); 255 } 256 } 257 258 277 public SelfModifier(Method method) throws IOException 278 { 279 phase = 1; 280 initJavaExec(); 281 initMethod(method); 282 } 283 284 292 private void initMethod(Method method) 293 { 294 int mod = method.getModifiers(); 295 if ((mod & Modifier.PUBLIC) == 0 || (mod & Modifier.STATIC) == 0) 296 throw new IllegalArgumentException ("Method not public and static"); 297 298 Class [] params = method.getParameterTypes(); 299 if (params.length != 1 || !params[0].isArray() 300 || !"java.lang.String".equals(params[0].getComponentType().getName())) 301 throw new IllegalArgumentException ("Method must accept String array"); 302 303 Class clazz = method.getDeclaringClass(); 304 mod = clazz.getModifiers(); 305 if ((mod & Modifier.PUBLIC) == 0 || (mod & Modifier.INTERFACE) != 0) 306 throw new IllegalArgumentException ("Method must be in a public class"); 307 308 this.method = method; 309 } 310 311 318 private void initJavaExec() throws IOException 319 { 320 try 321 { 322 Process p = Runtime.getRuntime().exec(javaCommand()); 323 324 new StreamProxy(p.getErrorStream(), "err").start(); 325 new StreamProxy(p.getInputStream(), "out").start(); 326 p.getOutputStream().close(); 327 328 p.waitFor(); 330 } 331 catch (InterruptedException ie) 332 { 333 throw new IOException ("Unable to create a java subprocess"); 334 } 335 } 336 337 341 342 360 public void invoke(String [] args) throws IOException 361 { 362 while (true) 364 { 365 logFile = File.createTempFile(prefix, ".log"); 366 String f = logFile.toString(); 367 sandbox = new File (f.substring(0, f.length() - 4) + ".d"); 368 369 if (!sandbox.exists()) break; 371 372 logFile.delete(); 373 } 374 if (!sandbox.mkdir()) throw new RuntimeException ("Failed to create temp dir: " + sandbox); 375 376 sandbox = sandbox.getCanonicalFile(); 377 logFile = logFile.getCanonicalFile(); 378 379 jarFile = findJarFile(method.getDeclaringClass()).getCanonicalFile(); 380 if (jarFile == null) throw new IllegalStateException ("SelfModifier must be in a jar file"); 381 log("JarFile: " + jarFile); 382 383 extractJarFile(); 384 385 if (args == null) args = new String [0]; 386 spawn(args, 2); 387 388 log("Exit"); 390 System.exit(0); 391 } 392 393 398 private Process spawn(String [] args, int nextPhase) throws IOException 399 { 400 String base = logFile.getAbsolutePath(); 401 base = base.substring(0, base.length() - 4); 402 403 String [] javaCmd = new String [] { javaCommand(), "-classpath", sandbox.getAbsolutePath(), 406 "-D" + BASE_KEY + "=" + base, "-D" + JAR_KEY + "=" + jarFile.getPath(), 407 "-D" + CLASS_KEY + "=" + method.getDeclaringClass().getName(), 408 "-D" + METHOD_KEY + "=" + method.getName(), "-D" + PHASE_KEY + "=" + nextPhase, 409 getClass().getName()}; 410 411 String [] entireCmd = new String [javaCmd.length + args.length]; 412 System.arraycopy(javaCmd, 0, entireCmd, 0, javaCmd.length); 413 System.arraycopy(args, 0, entireCmd, javaCmd.length, args.length); 414 415 StringBuffer sb = new StringBuffer ("Spawning phase "); 416 sb.append(nextPhase).append(": "); 417 for (int i = 0; i < entireCmd.length; i++) 418 sb.append("\n\t").append(entireCmd[i]); 419 log(sb.toString()); 420 421 if (JAVA_SPECIFICATION_VERSION < 1.3) 424 return Runtime.getRuntime().exec(entireCmd, null); 425 else 426 return Runtime.getRuntime().exec(entireCmd, null, null); } 428 429 435 public static File findJarFile(Class clazz) 436 { 437 String resource = clazz.getName().replace('.', '/') + ".class"; 438 439 URL url = ClassLoader.getSystemResource(resource); 440 if (!"jar".equals(url.getProtocol())) return null; 441 442 String path = url.getFile(); 443 path = path.substring(0, path.lastIndexOf('!')); 445 446 File file; 447 448 if (JAVA_SPECIFICATION_VERSION < 1.4) 454 file = new File (fromURI(path)); 455 else 456 file = new File (URI.create(path)); 457 458 return file; 459 } 460 461 464 private void extractJarFile() throws IOException 465 { 466 byte[] buf = new byte[5120]; 467 int extracted = 0; 468 InputStream in = null; 469 OutputStream out = null; 470 String MANIFEST = "META-INF/MANIFEST.MF"; 471 472 JarFile jar = new JarFile (jarFile, true); 473 474 try 475 { 476 Enumeration entries = jar.entries(); 477 while (entries.hasMoreElements()) 478 { 479 ZipEntry entry = (ZipEntry ) entries.nextElement(); 480 if (entry.isDirectory()) continue; 481 482 String pathname = entry.getName(); 483 if (MANIFEST.equals(pathname.toUpperCase())) continue; 484 485 in = jar.getInputStream(entry); 486 487 File outFile = new File (sandbox, pathname); 488 File parent = outFile.getParentFile(); 489 if (parent != null && !parent.exists()) parent.mkdirs(); 490 491 out = new BufferedOutputStream (new FileOutputStream (outFile)); 492 493 int n; 494 while ((n = in.read(buf, 0, buf.length)) > 0) 495 out.write(buf, 0, n); 496 497 out.close(); 498 extracted++; 499 } 500 jar.close(); 501 502 log("Extracted " + extracted + " file" + (extracted > 1 ? "s" : "") + " into " 503 + sandbox.getPath()); 504 } 505 finally 506 { 507 try 508 { 509 jar.close(); 510 } 511 catch (IOException ioe) 512 {} 513 if (out != null) 514 { 515 try 516 { 517 out.close(); 518 } 519 catch (IOException ioe) 520 {} 521 } 522 if (in != null) 523 { 524 try 525 { 526 in.close(); 527 } 528 catch (IOException ioe) 529 {} 530 } 531 } 532 } 533 534 538 539 546 private void invoke2(String [] args) 547 { 548 549 int retVal = -1; 550 try 551 { 552 try 559 { 560 Thread.sleep(1000); 561 } 562 catch (Exception x) 563 {} 564 565 Process p = spawn(args, 3); 567 568 new StreamProxy(p.getErrorStream(), "err", log).start(); 569 new StreamProxy(p.getInputStream(), "out", log).start(); 570 p.getOutputStream().close(); 571 572 try 573 { 574 retVal = p.waitFor(); 575 } 576 catch (InterruptedException e) 577 { 578 log(e); 579 } 580 581 log("deleteing sandbox"); 583 deleteTree(sandbox); 584 } 585 catch (Exception e) 586 { 587 log(e); 588 } 589 log("Phase 3 return value = " + retVal); 590 } 591 592 593 public static boolean deleteTree(File file) 594 { 595 if (file.isDirectory()) 596 { 597 File [] files = file.listFiles(); 598 for (int i = 0; i < files.length; i++) 599 deleteTree(files[i]); 600 } 601 return file.delete(); 602 } 603 604 609 610 613 private void invoke3(String [] args) 614 { 615 try 617 { 618 errlog("Invoking method: " + method.getDeclaringClass().getName() + "." 619 + method.getName() + "(String[] args)"); 620 621 method.invoke(null, new Object [] { args}); 622 } 623 catch (Throwable t) 624 { 625 errlog(t.getMessage()); 626 t.printStackTrace(); 627 errlog("exiting"); 628 System.err.flush(); 629 System.exit(31); 630 } 631 632 errlog("Method returned, waiting for other threads"); 633 System.err.flush(); 634 } 636 637 641 642 PrintStream log = null; 643 644 private void errlog(String msg) 645 { 646 date.setTime(System.currentTimeMillis()); 647 System.err.println(isoPoint.format(date) + " Phase " + phase + ": " + msg); 648 } 649 650 private PrintStream checkLog() 651 { 652 try 653 { 654 if (log == null) log = new PrintStream (new FileOutputStream (logFile.toString(), true)); 655 } 656 catch (IOException x) 657 { 658 System.err.println("Phase " + phase + " log err: " + x.getMessage()); 659 x.printStackTrace(); 660 } 661 date.setTime(System.currentTimeMillis()); 662 return log; 663 } 664 665 private void log(Throwable t) 666 { 667 if (checkLog() != null) 668 { 669 log.println(isoPoint.format(date) + " Phase " + phase + ": " + t.getMessage()); 670 t.printStackTrace(log); 671 } 672 } 673 674 private void log(String msg) 675 { 676 if (checkLog() != null) 677 log.println(isoPoint.format(date) + " Phase " + phase + ": " + msg); 678 } 679 680 public static class StreamProxy extends Thread 681 { 682 683 InputStream in; 684 685 String name; 686 687 OutputStream out; 688 689 public StreamProxy(InputStream in, String name) 690 { 691 this(in, name, null); 692 } 693 694 public StreamProxy(InputStream in, String name, OutputStream out) 695 { 696 this.in = in; 697 this.name = name; 698 this.out = out; 699 } 700 701 public void run() 702 { 703 try 704 { 705 PrintWriter pw = null; 706 if (out != null) pw = new PrintWriter (out); 707 708 BufferedReader br = new BufferedReader (new InputStreamReader (in)); 709 String line; 710 while ((line = br.readLine()) != null) 711 { 712 if (pw != null) pw.println(line); 713 } 715 if (pw != null) pw.flush(); 716 } 717 catch (IOException ioe) 718 { 719 ioe.printStackTrace(); 720 } 721 } 722 } 723 724 728 private static final float JAVA_SPECIFICATION_VERSION = Float.parseFloat(System 732 .getProperty("java.specification.version")); 733 734 private static final String JAVA_HOME = System.getProperty("java.home"); 735 736 750 public static String fromURI(String uri) 751 { 752 if (!uri.startsWith("file:")) 753 throw new IllegalArgumentException ("Can only handle file: URIs"); 754 755 if (uri.startsWith("file://")) 756 uri = uri.substring(7); 757 else 758 uri = uri.substring(5); 759 760 uri = uri.replace('/', File.separatorChar); 761 if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2 762 && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) 763 { 764 uri = uri.substring(1); 765 } 766 767 StringBuffer sb = new StringBuffer (); 768 CharacterIterator iter = new StringCharacterIterator (uri); 769 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) 770 { 771 if (c == '%') 772 { 773 char c1 = iter.next(); 774 if (c1 != CharacterIterator.DONE) 775 { 776 int i1 = Character.digit(c1, 16); 777 char c2 = iter.next(); 778 if (c2 != CharacterIterator.DONE) 779 { 780 int i2 = Character.digit(c2, 16); 781 sb.append((char) ((i1 << 4) + i2)); 782 } 783 } 784 } 785 else 786 { 787 sb.append(c); 788 } 789 } 790 791 String path = sb.toString(); 792 return path; 793 } 794 795 private static String addExtension(String command) 796 { 797 return command + (OsVersion.IS_WINDOWS || OsVersion.IS_OS2 ? ".exe" : ""); 800 } 801 802 private static String javaCommand() 803 { 804 String executable = addExtension("java"); 815 String dir = new File (JAVA_HOME + "/bin").getAbsolutePath(); 816 File jExecutable = new File (dir, executable); 817 818 if (!jExecutable.exists()) return executable; 822 return jExecutable.getAbsolutePath(); 823 } 824 } 825 | Popular Tags |