[izpack-changes] r1448 - izpack-src/trunk/src/lib/com/izforge/izpack/util

noreply at berlios.de noreply at berlios.de
Tue Jun 20 11:40:42 CEST 2006


Author: bartzkau
Date: 2006-06-20 11:40:42 +0200 (Tue, 20 Jun 2006)
New Revision: 1448

Added:
   izpack-src/trunk/src/lib/com/izforge/izpack/util/LibraryRemover.java
Modified:
   izpack-src/trunk/src/lib/com/izforge/izpack/util/Librarian.java
Log:
Removing of dlls/shls changed from FreeLibraryAndExitThread in the c part
to creating a new process which removes the dlls/shls via java calls.
This will be used only if VM version is 1.5.x or higher, else the old
mimik will be active.



Modified: izpack-src/trunk/src/lib/com/izforge/izpack/util/Librarian.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/util/Librarian.java	2006-06-19 13:33:20 UTC (rev 1447)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/util/Librarian.java	2006-06-20 09:40:42 UTC (rev 1448)
@@ -1,4 +1,5 @@
 /*
+ * $Id:$ 
  * IzPack - Copyright 2001-2006 Julien Ponge, All Rights Reserved.
  * 
  * http://www.izforge.com/izpack/
@@ -24,6 +25,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
@@ -78,6 +80,10 @@
     /** The block size used for reading and writing data, 4k. */
     private static final int BLOCK_SIZE = 4096;
 
+    /** VM version needed to select clean up method. */
+    private static final float JAVA_SPECIFICATION_VERSION = Float.parseFloat(System
+            .getProperty("java.specification.version"));
+
     // ------------------------------------------------------------------------
     // Variable Declarations
     // ------------------------------------------------------------------------
@@ -204,6 +210,16 @@
         // ----------------------------------------------------
         if (loaded(libraryName)) { return; }
 
+        
+        if( System.getProperty("DLL_PATH") != null )
+        {
+            String path = System.getProperty("DLL_PATH") + "/" + name + extension;
+            path = path.replace('/', File.separatorChar);
+            Debug.trace("Try to load library " + path);
+            System.load(path);
+            return;
+            
+        }
         // ----------------------------------------------------
         // First try a straight load
         // ----------------------------------------------------
@@ -632,10 +648,41 @@
     /**
      * This method attempts to remove all native libraries that have been temporarily created from
      * the system.
+     * The used method for clean up depends on the VM version.
+     * If the ersion is 1.5.x or higher this process should be exit in one second, else
+     * the native libraries will be not deleted.
+     * Tests with the different methods produces hinds that the
+     * FreeLibraryAndExitThread (handle, 0) call in the dlls are the 
+     * reason for VM crashes (version 1.5.x). May be this is a bug in the VM.
+     * But never seen a docu that this behavior is compatible with a VM.
+     * Since more than a year all 1.5 versions produce this crash. Therfore we make
+     * now a work around for it.
+     * But the idea to exit the thread for removing the file locking to give the
+     * possibility to delete the dlls are really nice. Therefore we use it with
+     * VMs which are compatible with it.  (Klaus Bartz 2006.06.20)
      */
     /*--------------------------------------------------------------------------*/
     public void cleanUp()
     {
+        if (JAVA_SPECIFICATION_VERSION < 1.5)
+            oldCleanUp();
+        else
+            newCleanUp();
+
+    }
+
+    /*--------------------------------------------------------------------------*/
+    /**
+     * This method attempts to remove all native libraries that have been temporarily created from
+     * the system.
+     * This method will be invoked if the VM has version 1.4.x or less. Version 1.5.x or higher
+     * uses newCleanUp.
+     * This method starts a new thread which calls a method in the dll which should unload the
+     * dll. The thread never returns. 
+     */
+    /*--------------------------------------------------------------------------*/
+    private void oldCleanUp()
+    {
         for (int i = 0; i < clients.size(); i++)
         {
             // --------------------------------------------------
@@ -668,5 +715,31 @@
             {} // nothing I can do
         }
     }
+
+    /*--------------------------------------------------------------------------*/
+    /**
+     * This method attempts to remove all native libraries that have been temporarily created from
+     * the system. This method will be invoked if the VM has version 1.5.x or higher. Version 1.4.x
+     * or less uses oldCleanUp. This method calls LibraryRemover which starts a new process which
+     * waits a little bit for exit of this process and tries than to delete the given files.
+     */
+    /*--------------------------------------------------------------------------*/
+    private void newCleanUp()
+    {
+        // This method will be used the SelfModifier stuff of uninstall 
+        // instead of killing the thread in the dlls which provokes a 
+        // segmentation violation with a 1.5 (also known as 5.0) VM.
+
+        try
+        {
+            LibraryRemover.invoke(temporaryFileNames);
+        }
+        catch (IOException e1)
+        {
+            // TODO Auto-generated catch block
+            e1.printStackTrace();
+        }
+
+    }
 }
 /*---------------------------------------------------------------------------*/

