KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > util > LibraryRemover


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

19 package com.izforge.izpack.util;
20
21 import java.util.List JavaDoc;
22 import java.io.BufferedOutputStream JavaDoc;
23 import java.io.BufferedReader JavaDoc;
24 import java.io.BufferedWriter JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream 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.OutputStreamWriter JavaDoc;
33 import java.io.PrintStream JavaDoc;
34 import java.io.PrintWriter JavaDoc;
35 import java.text.SimpleDateFormat JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Collections JavaDoc;
38 import java.util.Date JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.TreeSet JavaDoc;
41
42 /**
43  * This class tries to remove a given list of files which are locked by this process. For this the
44  * paths of the files are stored in a temporary file and a new process will be created. The class
45  * files which are needed by the new process will be unpacked from the jar file under users temp dir
46  * in a "sandbox". The new process receive the path of the temporary file and some other
47  * information. After a wait intervall it reads the path file and removes all files which there are
48  * listed. Next the created "sandbox" and the path file will be removed. This class uses the
49  * characteristik of the system loader that jar files will be keeped open, simple class files will
50  * be closed after loading a class. Therefore jar files are locked and cannot be deleted, class
51  * files are not locked and deletable.<br>
52  * The idea for this stuff is copied from Chadwick McHenry's SelfModifier in the uninstaller stuff
53  * of IzPack.
54  *
55  * @author Klaus Bartz
56  *
57  */

58 public class LibraryRemover
59 {
60
61     /**
62      * All class files which are needed for the second process. All have to be in this installers
63      * jar file. No slash in front should be used; no dot else slashes should be used;
64      * extension (.class) will be required.
65      */

66     private static final String JavaDoc[] SANDBOX_CONTENT = { "com/izforge/izpack/util/LibraryRemover.class"};
67
68     /** System property name of base for log and sandbox of secondary processes. */
69     private static final String JavaDoc BASE_KEY = "lib.rem.base";
70
71     /** System property name of phase (1, 2, or 3) indicator. */
72     private static final String JavaDoc PHASE_KEY = "self.mod.phase";
73
74     /** VM home Needed for the java command. */
75     private static final String JavaDoc JAVA_HOME = System.getProperty("java.home");
76
77     /** Prefix of sandbox, path and log file. */
78     private static final String JavaDoc PREFIX = "InstallRemover";
79
80     /** Phase of this process. */
81     private int phase = 0;
82
83     /** Log for phase 2, because we can't capture the stdio from them. */
84     private File JavaDoc logFile = null;
85
86     /** Directory which we extract too, invoke from, and finally delete. */
87     private File JavaDoc sandbox = null;
88
89     /** The file which contains the paths of the files to delete. */
90     private File JavaDoc specFile = null;
91
92     /** For logging time. */
93     private SimpleDateFormat JavaDoc isoPoint = new SimpleDateFormat JavaDoc("yyyy-MM-dd'T'HH:mm:ss.SSS");
94
95     /** Also for logging time. */
96     private Date JavaDoc date = new Date JavaDoc();
97
98     /**
99      * Constructor for both phases. Depending on the phase different initializing will be performed.
100      *
101      * @param phase for which an object should be created.
102      * @throws IOException
103      */

104     private LibraryRemover(int phase) throws IOException JavaDoc
105     {
106         this.phase = phase;
107         if (phase == 1)
108         {
109             initJavaExec();
110         }
111         else
112         {
113             logFile = new File JavaDoc(System.getProperty(BASE_KEY) + ".log");
114             specFile = new File JavaDoc(System.getProperty(BASE_KEY) + ".spec");
115             sandbox = new File JavaDoc(System.getProperty(BASE_KEY) + ".d");
116         }
117     }
118
119     /**
120      * Entry point for phase 1. This class tries to remove all files given in the Vector.
121      *
122      * @param temporaryFileNames
123      * @throws IOException
124      */

125     public static void invoke(List JavaDoc temporaryFileNames) throws IOException JavaDoc
126     {
127         LibraryRemover self = new LibraryRemover(1);
128         self.invoke1(temporaryFileNames);
129     }
130
131     /**
132      * This call ensures that java can be exec'd in a separate process.
133      *
134      * @throws IOException if an I/O error occurs, indicating java is unable to be exec'd
135      * @throws SecurityException if a security manager exists and doesn't allow creation of a
136      * subprocess
137      */

138     private void initJavaExec() throws IOException JavaDoc
139     {
140         try
141         {
142             Process JavaDoc p = Runtime.getRuntime().exec(javaCommand());
143
144             new StreamProxy(p.getErrorStream(), "err").start();
145             new StreamProxy(p.getInputStream(), "out").start();
146             p.getOutputStream().close();
147
148             // even if it returns an error code, it was at least found
149
p.waitFor();
150         }
151         catch (InterruptedException JavaDoc ie)
152         {
153             throw new IOException JavaDoc("Unable to create a java subprocess");
154         }
155     }
156
157     /**
158      * Internal invoke method for phase 1.
159      *
160      * @param temporaryFileNames list of paths of the files which should be removed
161      * @throws IOException
162      */

163     private void invoke1(List JavaDoc temporaryFileNames) throws IOException JavaDoc
164     {
165         // Initialize sandbox and log file to be unique, but similarly named
166
while (true)
167         {
168             logFile = File.createTempFile(PREFIX, ".log");
169             String JavaDoc f = logFile.getCanonicalPath();
170             specFile = new File JavaDoc(f.substring(0, f.length() - 4) + ".spec");
171             sandbox = new File JavaDoc(f.substring(0, f.length() - 4) + ".d");
172
173             // check if the similarly named directory is free
174
if (!sandbox.exists()) break;
175
176             logFile.delete();
177         }
178         if (!sandbox.mkdir()) throw new RuntimeException JavaDoc("Failed to create temp dir: " + sandbox);
179
180         sandbox = sandbox.getCanonicalFile();
181         logFile = logFile.getCanonicalFile();
182         OutputStream JavaDoc out = null;
183         InputStream JavaDoc in = null;
184         byte[] buf = new byte[5120];
185         int extracted = 0;
186         // Write out the class files from the current installer jar into the sandbox.
187
// This allows later to delete the classes because class files are deleteable
188
// also the using process is running, jar files are not deletable in that
189
// situation.,
190
for (int i = 0; i < SANDBOX_CONTENT.length; ++i)
191         {
192             in = getClass().getResourceAsStream("/" + SANDBOX_CONTENT[i]);
193
194             File JavaDoc outFile = new File JavaDoc(sandbox, SANDBOX_CONTENT[i]);
195             File JavaDoc parent = outFile.getParentFile();
196             if (parent != null && !parent.exists()) parent.mkdirs();
197
198             out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(outFile));
199
200             int n;
201             while ((n = in.read(buf, 0, buf.length)) > 0)
202                 out.write(buf, 0, n);
203
204             out.close();
205             extracted++;
206
207         }
208         // We write a file which contains the paths to remove.
209
out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(specFile));
210         BufferedWriter JavaDoc specWriter = new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(out));
211         Iterator JavaDoc iter = temporaryFileNames.iterator();
212         while (iter.hasNext())
213         {
214             specWriter.write((String JavaDoc) iter.next());
215             if (iter.hasNext()) specWriter.newLine();
216         }
217         specWriter.flush();
218         out.close();
219
220         spawn(2);
221
222         // finally, if all went well, the invoking process must exit
223
log("Exit");
224         System.exit(0);
225     }
226
227     /**
228      * Returns an ArrayList of the files to delete.
229      *
230      * @return The files list.
231      * @exception Exception Description of the Exception
232      */

