KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > uninstaller > SelfModifier


1 /*
2  * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
3  *
4  * http://www.izforge.com/izpack/
5  * http://developer.berlios.de/projects/izpack/
6  *
7  * Copyright 2004 Chadwick McHenry
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package com.izforge.izpack.uninstaller;
23
24 import java.io.BufferedOutputStream JavaDoc;
25 import java.io.BufferedReader JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.io.PrintStream JavaDoc;
33 import java.io.PrintWriter JavaDoc;
34 import java.io.RandomAccessFile JavaDoc;
35 import java.lang.reflect.Method JavaDoc;
36 import java.lang.reflect.Modifier JavaDoc;
37 import java.net.URI JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.text.CharacterIterator JavaDoc;
40 import java.text.SimpleDateFormat JavaDoc;
41 import java.text.StringCharacterIterator JavaDoc;
42 import java.util.Date JavaDoc;
43 import java.util.Enumeration JavaDoc;
44 import java.util.jar.JarFile JavaDoc;
45 import java.util.zip.ZipEntry JavaDoc;
46
47 import com.izforge.izpack.util.OsVersion;
48
49 /**
50  * Allows an application to modify the jar file from which it came, including outright deletion. The
51  * jar file of an app is usually locked when java is run so this is normally not possible.
52  * <p>
53  *
54  * Create a SelfModifier with a target method, then invoke the SelfModifier with arguments to be
55  * passed to the target method. The jar file containing the target method's class (obtained by
56  * reflection) will be extracted to a temporary directory, and a new java process will be spawned to
57  * invoke the target method. The original jar file may now be modified.
58  * <p>
59  *
60  * If the constructor or invoke() methods fail, it is generally because secondary java processes
61  * could not be started.
62  *
63  * <b>Requirements</b>
64  * <ul>
65  * <li>The target method, and all it's required classes must be in a jar file.
66  * <li>The Self Modifier, and its inner classes must also be in the jar file.
67  * </ul>
68  *
69  * There are three system processes (or "phases") involved, the first invoked by the user, the
70  * second and third by the SelfModifier.
71  * <p>
72  *
73  * <b>Phase 1:</b>
74  * <ol>
75  * <li>Program is launched, SelfModifier is created, invoke(String[]) is called
76  * <li>A temporary directory (or "sandbox") is created in the default temp directory, and the jar
77  * file contents ar extracted into it
78  * <li>Phase 2 is spawned using the sandbox as it's classpath, SelfModifier as the main class, the
79  * arguments to "invoke(String[])" as the main arguments, and the <a
80  * HREF="#selfmodsysprops">SelfModifier system properties</a> set.
81  * <li>Immidiately exit so the system unlocks the jar file
82  * </ol>
83  *
84  * <b>Phase 2:</b>
85  * <ol>
86  * <li>Initializes from system properties.
87  * <li>Spawn phase 3 exactly as phase 2 except the self.modifier.phase system properties set to 3.
88  * <li>Wait for phase 3 to die
89  * <li>Delete the temporary sandbox
90  * </ol>
91  *
92  * <b>Phase 3:</b>
93  * <ol>
94  * <li>Initializes from system properties.
95  * <li>Redirect std err stream to the log
96  * <li>Invoke the target method with arguments we were given
97  * <li>The target method is expected to call exit(), or to not start any looping threads (e.g. AWT
98  * thread). In other words, the target is the new "main" method.
99  * </ol>
100  *
101  * <a name="selfmodsysprops"><b>SelfModifier system properties</b></a> used to pass information
102  * between processes. <table border="1">
103  * <tr>
104  * <th>Constant
105  * <th>System property
106  * <th>description</tr>
107  * <tr>
108  * <td><a HREF="#BASE_KEY">BASE_KEY</a>
109  * <td>self.mod.jar
110  * <td>base path to log file and sandbox dir</tr>
111  * <tr>
112  * <td><a HREF="#JAR_KEY">JAR_KEY</a>
113  * <td>self.mod.class
114  * <td>path to original jar file</tr>
115  * <tr>
116  * <td><a HREF="#CLASS_KEY">CLASS_KEY</a>
117  * <td>self.mod.method
118  * <td>class of target method</tr>
119  * <tr>
120  * <td><a HREF="#METHOD_KEY">METHOD_KEY</a>
121  * <td>self.mod.phase
122  * <td>name of method to be invoked in sandbox</tr>
123  * <tr>
124  * <td><a HREF="#PHASE_KEY">PHASE_KEY</a>
125  * <td>self.mod.base
126  * <td>phase of operation to run</tr>
127  * </table>
128  *
129  * @author Chadwick McHenry
130  * @version 1.0
131  */