Added: izpack-src/trunk/src/lib/com/izforge/izpack/util/LibraryRemover.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/util/LibraryRemover.java	2006-06-19 13:33:20 UTC (rev 1447)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/util/LibraryRemover.java	2006-06-20 09:40:42 UTC (rev 1448)
@@ -0,0 +1,474 @@
+/*
+ * $Id:$ 
+ * IzPack - Copyright 2001-2006 Julien Ponge, All Rights Reserved.
+ * 
+ * http://www.izforge.com/izpack/ http://developer.berlios.de/projects/izpack/
+ * 
+ * Copyright 2006 Klaus Bartz
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.izforge.izpack.util;
+
+import java.util.List;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * This class tries to remove a given list of files which are locked by this process. For this the
+ * paths of the files are stored in a temporary file and a new process will be created. The class
+ * files which are needed by the new process will be unpacked from the jar file under users temp dir
+ * in a "sandbox". The new process receive the path of the temporary file and some other
+ * information. After a wait intervall it reads the path file and removes all files which there are
+ * listed. Next the created "sandbox" and the path file will be removed. This class uses the
+ * characteristik of the system loader that jar files will be keeped open, simple class files will
+ * be closed after loading a class. Therefore jar files are locked and cannot be deleted, class
+ * files are not locked and deletable.<br>
+ * The idea for this stuff is copied from Chadwick McHenry's SelfModifier in the uninstaller stuff
+ * of IzPack.
+ * 
+ * @author Klaus Bartz
+ * 
+ */
+public class LibraryRemover
+{
+
+    /**
+     * All class files which are needed for the second process. All have to be in this installers
+     * jar file. No slash in front should be used; no dot else slashes should be used;
+     * extension (.class) will be required.
+     */
+    private static final String[] SANDBOX_CONTENT = { "com/izforge/izpack/util/LibrianRemover.class"};
+
+    /** System property name of base for log and sandbox of secondary processes. */
+    private static final String BASE_KEY = "lib.rem.base";
+
+    /** System property name of phase (1, 2, or 3) indicator. */
+    private static final String PHASE_KEY = "self.mod.phase";
+
+    /** VM home Needed for the java command. */
+    private static final String JAVA_HOME = System.getProperty("java.home");
+
+    /** Prefix of sandbox, path and log file. */
+    private static final String PREFIX = "InstallRemover";
+
+    /** Phase of this process. */
+    private int phase = 0;
+
+    /** Log for phase 2, because we can't capture the stdio from them. */
+    private File logFile = null;
+
+    /** Directory which we extract too, invoke from, and finally delete. */
+    private File sandbox = null;
+
+    /** The file which contains the paths of the files to delete. */
+    private File specFile = null;
+
+    /** For logging time. */
+    private SimpleDateFormat isoPoint = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
+
+    /** Also for logging time. */
+    private Date date = new Date();
+
+    /**
+     * Constructor for both phases. Depending on the phase different initializing will be performed.
+     * 
+     * @param phase for which an object should be created.
+     * @throws IOException
+     */
+    private LibraryRemover(int phase) throws IOException
+    {
+        this.phase = phase;
+        if (phase == 1)
+        {
+            initJavaExec();
+        }
+        else
+        {
+            logFile = new File(System.getProperty(BASE_KEY) + ".log");
+            specFile = new File(System.getProperty(BASE_KEY) + ".spec");
+            sandbox = new File(System.getProperty(BASE_KEY) + ".d");
+        }
+    }
+
+    /**
+     * Entry point for phase 1. This class tries to remove all files given in the Vector.
+     * 
+     * @param temporaryFileNames
+     * @throws IOException
+     */
+    public static void invoke(List temporaryFileNames) throws IOException
+    {
+        LibraryRemover self = new LibraryRemover(1);
+        self.invoke1(temporaryFileNames);
+    }
+
+    /**
+     * This call ensures that java can be exec'd in a separate process.
+     * 
+     * @throws IOException if an I/O error occurs, indicating java is unable to be exec'd
+     * @throws SecurityException if a security manager exists and doesn't allow creation of a
+     * subprocess
+     */
+    private void initJavaExec() throws IOException
+    {
+        try
+        {
+            Process p = Runtime.getRuntime().exec(javaCommand());
+
+            new StreamProxy(p.getErrorStream(), "err").start();
+            new StreamProxy(p.getInputStream(), "out").start();
+            p.getOutputStream().close();
+
+            // even if it returns an error code, it was at least found
+            p.waitFor();
+        }
+        catch (InterruptedException ie)
+        {
+            throw new IOException("Unable to create a java subprocess");
+        }
+    }
+
+    /**
+     * Internal invoke method for phase 1.
+     * 
+     * @param temporaryFileNames list of paths of the files which should be removed
+     * @throws IOException
+     */
+    private void invoke1(List temporaryFileNames) throws IOException
+    {
+        // Initialize sandbox and log file to be unique, but similarly named
+        while (true)
+        {
+            logFile = File.createTempFile(PREFIX, ".log");
+            String f = logFile.getCanonicalPath();
+            specFile = new File(f.substring(0, f.length() - 4) + ".spec");
+            sandbox = new File(f.substring(0, f.length() - 4) + ".d");
+
+            // check if the similarly named directory is free
+            if (!sandbox.exists()) break;
+
+            logFile.delete();
+        }
+        if (!sandbox.mkdir()) throw new RuntimeException("Failed to create temp dir: " + sandbox);
+
+        sandbox = sandbox.getCanonicalFile();
+        logFile = logFile.getCanonicalFile();
+        OutputStream out = null;
+        InputStream in = null;
+        byte[] buf = new byte[5120];
+        int extracted = 0;
+        // Write out the class files from the current installer jar into the sandbox.
+        // This allows later to delete the classes because class files are deleteable
+        // also the using process is running, jar files are not deletable in that
+        // situation.,
+        for (int i = 0; i < SANDBOX_CONTENT.length; ++i)
+        {
+            in = getClass().getResourceAsStream("/" + SANDBOX_CONTENT[i]);
+
+            File outFile = new File(sandbox, SANDBOX_CONTENT[i]);
+            File parent = outFile.getParentFile();
+            if (parent != null && !parent.exists()) parent.mkdirs();
+
+            out = new BufferedOutputStream(new FileOutputStream(outFile));
+
+            int n;
+            while ((n = in.read(buf, 0, buf.length)) > 0)
+                out.write(buf, 0, n);
+
+            out.close();
+            extracted++;
+
+        }
+        // We write a file which contains the paths to remove.
+        out = new BufferedOutputStream(new FileOutputStream(specFile));
+        BufferedWriter specWriter = new BufferedWriter(new OutputStreamWriter(out));
+        Iterator iter = temporaryFileNames.iterator();
+        while (iter.hasNext())
+        {
+            specWriter.write((String) iter.next());
+            if (iter.hasNext()) specWriter.newLine();
+        }
+        specWriter.flush();
+        out.close();
+
+        spawn(2);
+
+        // finally, if all went well, the invoking process must exit
+        log("Exit");
+        System.exit(0);
+    }
+
+    /**
+     * Returns an ArrayList of the files to delete.
+     * 
+     * @return The files list.
+     * @exception Exception Description of the Exception
+     */
+    private ArrayList getFilesList() throws Exception
+    {
+        // Initialisations
+        TreeSet files = new TreeSet(Collections.reverseOrder());
+        InputStream in = new FileInputStream(specFile);
+        InputStreamReader inReader = new InputStreamReader(in);
+        BufferedReader reader = new BufferedReader(inReader);
+
+        // We read it
+        String read = reader.readLine();
+        while (read != null)
+        {
+            files.add(new File(read));
+            read = reader.readLine();
+        }
+        in.close();
+        // We return it
+        return new ArrayList(files);
+    }
+
+    /**
+     * Invoke methode for phase 2.
+     */
+    private void invoke2()
+    {
+
+        try
+        {
+            // Give main program time to exit.
+            try
+            {
+                Thread.sleep(1000);
+            }
+            catch (Exception x)
+            {}
+
+            ArrayList files = getFilesList();
+            int size = files.size();
+            // We destroy the files
+
+            log("deleteing temporary dlls/shls");
+            for (int i = 0; i < size; i++)
+            {
+                File file = (File) files.get(i);
+                file.delete();
+                if (file.exists())
+                    log("    deleting of " + file.getCanonicalPath() + " failed!!!");
+                else
+                    log("    " + file.getCanonicalPath());
+
+            }
+
+            // clean up and go
+            log("deleteing sandbox");
+            deleteTree(sandbox);
+            specFile.delete();
+        }
+        catch (Exception e)
+        {
+            log(e);
+        }
+    }
+
+    /**
+     * Copied from com.izforge.izpack.uninstaller.SelfModifier. Little addaption for this class.
+     * 
+     * @param nextPhase phase of the spawn
+     * @return created process object
+     * @throws IOException
+     */
+    private Process spawn(int nextPhase) throws IOException
+    {
+        String base = logFile.getAbsolutePath();
+        base = base.substring(0, base.length() - 4);
+
+        // invoke from tmpdir, passing target method arguments as args, and
+        // SelfModifier parameters as sustem properties
+        String[] javaCmd = new String[] { javaCommand(), "-classpath", sandbox.getAbsolutePath(),
+                "-D" + BASE_KEY + "=" + base, "-D" + PHASE_KEY + "=" + nextPhase,
+                getClass().getName()};
+
+        StringBuffer sb = new StringBuffer("Spawning phase ");
+        sb.append(nextPhase).append(": ");
+        for (int i = 0; i < javaCmd.length; i++)
+            sb.append("\n\t").append(javaCmd[i]);
+        log(sb.toString());
+
+        // Just invoke it and let it go, the exception will be caught above
+        return Runtime.getRuntime().exec(javaCmd, null, null); // workDir);
+    }
+
+    /**
+     * Recursively delete a file structure. Copied from com.izforge.izpack.uninstaller.SelfModifier.
+     * Little addaption to this class.
+     * 
+     * @return command for spawning
+     */
+    public static boolean deleteTree(File file)
+    {
+        if (file.isDirectory())
+        {
+            File[] files = file.listFiles();
+            for (int i = 0; i < files.length; i++)
+                deleteTree(files[i]);
+        }
+        return file.delete();
+    }
+
+    /**
+     * Copied from com.izforge.izpack.uninstaller.SelfModifier.
+     * 
+     * @return command command extended with extension of executable
+     */
+    private static String addExtension(String command)
+    {
+        // This is the most common extension case - exe for windows and OS/2,
+        // nothing for *nix.
+        return command + (OsVersion.IS_WINDOWS || OsVersion.IS_OS2 ? ".exe" : "");
+    }
+
+    /**
+     * Copied from com.izforge.izpack.uninstaller.SelfModifier. Little addaption for this class.
+     * 
+     * @return command for spawning
+     */
+    private static String javaCommand()
+    {
+        String executable = addExtension("java");
+        String dir = new File(JAVA_HOME + "/bin").getAbsolutePath();
+        File jExecutable = new File(dir, executable);
+
+        // Unfortunately on Windows java.home doesn't always refer
+        // to the correct location, so we need to fall back to
+        // assuming java is somewhere on the PATH.
+        if (!jExecutable.exists()) return executable;
+        return jExecutable.getAbsolutePath();
+    }
+
+    public static void main(String[] args)
+    {
+        // Phase 2 removes given path list, sandbox and spec file.
+        // Phase 3 as used in SelfModifier will be not needed here because
+        // this class do not use a GUI which can call exit like the
+        // one in SelfModifier.
+
+        try
+        {
+            // all it's attributes are retrieved from system properties
+            LibraryRemover librianRemover = new LibraryRemover(2);
+            librianRemover.invoke2();
+        }
+        catch (IOException ioe)
+        {
+            System.err.println("Error invoking a secondary phase");
+            System.err.println("Note that this program is only intended as a secondary process");
+            ioe.printStackTrace();
+        }
+    }
+
+    /***********************************************************************************************
+     * --------------------------------------------------------------------- Logging
+     * --------------------------------------------------------------------- Copied from
+     * com.izforge.izpack.uninstaller.SelfModifier.
+     */
+
+    PrintStream log = null;
+
+    private PrintStream checkLog()
+    {
+        try
+        {
+            if (log == null) log = new PrintStream(new FileOutputStream(logFile.toString(), true));
+        }
+        catch (IOException x)
+        {
+            System.err.println("Phase " + phase + " log err: " + x.getMessage());
+            x.printStackTrace();
+        }
+        date.setTime(System.currentTimeMillis());
+        return log;
+    }
+
+    private void log(Throwable t)
+    {
+        if (checkLog() != null)
+        {
+            log.println(isoPoint.format(date) + " Phase " + phase + ": " + t.getMessage());
+            t.printStackTrace(log);
+        }
+    }
+
+    private void log(String msg)
+    {
+        if (checkLog() != null)
+            log.println(isoPoint.format(date) + " Phase " + phase + ": " + msg);
+    }
+
+    public static class StreamProxy extends Thread
+    {
+
+        InputStream in;
+
+        String name;
+
+        OutputStream out;
+
+        public StreamProxy(InputStream in, String name)
+        {
+            this(in, name, null);
+        }
+
+        public StreamProxy(InputStream in, String name, OutputStream out)
+        {
+            this.in = in;
+            this.name = name;
+            this.out = out;
+        }
+
+        public void run()
+        {
+            try
+            {
+                PrintWriter pw = null;
+                if (out != null) pw = new PrintWriter(out);
+
+                BufferedReader br = new BufferedReader(new InputStreamReader(in));
+                String line;
+                while ((line = br.readLine()) != null)
+                {
+                    if (pw != null) pw.println(line);
+                    // System.out.println(name + ">" + line);
+                }
+                if (pw != null) pw.flush();
+            }
+            catch (IOException ioe)
+            {
+                ioe.printStackTrace();
+            }
+        }
+    }
+
+}




More information about the izpack-changes mailing list