Source:
public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath, @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException { try { Path path = fileToPath(new File(baseDir, symlinkPath)); Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY); try { File symlink = File.createTempFile("tmp-"+symlinkPath, null, baseDir); symlink.delete(); Path tempSymlinkPath = symlink.toPath(); Files.createSymbolicLink(tempSymlinkPath, target); Files.move(tempSymlinkPath, path, StandardCopyOption.ATOMIC_MOVE); return; } catch (AtomicMoveNotSupportedException e) { } catch (FileAlreadyExistsException e) { } catch (DirectoryNotEmptyException e) { } catch (IOException e) { } catch (SecurityException e) { } catch (InvalidPathException e) { } catch (UnsupportedOperationException e) { } final int maxNumberOfTries = 4; final int timeInMillis = 100; for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) { Files.deleteIfExists(path); try { Files.createSymbolicLink(path, target); break; } catch (FileAlreadyExistsException fileAlreadyExistsException) { if (tryNumber < maxNumberOfTries) { TimeUnit.MILLISECONDS.sleep(timeInMillis); continue; } LOGGER.log(Level.WARNING, "symlink FileAlreadyExistsException thrown {0} times => cannot createSymbolicLink", maxNumberOfTries); throw fileAlreadyExistsException; } } } catch (UnsupportedOperationException e) { PrintStream log = listener.getLogger(); log.print("Symbolic links are not supported on this platform"); Functions.printStackTrace(e, log); } catch (IOException e) { if (Functions.isWindows() && e instanceof FileSystemException) { warnWindowsSymlink(); return; } PrintStream log = listener.getLogger(); log.printf("ln %s %s failed%n",targetPath, new File(baseDir, symlinkPath)); Functions.printStackTrace(e, log); } }

Target:
public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath, @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException { File fileForSymlink = new File(baseDir, symlinkPath); try { Path pathForSymlink = fileToPath(fileForSymlink); Path target = Paths.get(targetPath, MemoryReductionUtil.EMPTY_STRING_ARRAY); if (createSymlinkAtomic(pathForSymlink, fileForSymlink, target, symlinkPath)) { return; } final int maxNumberOfTries = 4; final int timeInMillis = 100; for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) { Files.deleteIfExists(pathForSymlink); try { Files.createSymbolicLink(pathForSymlink, target); break; } catch (FileAlreadyExistsException fileAlreadyExistsException) { if (tryNumber < maxNumberOfTries) { TimeUnit.MILLISECONDS.sleep(timeInMillis); continue; } LOGGER.log(Level.WARNING, "symlink FileAlreadyExistsException thrown {0} times => cannot createSymbolicLink", maxNumberOfTries); throw fileAlreadyExistsException; } } } catch (UnsupportedOperationException e) { PrintStream log = listener.getLogger(); log.print("Symbolic links are not supported on this platform"); Functions.printStackTrace(e, log); } catch (IOException e) { if (Functions.isWindows() && e instanceof FileSystemException) { warnWindowsSymlink(); return; } PrintStream log = listener.getLogger(); log.printf("ln %s %s failed%n", targetPath, fileForSymlink); Functions.printStackTrace(e, log); } }