233     private ArrayList JavaDoc getFilesList() throws Exception JavaDoc
234     {
235         // Initialisations
236
TreeSet JavaDoc files = new TreeSet JavaDoc(Collections.reverseOrder());
237         InputStream JavaDoc in = new FileInputStream JavaDoc(specFile);
238         InputStreamReader JavaDoc inReader = new InputStreamReader JavaDoc(in);
239         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(inReader);
240
241         // We read it
242
String JavaDoc read = reader.readLine();
243         while (read != null)
244         {
245             files.add(new File JavaDoc(read));
246             read = reader.readLine();
247         }
248         in.close();
249         // We return it
250
return new ArrayList JavaDoc(files);
251     }
252
253     /**
254      * Invoke methode for phase 2.
255      */

256     private void invoke2()
257     {
258
259         try
260         {
261             // Give main program time to exit.
262
try
263             {
264                 Thread.sleep(1000);
265             }
266             catch (Exception JavaDoc x)
267             {}
268
269             ArrayList JavaDoc files = getFilesList();
270             int size = files.size();
271             // We destroy the files
272

273             log("deleteing temporary dlls/shls");
274             for (int i = 0; i < size; i++)
275             {
276                 File JavaDoc file = (File JavaDoc) files.get(i);
277                 file.delete();
278                 if (file.exists())
279                     log(" deleting of " + file.getCanonicalPath() + " failed!!!");
280                 else
281                     log(" " + file.getCanonicalPath());
282
283             }
284
285             // clean up and go
286
log("deleteing sandbox");
287             deleteTree(sandbox);
288             specFile.delete();
289         }
290         catch (Exception JavaDoc e)
291         {
292             log(e);
293         }
294     }
295
296     /**
297      * Copied from com.izforge.izpack.uninstaller.SelfModifier. Little addaption for this class.
298      *
299      * @param nextPhase phase of the spawn
300      * @return created process object
301      * @throws IOException
302      */