132 public class SelfModifier
133 {
134
135     /** System property name of base for log and sandbox of secondary processes. */
136     public static final String JavaDoc BASE_KEY = "self.mod.base";
137
138     /** System property name of original jar file containing application. */
139     public static final String JavaDoc JAR_KEY = "self.mod.jar";
140
141     /** System property name of class declaring target method. */
142     public static final String JavaDoc CLASS_KEY = "self.mod.class";
143
144     /** System property name of target method to invoke in secondary process. */
145     public static final String JavaDoc METHOD_KEY = "self.mod.method";
146
147     /** System property name of phase (1, 2, or 3) indicator. */
148     public static final String JavaDoc PHASE_KEY = "self.mod.phase";
149
150     /** Base prefix name for sandbox and log, used only in phase 1. */
151     private String JavaDoc prefix = "izpack";
152
153     /** Target method to be invoked in sandbox. */
154     private Method JavaDoc method = null;
155
156     /** Log for phase 2 and 3, because we can't capture the stdio from them. */
157     private File JavaDoc logFile = null;
158
159     /** Directory which we extract too, invoke from, and finally delete. */
160     private File JavaDoc sandbox = null;
161
162     /** Original jar file program was launched from. */
163     private File JavaDoc jarFile = null;
164
165     /** Current phase of execution: 1, 2, or 3. */
166     private int phase = 0;
167
168     /** For logging time. */
169     private SimpleDateFormat JavaDoc isoPoint = new SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss.SSS");
170
171     private Date JavaDoc date = new Date JavaDoc();
172
173     public static void test(String JavaDoc[] args)
174     {
175         // open a File for random access in the sandbox, which will cause
176
// deletion
177
// of the file and its parent directories to fail until it is closed (by
178
// virtue of this java process halting)
179
try
180         {
181             File JavaDoc sandbox = new File JavaDoc(System.getProperty(BASE_KEY) + ".d");
182             File JavaDoc randFile = new File JavaDoc(sandbox, "RandomAccess.tmp");
183             RandomAccessFile JavaDoc rand = new RandomAccessFile JavaDoc(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 JavaDoc x)
191         {
192             System.err.println(x.getMessage());
193             x.printStackTrace();
194         }
195     }
196
197     public static void main(String JavaDoc[] args)
198     {
199         // phase 1 already set up the sandbox and spawned phase 2.
200
// phase 2 creates the log, spawns phase 3 and waits
201
// phase 3 invokes method and returns. method must kill all it's threads
202

203         try
204         {
205             // all it's attributes are retrieved from system properties
206
SelfModifier selfModifier = new SelfModifier();
207
208             // phase 2: invoke a process for phase 3, wait, and clean up
209
if (selfModifier.phase == 2)
210                 selfModifier.invoke2(args);
211
212             // phase 3: invoke method and die
213
else if (selfModifier.phase == 3) selfModifier.invoke3(args);
214         }
215         catch (IOException JavaDoc 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     /**
224      * Internal constructor where target class and method are obtained from system properties.
225      *
226      * @throws IOException for errors getting to the sandbox.
227      * @throws SecurityException if access to the target method is denied
228      */

229     private SelfModifier() throws IOException JavaDoc
230     {
231         phase = Integer.parseInt(System.getProperty(PHASE_KEY));
232
233         String JavaDoc cName = System.getProperty(CLASS_KEY);
234         String JavaDoc tName = System.getProperty(METHOD_KEY);
235
236         jarFile = new File JavaDoc(System.getProperty(JAR_KEY));
237         logFile = new File JavaDoc(System.getProperty(BASE_KEY) + ".log");
238         sandbox = new File JavaDoc(System.getProperty(BASE_KEY) + ".d");
239
240         // retrieve refrence to target method
241
try
242         {
243             Class JavaDoc clazz = Class.forName(cName);
244             Method JavaDoc method = clazz.getMethod(tName, new Class JavaDoc[] { String JavaDoc[].class});
245
246             initMethod(method);
247         }
248         catch (ClassNotFoundException JavaDoc x1)
249         {
250             log("No class found for " + cName);
251         }
252         catch (NoSuchMethodException JavaDoc x2)
253         {
254             log("No method " + tName + " found in " + cName);
255         }
256     }
257
258     /**
259      * Creates a SelfModifier which will invoke the target method in a separate process from which
260      * it may modify it's own jar file.
261      *
262      * The target method must be public, static, and take a single array of strings as its only
263      * parameter. The class which declares the method must also be public. Reflection is used to
264      * ensure this.
265      *
266      * @param method a public, static method that accepts a String array as it's only parameter. Any
267      * return value is ignored.
268      *
269      * @throws NullPointerException if <code>method</code> is null
270      * @throws IllegalArgumentException if <code>method</code> is not public, static, and take a
271      * String array as it's only argument, or of it's declaring class is not public.
272      * @throws IllegalStateException if process was not invoked from a jar file, or an IOExceptioin
273      * occured while accessing it
274      * @throws IOException if java is unable to be executed as a separte process
275      * @throws SecurityException if access to the method, or creation of a subprocess is denied
276      */

277     public SelfModifier(Method JavaDoc method) throws IOException JavaDoc
278     {
279         phase = 1;
280         initJavaExec();
281         initMethod(method);
282     }
283
284     /**
285      * Check the method for the required properties (public, static, params:(String[])).
286      *
287      * @throws NullPointerException if <code>method</code> is null
288      * @throws IllegalArgumentException if <code>method</code> is not public, static, and take a
289      * String array as it's only argument, or of it's declaring class is not public.
290      * @throws SecurityException if access to the method is denied
291      */

292     private void initMethod(Method JavaDoc method)
293     {
294         int mod = method.getModifiers();
295         if ((mod & Modifier.PUBLIC) == 0 || (mod & Modifier.STATIC) == 0)
296             throw new IllegalArgumentException JavaDoc("Method not public and static");
297
298         Class JavaDoc[] params = method.getParameterTypes();
299         if (params.length != 1 || !params[0].isArray()
300                 || !"java.lang.String".equals(params[0].getComponentType().getName()))
301             throw new IllegalArgumentException JavaDoc("Method must accept String array");
302
303         Class JavaDoc clazz = method.getDeclaringClass();
304         mod = clazz.getModifiers();
305         if ((mod & Modifier.PUBLIC) == 0 || (mod & Modifier.INTERFACE) != 0)
306             throw new IllegalArgumentException JavaDoc("Method must be in a public class");
307
308         this.method = method;
309     }
310
311     /**
312      * This call ensures that java can be exec'd in a separate process.
313      *
314      * @throws IOException if an I/O error occurs, indicating java is unable to be exec'd
315      * @throws SecurityException if a security manager exists and doesn't allow creation of a
316      * subprocess
317      */

318     private void initJavaExec() throws IOException JavaDoc
319     {
320         try
321         {
322             Process JavaDoc 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             // even if it returns an error code, it was at least found
329
p.waitFor();
330         }
331         catch (InterruptedException JavaDoc ie)
332         {
333             throw new IOException JavaDoc("Unable to create a java subprocess");
334         }
335     }
336
337     /***********************************************************************************************
338      * --------------------------------------------------------------------- Phase 1 (call from
339      * external spawn phase 2) ---------------------------------------------------------------------
340      */

341
342     /**
343      * Invoke the target method in a separate process from which it may modify it's own jar file.
344      * This method does not normally return. After spawning the secondary process, the current
345      * process must die before the jar file is unlocked, therefore calling this method is akin to
346      * calling {@link System#exit(int)}.
347      * <p>
348      *
349      * The contents of the current jar file are extracted copied to a 'sandbox' directory from which
350      * the method is invoked. The path to the original jar file is placed in the system property
351      * {@link #JAR_KEY}.
352      * <p>
353      *
354      * @param args arguments to pass to the target method. May be empty or null to indicate no
355      * arguments.
356      *
357      * @throws IOException for lots of things
358      * @throws IllegalStateException if method's class was not loaded from a jar
359      */

360     public void invoke(String JavaDoc[] args) throws IOException JavaDoc
361     {
362         // Initialize sandbox and log file to be unique, but similarly named
363
while (true)
364         {
365             logFile = File.createTempFile(prefix, ".log");
366             String JavaDoc f = logFile.toString();
367             sandbox = new File JavaDoc(f.substring(0, f.length() - 4) + ".d");
368
369             // check if the similarly named directory is free
370
if (!sandbox.exists()) break;
371
372             logFile.delete();
373         }
374         if (!sandbox.mkdir()) throw new RuntimeException JavaDoc("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 JavaDoc("SelfModifier must be in a jar file");
381         log("JarFile: " + jarFile);
382
383         extractJarFile();
384
385         if (args == null) args = new String JavaDoc[0];
386         spawn(args, 2);
387
388         // finally, if all went well, the invoking process must exit
389
log("Exit");
390         System.exit(0);
391     }
392
393     /**
394      * Run a new jvm with all the system parameters needed for phases 2 and 3.
395      *
396      * @throws IOException if there is an error getting the cononical name of a path
397      */

398     private Process JavaDoc spawn(String JavaDoc[] args, int nextPhase) throws IOException JavaDoc
399     {
400         String JavaDoc base = logFile.getAbsolutePath();
401         base = base.substring(0, base.length() - 4);
402
403         // invoke from tmpdir, passing target method arguments as args, and
404
// SelfModifier parameters as sustem properties
405
String JavaDoc[] javaCmd = new String JavaDoc[] { 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 JavaDoc[] entireCmd = new String JavaDoc[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 JavaDoc sb = new StringBuffer JavaDoc("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         // Just invoke it and let it go, the exception will be caught above
422
// Won't compile on < jdk1.3, but will run on jre1.2
423
if (JAVA_SPECIFICATION_VERSION < 1.3)
424             return Runtime.getRuntime().exec(entireCmd, null);
425         else
426             return Runtime.getRuntime().exec(entireCmd, null, null); // workDir);
427
}
428
429     /**
430      * Retrieve the jar file the specified class was loaded from.
431      *
432      * @return null if file was not loaded from a jar file
433      * @throws SecurityException if access to is denied by SecurityManager
434      */

435     public static File JavaDoc findJarFile(Class JavaDoc clazz)
436     {
437         String JavaDoc resource = clazz.getName().replace('.', '/') + ".class";
438
439         URL JavaDoc url = ClassLoader.getSystemResource(resource);
440         if (!"jar".equals(url.getProtocol())) return null;
441
442         String JavaDoc path = url.getFile();
443         // starts at "file:..." (use getPath() as of 1.3)
444
path = path.substring(0, path.lastIndexOf('!'));
445
446         File JavaDoc file;
447
448         // getSystemResource() returns a valid URL (eg. spaces are %20), but a
449
// file
450
// Constructed w/ it will expect "%20" in path. URI and File(URI)
451
// properly
452
// deal with escaping back and forth, but didn't exist until 1.4
453
if (JAVA_SPECIFICATION_VERSION < 1.4)
454             file = new File JavaDoc(fromURI(path));
455         else
456             file = new File JavaDoc(URI.create(path));
457
458         return file;
459     }
460
461     /**
462      * @throws IOException
463      */

464     private void extractJarFile() throws IOException JavaDoc
465     {
466         byte[] buf = new byte[5120];
467         int extracted = 0;
468         InputStream JavaDoc in = null;
469         OutputStream JavaDoc out = null;
470         String JavaDoc MANIFEST = "META-INF/MANIFEST.MF";
471
472         JarFile JavaDoc jar = new JarFile JavaDoc(jarFile, true);
473
474         try
475         {
476             Enumeration JavaDoc entries = jar.entries();
477             while (entries.hasMoreElements())
478             {
479                 ZipEntry JavaDoc entry = (ZipEntry JavaDoc) entries.nextElement();
480                 if (entry.isDirectory()) continue;
481
482                 String JavaDoc pathname = entry.getName();
483                 if (MANIFEST.equals(pathname.toUpperCase())) continue;
484
485                 in = jar.getInputStream(entry);
486
487                 File JavaDoc outFile = new File JavaDoc(sandbox, pathname);
488                 File JavaDoc parent = outFile.getParentFile();
489                 if (parent != null && !parent.exists()) parent.mkdirs();
490
491                 out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(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 JavaDoc ioe)
512             {}
513             if (out != null)
514             {
515                 try
516                 {
517                     out.close();
518                 }
519                 catch (IOException JavaDoc ioe)
520                 {}
521             }
522             if (in != null)
523             {
524                 try
525                 {
526                     in.close();
527                 }
528                 catch (IOException JavaDoc ioe)
529                 {}
530             }
531         }
532     }
533
534     /***********************************************************************************************
535      * --------------------------------------------------------------------- Phase 2 (spawn the
536      * phase 3 and clean up) ---------------------------------------------------------------------
537      */

538
539     /**
540      * Invoke phase 2, which starts phase 3, then cleans up the sandbox. This is needed because
541      * GUI's often call the exit() method to kill the AWT thread, and early versions of java did not
542      * have exit hooks. In order to delete the sandbox on exit we invoke method in separate process
543      * and wait for that process to complete. Even worse, resources in the jar may be locked by the
544      * target process, which would prevent the sandbox from being deleted as well.
545      */

546     private void invoke2(String JavaDoc[] args)
547     {
548
549         int retVal = -1;
550         try
551         {
552             // TODO: in jre 1.2, Phs1 consistently needs more time to unlock the
553
// original jar. Phs2 should wait to invoke Phs3 until it knows its
554
// parent (Phs1) has died, but Process.waitFor() only works on
555
// children. Can we see when a parent dies, or /this/ Process
556
// becomes
557
// orphaned?
558
try
559             {
560                 Thread.sleep(1000);
561             }
562             catch (Exception JavaDoc x)
563             {}
564
565             // spawn phase 3, capture its stdio and wait for it to exit
566
Process JavaDoc 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 JavaDoc e)
577             {
578                 log(e);
579             }
580
581             // clean up and go
582
log("deleteing sandbox");
583             deleteTree(sandbox);
584         }
585         catch (Exception JavaDoc e)
586         {
587             log(e);
588         }
589         log("Phase 3 return value = " + retVal);
590     }
591
592     /** Recursively delete a file structure. */
593     public static boolean deleteTree(File JavaDoc file)
594     {
595         if (file.isDirectory())
596         {
597             File JavaDoc[] files = file.listFiles();
598             for (int i = 0; i < files.length; i++)
599                 deleteTree(files[i]);
600         }
601         return file.delete();
602     }
603
604     /***********************************************************************************************
605      * --------------------------------------------------------------------- Phase 3 (invoke method,
606      * let it go as long as it likes)
607      * ---------------------------------------------------------------------
608      */

609
610     /**
611      * Invoke the target method and let it run free!
612      */

613     private void invoke3(String JavaDoc[] args)
614     {
615         // std io is being redirected to the log
616
try
617         {
618             errlog("Invoking method: " + method.getDeclaringClass().getName() + "."
619                     + method.getName() + "(String[] args)");
620
621             method.invoke(null, new Object JavaDoc[] { args});
622         }
623         catch (Throwable JavaDoc 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         // now let the method call exit...
635
}
636
637     /***********************************************************************************************
638      * --------------------------------------------------------------------- Logging
639      * ---------------------------------------------------------------------
640      */

641
642     PrintStream JavaDoc log = null;
643
644     private void errlog(String JavaDoc msg)
645     {
646         date.setTime(System.currentTimeMillis());
647         System.err.println(isoPoint.format(date) + " Phase " + phase + ": " + msg);
648     }
649
650     private PrintStream JavaDoc checkLog()
651     {
652         try
653         {
654             if (log == null) log = new PrintStream JavaDoc(new FileOutputStream JavaDoc(logFile.toString(), true));
655         }
656         catch (IOException JavaDoc 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 JavaDoc 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 JavaDoc msg)
675     {
676         if (checkLog() != null)
677             log.println(isoPoint.format(date) + " Phase " + phase + ": " + msg);
678     }
679
680     public static class StreamProxy extends Thread JavaDoc
681     {
682
683         InputStream JavaDoc in;
684
685         String JavaDoc name;
686
687         OutputStream JavaDoc out;
688
689         public StreamProxy(InputStream JavaDoc in, String JavaDoc name)
690         {
691             this(in, name, null);
692         }
693
694         public StreamProxy(InputStream JavaDoc in, String JavaDoc name, OutputStream JavaDoc 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 JavaDoc pw = null;
706                 if (out != null) pw = new PrintWriter JavaDoc(out);
707
708                 BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(in));
709                 String JavaDoc line;
710                 while ((line = br.readLine()) != null)
711                 {
712                     if (pw != null) pw.println(line);
713                     // System.out.println(name + ">" + line);
714
}
715                 if (pw != null) pw.flush();
716             }
717             catch (IOException JavaDoc ioe)
718             {
719                 ioe.printStackTrace();
720             }
721         }
722     }
723
724     /***********************************************************************************************
725      * --------------------------------------------------------------------- Apache ant code
726      * ---------------------------------------------------------------------
727      */

728     // This was stolen (and specialized from much more modular code) from the
729
// jakarta ant class org.apache.tools.ant.taskdefs.condition.Os
730
// See the javaCommand() method.
731
private static final float JAVA_SPECIFICATION_VERSION = Float.parseFloat(System
732             .getProperty("java.specification.version"));
733
734     private static final String JavaDoc JAVA_HOME = System.getProperty("java.home");
735
736     /**
737      * Constructs a file path from a <code>file:</code> URI.
738      *
739      * <p>
740      * Will be an absolute path if the given URI is absolute.
741      * </p>
742      *
743      * <p>
744      * Swallows '%' that are not followed by two characters, doesn't deal with non-ASCII characters.
745      * </p>
746      *
747      * @param uri the URI designating a file in the local filesystem.
748      * @return the local file system path for the file.
749      */

750     public static String JavaDoc fromURI(String JavaDoc uri)
751     {
752         if (!uri.startsWith("file:"))
753             throw new IllegalArgumentException JavaDoc("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 JavaDoc sb = new StringBuffer JavaDoc();
768         CharacterIterator JavaDoc iter = new StringCharacterIterator JavaDoc(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 JavaDoc path = sb.toString();
792         return path;
793     }
794
795     private static String JavaDoc addExtension(String JavaDoc command)
796     {
797         // This is the most common extension case - exe for windows and OS/2,
798
// nothing for *nix.
799
return command + (OsVersion.IS_WINDOWS || OsVersion.IS_OS2 ? ".exe" : "");
800     }
801
802     private static String JavaDoc javaCommand()
803     {
804         // This was stolen (and specialized from much more modular code) from
805
// the
806
// jakarta ant classes Os & JavaEnvUtils. Also see the following
807
// org.apache.tools.ant.taskdefs.Java
808
// org.apache.tools.ant.taskdefs.Execute
809
// org.apache.tools.ant.taskdefs.condition.Os
810
// org.apache.tools.ant.util.CommandlineJava
811
// org.apache.tools.ant.util.JavaEnvUtils
812
// org.apache.tools.ant.util.FileUtils
813
// TODO: I didn't copy nearly all of their conditions
814
String JavaDoc executable = addExtension("java");
815         String JavaDoc dir = new File JavaDoc(JAVA_HOME + "/bin").getAbsolutePath();
816         File JavaDoc jExecutable = new File JavaDoc(dir, executable);
817
818         // Unfortunately on Windows java.home doesn't always refer
819
// to the correct location, so we need to fall back to
820
// assuming java is somewhere on the PATH.
821
if (!jExecutable.exists()) return executable;
822         return jExecutable.getAbsolutePath();
823     }
824 }
825
Popular Tags