Index: src/java/org/apache/ivy/util/FileUtil.java =================================================================== --- src/java/org/apache/ivy/util/FileUtil.java (revision 1373520) +++ src/java/org/apache/ivy/util/FileUtil.java (working copy) @@ -17,6 +17,7 @@ */ package org.apache.ivy.util; +import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -52,6 +53,20 @@ private static final byte[] EMPTY_BUFFER = new byte[0]; + // currently active symlink helper process + private static Process symlinkHelper; + + private static PrintWriter symlinkHelperStdin; + + private static BufferedReader symlinkHelperStdout; + + // symlink helper process script, run via sh -c '...' + private static final String symlinkScript = "IFS=; " + "while read -r SRC; do " + + " read -r DEST; " + " read -r FORCE; " + + " error=`ln -s \"$FORCE\" -- \"$SRC\" \"$DEST\" 2>&1`; " + + " if [ $? -ne 0 ]; then echo \"error: $error\" | head -1; else echo \"ok\"; fi; " + + "done; " + "echo \"script terminated\"; exit 0"; + public static void symlink(File src, File dest, CopyProgressListener l, boolean overwrite) throws IOException { try { @@ -68,30 +83,68 @@ dest.getParentFile().mkdirs(); } - Runtime runtime = Runtime.getRuntime(); - Message.verbose("executing 'ln -s -f " + src.getAbsolutePath() + " " + dest.getPath() - + "'"); - Process process = runtime.exec(new String[] {"ln", "-s", "-f", src.getAbsolutePath(), - dest.getPath()}); + final PrintWriter stdin; + final BufferedReader stdout; + synchronized (FileUtil.class) { + if (symlinkHelper == null) { + Message.verbose("Starting symlink helper - executing: sh -c '" + symlinkScript + + "'"); - if (process.waitFor() != 0) { - InputStream errorStream = process.getErrorStream(); - InputStreamReader isr = new InputStreamReader(errorStream); - BufferedReader br = new BufferedReader(isr); + ProcessBuilder builder = new ProcessBuilder(new String[] {"sh", "-c", + symlinkScript}); + builder.redirectErrorStream(true); + symlinkHelper = builder.start(); - StringBuffer error = new StringBuffer(); - String line; - while ((line = br.readLine()) != null) { - error.append(line); - error.append('\n'); + symlinkHelperStdin = new PrintWriter(new BufferedOutputStream( + symlinkHelper.getOutputStream())); + symlinkHelperStdout = new BufferedReader(new InputStreamReader( + symlinkHelper.getInputStream())); } - throw new IOException("error symlinking " + src + " to " + dest + ":\n" + error); + stdin = symlinkHelperStdin; + stdout = symlinkHelperStdout; } - + + String result; + try { + Message.verbose("Requesting symlink of " + src.getAbsolutePath() + " to " + + dest.getPath()); + symlinkHelperStdin.println(src.getAbsolutePath()); + symlinkHelperStdin.println(dest.getPath()); + symlinkHelperStdin.println(overwrite ? "-f" : ""); + symlinkHelperStdin.flush(); + result = symlinkHelperStdout.readLine(); + } catch (IOException ioe) { + Message.verbose("error writing to symlink helper script: " + ioe); + synchronized (FileUtil.class) { + if (symlinkHelper != null) + symlinkHelper.destroy(); + symlinkHelper = null; + } + + throw ioe; + } + + Message.verbose("Symlink helper responded: " + result); + + if (result == null || (!result.equals("ok") && !result.startsWith("error: "))) { + // EOF or unexpected response from script + Message.verbose("EOF or unexpected response from symlink helper script"); + synchronized (FileUtil.class) { + if (symlinkHelper != null) + symlinkHelper.destroy(); + symlinkHelper = null; + } + } + + if (!"ok".equals(result)) { + throw new IOException("while symlinking " + src + " to " + dest + ": " + + (result == null ? "helper script EOF" : result)); + } + // check if the creation of the symbolic link was successful if (!dest.exists()) { - throw new IOException("error symlinking: " + dest + " doesn't exists"); + throw new IOException("error symlinking: " + dest + " doesn't exist"); } // check if the result is a true symbolic link @@ -105,8 +158,6 @@ x.printStackTrace(new PrintWriter(buffer)); Message.debug(buffer.toString()); copy(src, dest, l, overwrite); - } catch (InterruptedException x) { - Thread.currentThread().interrupt(); } } Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 1373520) +++ CHANGES.txt (working copy) @@ -63,6 +63,7 @@ Anders Janmyr Steve Jones Christer Jonsson + Oliver Jowett Michael Kebe Matthias Kilian Alexey Kiselev @@ -141,6 +142,7 @@ - NEW: Support Conditional Setting of a Property (IVY-1367) - NEW: Exposing some parent metadata (organisation, module, revision, branch) as properties (IVY-1288) (Thanks to Jean-Louis Boudart) +- NEW: Faster symlink creation (avoid a JVM fork per symlink) (IVY-1318) (Thanks to Oliver Jowett) - IMPROVEMENT: add support for source bundles from p2 repositories - IMPROVEMENT: add support for source URI from OBR repositories