303     private Process JavaDoc spawn(int nextPhase) throws IOException JavaDoc
304     {
305         String JavaDoc base = logFile.getAbsolutePath();
306         base = base.substring(0, base.length() - 4);
307
308         // invoke from tmpdir, passing target method arguments as args, and
309
// SelfModifier parameters as sustem properties
310
String JavaDoc[] javaCmd = new String JavaDoc[] { javaCommand(), "-classpath", sandbox.getAbsolutePath(),
311                 "-D" + BASE_KEY + "=" + base, "-D" + PHASE_KEY + "=" + nextPhase,
312                 getClass().getName()};
313
314         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Spawning phase ");
315         sb.append(nextPhase).append(": ");
316         for (int i = 0; i < javaCmd.length; i++)
317             sb.append("\n\t").append(javaCmd[i]);
318         log(sb.toString());
319
320         // Just invoke it and let it go, the exception will be caught above
321
return Runtime.getRuntime().exec(javaCmd, null, null); // workDir);
322
}
323
324     /**
325      * Recursively delete a file structure. Copied from com.izforge.izpack.uninstaller.SelfModifier.
326      * Little addaption to this class.
327      *
328      * @return command for spawning
329      */

330     public static boolean deleteTree(File JavaDoc file)
331     {
332         if (file.isDirectory())
333         {
334             File JavaDoc[] files = file.listFiles();
335             for (int i = 0; i < files.length; i++)
336                 deleteTree(files[i]);
337         }
338         return file.delete();
339     }
340
341     /**
342      * Copied from com.izforge.izpack.uninstaller.SelfModifier.
343      *
344      * @return command command extended with extension of executable
345      */

346     private static String JavaDoc addExtension(String JavaDoc command)
347     {
348         // This is the most common extension case - exe for windows and OS/2,
349
// nothing for *nix.
350
return command + (OsVersion.IS_WINDOWS || OsVersion.IS_OS2 ? ".exe" : "");
351     }
352
353     /**
354      * Copied from com.izforge.izpack.uninstaller.SelfModifier. Little addaption for this class.
355      *
356      * @return command for spawning
357      */

358     private static String JavaDoc javaCommand()
359     {
360         String JavaDoc executable = addExtension("java");
361         String JavaDoc dir = new File JavaDoc(JAVA_HOME + "/bin").getAbsolutePath();
362         File JavaDoc jExecutable = new File JavaDoc(dir, executable);
363
364         // Unfortunately on Windows java.home doesn't always refer
365
// to the correct location, so we need to fall back to
366
// assuming java is somewhere on the PATH.
367
if (!jExecutable.exists()) return executable;
368         return jExecutable.getAbsolutePath();
369     }
370
371     public static void main(String JavaDoc[] args)
372     {
373         // Phase 2 removes given path list, sandbox and spec file.
374
// Phase 3 as used in SelfModifier will be not needed here because
375
// this class do not use a GUI which can call exit like the
376
// one in SelfModifier.
377

378         try
379         {
380             // all it's attributes are retrieved from system properties
381
LibraryRemover librianRemover = new LibraryRemover(2);
382             librianRemover.invoke2();
383         }
384         catch (IOException JavaDoc ioe)
385         {
386             System.err.println("Error invoking a secondary phase");
387             System.err.println("Note that this program is only intended as a secondary process");
388             ioe.printStackTrace();
389         }
390     }
391
392     /***********************************************************************************************
393      * --------------------------------------------------------------------- Logging
394      * --------------------------------------------------------------------- Copied from
395      * com.izforge.izpack.uninstaller.SelfModifier.
396      */

397
398     PrintStream JavaDoc log = null;
399
400     private PrintStream JavaDoc checkLog()
401     {
402         try
403         {
404             if (log == null) log = new PrintStream JavaDoc(new FileOutputStream JavaDoc(logFile.toString(), true));
405         }
406         catch (IOException JavaDoc x)
407         {
408             System.err.println("Phase " + phase + " log err: " + x.getMessage());
409             x.printStackTrace();
410         }
411         date.setTime(System.currentTimeMillis());
412         return log;
413     }
414
415     private void log(Throwable JavaDoc t)
416     {
417         if (checkLog() != null)
418         {
419             log.println(isoPoint.format(date) + " Phase " + phase + ": " + t.getMessage());
420             t.printStackTrace(log);
421         }
422     }
423
424     private void log(String JavaDoc msg)
425     {
426         if (checkLog() != null)
427             log.println(isoPoint.format(date) + " Phase " + phase + ": " + msg);
428     }
429
430     public static class StreamProxy extends Thread JavaDoc
431     {
432
433         InputStream JavaDoc in;
434
435         String JavaDoc name;
436
437         OutputStream JavaDoc out;
438
439         public StreamProxy(InputStream JavaDoc in, String JavaDoc name)
440         {
441             this(in, name, null);
442         }
443
444         public StreamProxy(InputStream JavaDoc in, String JavaDoc name, OutputStream JavaDoc out)
445         {
446             this.in = in;
447             this.name = name;
448             this.out = out;
449         }
450
451         public void run()
452         {
453             try
454             {
455                 PrintWriter JavaDoc pw = null;
456                 if (out != null) pw = new PrintWriter JavaDoc(out);
457
458                 BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(in));
459                 String JavaDoc line;
460                 while ((line = br.readLine()) != null)
461                 {
462                     if (pw != null) pw.println(line);
463                     // System.out.println(name + ">" + line);
464
}
465                 if (pw != null) pw.flush();
466             }
467             catch (IOException JavaDoc ioe)
468             {
469                 ioe.printStackTrace();
470             }
471         }
472     }
473
474 }
475
Popular Tags