3434import java .nio .file .SimpleFileVisitor ;
3535import java .nio .file .StandardCopyOption ;
3636import java .nio .file .attribute .BasicFileAttributes ;
37+ import java .util .Comparator ;
3738import java .util .EnumSet ;
3839import java .util .function .Predicate ;
3940import java .util .jar .JarEntry ;
4041import java .util .jar .JarFile ;
4142import java .util .jar .JarInputStream ;
4243import java .util .jar .JarOutputStream ;
4344import java .util .jar .Manifest ;
45+ import java .util .stream .Stream ;
4446import java .util .zip .ZipEntry ;
4547import java .util .zip .ZipInputStream ;
4648import java .util .zip .ZipOutputStream ;
@@ -168,13 +170,13 @@ public static void addToArchive(final File input, final boolean includeDirName,
168170 public static void addToArchive (final File input , final boolean includeDirName ,
169171 final ZipOutputStream output ,
170172 final Predicate <String > fileNameFilter ) throws IOException {
171- final URI baseURI = input .toURI ();
173+ final URI baseUri = input .toURI ();
172174 Files .walkFileTree (input .toPath (), EnumSet .of (FileVisitOption .FOLLOW_LINKS ),
173175 Integer .MAX_VALUE , new SimpleFileVisitor <Path >() {
174176 @ Override
175177 public FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs )
176178 throws IOException {
177- URI uri = baseURI .relativize (dir .toUri ());
179+ URI uri = baseUri .relativize (dir .toUri ());
178180 String entryName =
179181 includeDirName ? input .getName () + "/" + uri .getPath () : uri .getPath ();
180182
@@ -191,7 +193,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
191193 if (fileNameFilter .test (file .getFileName ().toString ())) {
192194 return FileVisitResult .CONTINUE ;
193195 }
194- URI uri = baseURI .relativize (file .toUri ());
196+ URI uri = baseUri .relativize (file .toUri ());
195197 if (uri .getPath ().isEmpty ()) {
196198 // Only happen if the given "input" is a file.
197199 output .putNextEntry (new ZipEntry (file .toFile ().getName ()));
@@ -210,7 +212,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
210212 * Takes a jar or a local directory and prepares a folder to be loaded by classloader. If a jar is
211213 * provided it unpacks a manifest and any nested jars and links original jar into the destination
212214 * folder, so that it would be picked up by classloader to load any classes or resources.
213- *
215+ * <p>
214216 * If a directory is provided, it assumes that this directory already contains the unpacked jar
215217 * contents (ie. this directory was used as the destinationFolder in a previous call to this
216218 * method). In this case, no unpacking is needed. The {@link ClassLoaderFolder} returned will be
@@ -314,6 +316,58 @@ private static File unJar(File jarFile, File destinationFolder, Predicate<String
314316 return destinationFolder ;
315317 }
316318
319+ private static void unJar (ZipInputStream input , File targetDirectory ,
320+ Predicate <String > nameFilter ,
321+ CopyOption ... copyOptions )
322+ throws IOException {
323+
324+ Path targetPath = targetDirectory .toPath ().normalize ();
325+ boolean isSuccess = false ;
326+ boolean isTargetDirectoryNewlyCreated = false ;
327+
328+ try {
329+ // Check if the directory exists before trying to create it
330+ if (!Files .exists (targetPath )) {
331+ Files .createDirectories (targetPath );
332+ isTargetDirectoryNewlyCreated = true ;
333+ }
334+
335+ ZipEntry entry ;
336+ while ((entry = input .getNextEntry ()) != null ) {
337+ String entryName = entry .getName ();
338+
339+ if (!nameFilter .test (entryName )) {
340+ continue ;
341+ }
342+
343+ Path output = targetPath .resolve (entryName ).normalize ();
344+
345+ if (!output .startsWith (targetPath )) {
346+ throw new IllegalArgumentException ("Illegal path detected for Jar Entry : " + entryName + ". This will try to"
347+ + " write outside the target directory, which is not allowed." );
348+ }
349+
350+ if (entry .isDirectory ()) {
351+ Files .createDirectories (output );
352+ } else {
353+ Files .createDirectories (output .getParent ());
354+ Files .copy (input , output , copyOptions );
355+ }
356+ input .closeEntry ();
357+ }
358+ isSuccess = true ;
359+ } finally {
360+ // Cleanup logic: Only delete if not successful AND we created the directory
361+ if (!isSuccess && isTargetDirectoryNewlyCreated && Files .exists (targetPath )) {
362+ try (Stream <Path > paths = Files .walk (targetPath )) {
363+ paths .sorted (Comparator .reverseOrder ())
364+ .map (Path ::toFile )
365+ .forEach (File ::delete );
366+ }
367+ }
368+ }
369+ }
370+
317371 /**
318372 * Search for {@link Manifest} from the given {@link JarInputStream}.
319373 *
@@ -341,28 +395,6 @@ private static Manifest getManifest(JarInputStream jarInput) throws IOException
341395 return null ;
342396 }
343397
344- private static void unJar (ZipInputStream input , File targetDirectory ,
345- Predicate <String > nameFilter ,
346- CopyOption ... copyOptions )
347- throws IOException {
348- Path targetPath = targetDirectory .toPath ();
349- Files .createDirectories (targetPath );
350-
351- ZipEntry entry ;
352- while ((entry = input .getNextEntry ()) != null ) {
353- if (nameFilter .test (entry .getName ())) {
354- Path output = targetPath .resolve (entry .getName ());
355-
356- if (entry .isDirectory ()) {
357- Files .createDirectories (output );
358- } else {
359- Files .createDirectories (output .getParent ());
360- Files .copy (input , output , copyOptions );
361- }
362- }
363- }
364- }
365-
366398 private BundleJarUtil () {
367399 }
368400}
0 commit comments