You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by re...@apache.org on 2023/01/24 07:25:23 UTC

[uima-uimaj] branch main-v2 updated: Issue #294: Porting file-handling enhancements from main

This is an automated email from the ASF dual-hosted git repository.

rec pushed a commit to branch main-v2
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git


The following commit(s) were added to refs/heads/main-v2 by this push:
     new cbbc0ddd0 Issue #294: Porting file-handling enhancements from main
cbbc0ddd0 is described below

commit cbbc0ddd074bd6d10f232e7af586d49caf727e12
Author: Benjamin De Boe <be...@intersystems.com>
AuthorDate: Tue Jan 24 08:25:16 2023 +0100

    Issue #294: Porting file-handling enhancements from main
    
    This change introduces file-handling enhancements from
    apache/uima-uimaj#209 and apache/uima-uimaj#211 to the main-v2
    branch, including the fix addressing CVE-2022-32287.
    
    This meant porting the entire org.apache.uima.util.impl.Constants class
    and also the unit test with associated org.junit.jupiter libraries.
    Given that these are only used in the test scope, it didn't feel too
    outrageous to use them just in this test and keep the others how
    they've always been in v2.
---
 uimaj-core/pom.xml                                 |  13 +
 .../java/org/apache/uima/pear/util/FileUtil.java   | 770 +++++++++------------
 .../java/org/apache/uima/util/impl/Constants.java  |  42 ++
 .../java/org/apache/uima/util/FileUtilsTest.java   |  60 +-
 4 files changed, 430 insertions(+), 455 deletions(-)

diff --git a/uimaj-core/pom.xml b/uimaj-core/pom.xml
index 1b7d8abdd..df15e98ce 100644
--- a/uimaj-core/pom.xml
+++ b/uimaj-core/pom.xml
@@ -37,6 +37,7 @@
     <uimaScmProject>${project.artifactId}</uimaScmProject>
     <postNoticeText>${ibmNoticeText}</postNoticeText>
     <maven.surefire.heap>650M</maven.surefire.heap>
+    <junit-version>5.9.1</junit-version>
   </properties>
   
 	<dependencies>
@@ -53,6 +54,18 @@
 			<version>${project.parent.version}</version>
 			<scope>test</scope>
 		</dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <version>${junit-version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <version>${junit-version}</version>
+      <scope>test</scope>
+    </dependency>
 		<dependency>
 			<groupId>org.assertj</groupId>
 			<artifactId>assertj-core</artifactId>
diff --git a/uimaj-core/src/main/java/org/apache/uima/pear/util/FileUtil.java b/uimaj-core/src/main/java/org/apache/uima/pear/util/FileUtil.java
index f9dc13098..50a8ff970 100644
--- a/uimaj-core/src/main/java/org/apache/uima/pear/util/FileUtil.java
+++ b/uimaj-core/src/main/java/org/apache/uima/pear/util/FileUtil.java
@@ -39,6 +39,9 @@ import java.io.StringWriter;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.nio.charset.Charset;
+import java.nio.file.CopyOption;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -55,11 +58,22 @@ import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
+import org.apache.uima.util.impl.Constants;
+
 /**
  * The <code>FileUtil</code> class provides utility methods for working with general files.
  */
-
 public class FileUtil {
+  private static final char DOT = '.';
+  private static final String UTF8_ENCODING = "UTF-8";
+  private static final String ASCII_ENCODING = "ASCII";
+  private static final String XML_EXTENSION = DOT + "xml";
+  private static final String BACKUP_EXTENSION = DOT + "bak";
+  private static final String ZIP_EXTENSION = DOT + "zip";
+  private static final char UNIX_SEPARATOR_CHAR = '/';
+  private static final char WINDOWS_SEPARATOR_CHAR = '\\';
+  private static final String UNIX_SEPARATOR = String.valueOf(UNIX_SEPARATOR_CHAR);
+  private static final String WINDOWS_SEPARATOR = String.valueOf(WINDOWS_SEPARATOR_CHAR);
 
   /**
    * The <code>FileTimeComparator</code> class allows comparing 'last modified' time in 2 given
@@ -73,6 +87,7 @@ public class FileUtil {
      *           if the arguments' types prevent them from being compared by this
      *           <code>Comparator</code>.
      */
+    @Override
     public int compare(File o1, File o2) throws ClassCastException {
       long t1 = o1.lastModified();
       long t2 = o2.lastModified();
@@ -83,9 +98,9 @@ public class FileUtil {
      * @param obj
      *          The reference object with which to compare.
      * @return <code>true</code> only if the specified object is also a
-     *         <code>FileTimeComparator</code>, and it imposes the same ordering as this
-     *         comparator.
+     *         <code>FileTimeComparator</code>, and it imposes the same ordering as this comparator.
      */
+    @Override
     public boolean equals(Object obj) {
       return (obj instanceof FileTimeComparator);
     }
@@ -110,25 +125,28 @@ public class FileUtil {
      *          The given file extension.
      */
     public DirFileFilter(String dirPath, String fileExt) {
-      _dirPath = (dirPath != null) ? dirPath.replace('\\', '/') : null;
-      if (fileExt != null)
+      _dirPath = (dirPath != null) ? dirPath.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR)
+              : null;
+      if (fileExt != null) {
         _fileExt = fileExt.startsWith(".") ? fileExt.toLowerCase() : "." + fileExt.toLowerCase();
-      else
+      } else {
         _fileExt = null;
+      }
     }
 
     /**
      * @param file
      *          The given file to be tested.
-     * @return <code>true</code> if the given file should be accepted, <code>false</code>
-     *         otherwise.
+     * @return <code>true</code> if the given file should be accepted, <code>false</code> otherwise.
      */
+    @Override
     public boolean accept(File file) {
       boolean dirAccepted = true;
       boolean extAccepted = true;
       if (_dirPath != null) {
         String parentDir = file.getParent();
-        dirAccepted = parentDir != null && parentDir.replace('\\', '/').startsWith(_dirPath);
+        dirAccepted = parentDir != null && parentDir
+                .replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR).startsWith(_dirPath);
       }
       if (_fileExt != null) {
         extAccepted = file.getPath().toLowerCase().endsWith(_fileExt);
@@ -139,7 +157,6 @@ public class FileUtil {
 
   /**
    * The <code>NameFileFilter</code> class allows to filter files based on specified file name.
-   * 
    */
   public static class NameFileFilter implements FileFilter {
     // attributes
@@ -152,24 +169,26 @@ public class FileUtil {
      *          The given file name for filtering.
      */
     public NameFileFilter(String fileName) {
-      _fileName = fileName.replace('\\', '/');
+      _fileName = fileName.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
     }
 
     /**
      * @param file
      *          The given file to be tested.
-     * @return <code>true</code> if the given file should be accepted, <code>false</code>
-     *         otherwise.
+     * @return <code>true</code> if the given file should be accepted, <code>false</code> otherwise.
      */
+    @Override
     public boolean accept(File file) {
-      String filePath = file.getAbsolutePath().replace('\\', '/');
+      String filePath = file.getAbsolutePath().replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
       if (filePath.endsWith(_fileName)) {
         if (filePath.length() > _fileName.length()) {
           char prevChar = filePath.charAt(filePath.length() - _fileName.length() - 1);
-          if (prevChar == ':' || prevChar == '/')
+          if (prevChar == ':' || prevChar == UNIX_SEPARATOR_CHAR) {
             return true;
-        } else
+          }
+        } else {
           return true;
+        }
       }
       return false;
     }
@@ -199,8 +218,8 @@ public class FileUtil {
     }
 
     /**
-     * Create instance of the <code>ExtFileFilter</code> class for a given filename extension. If
-     * a given <code>boolean</code> flag is <code>true</code>, this filename filter is case
+     * Create instance of the <code>ExtFileFilter</code> class for a given filename extension. If a
+     * given <code>boolean</code> flag is <code>true</code>, this filename filter is case
      * insensitive, otherwise it's case sensitive. If the given filename extension does not start
      * from the '.' character, adds this character at the beginning.
      * 
@@ -212,8 +231,9 @@ public class FileUtil {
     public ExtFilenameFilter(String fileExt, boolean ignoreCase) {
       _fileExt = fileExt.startsWith(".") ? fileExt : "." + fileExt;
       _ignoreCase = ignoreCase;
-      if (ignoreCase)
+      if (ignoreCase) {
         _fileExt = _fileExt.toLowerCase();
+      }
     }
 
     /**
@@ -226,6 +246,7 @@ public class FileUtil {
      * @return <code>true</code>, if the given file should be included in the list,
      *         <code>false</code> otherwise.
      */
+    @Override
     public boolean accept(File dir, String name) {
       String fileName = _ignoreCase ? name.toLowerCase() : name;
       return fileName.endsWith(_fileExt);
@@ -234,8 +255,8 @@ public class FileUtil {
 
   /**
    * Deletes all files and subdirectories in a given directory. In case of unsuccessful deletion,
-   * calls the <code>deleteOnExit()</code> method to request that files and subdirs are deleted
-   * when the JVM terminates.
+   * calls the <code>deleteOnExit()</code> method to request that files and subdirs are deleted when
+   * the JVM terminates.
    * 
    * @param directory
    *          The given directory to be cleaned-up.
@@ -251,15 +272,17 @@ public class FileUtil {
         File aFile = allDirFiles[i];
         if (aFile.isDirectory()) {
           counter += cleanUpDirectoryContent(aFile);
-          if (aFile.delete())
+          if (aFile.delete()) {
             counter++;
-          else
+          } else {
             aFile.deleteOnExit();
+          }
         } else if (aFile.isFile()) {
-          if (aFile.delete())
+          if (aFile.delete()) {
             counter++;
-          else
+          } else {
             aFile.deleteOnExit();
+          }
         }
       }
     }
@@ -283,10 +306,11 @@ public class FileUtil {
       for (int i = 0; i < allDirFiles.length; i++) {
         File aFile = allDirFiles[i];
         if (aFile.isFile()) {
-          if (aFile.delete())
+          if (aFile.delete()) {
             counter++;
-          else
+          } else {
             aFile.deleteOnExit();
+          }
         }
       }
     }
@@ -317,16 +341,25 @@ public class FileUtil {
         File file = list.next();
         no++;
         if (no > maxLimit) {
-          if (file.delete())
+          if (file.delete()) {
             counter++;
-          else
+          } else {
             file.deleteOnExit();
+          }
         }
       }
     }
     return counter;
   }
 
+  private static String normalizeToUnix(String aPath) {
+    if (aPath == null) {
+      return null;
+    }
+
+    return aPath.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
+  }
+
   /**
    * Computes relative path to a given file from a given reference directory, if both the reference
    * directory and the file are in the same logical file system (partition).
@@ -343,25 +376,26 @@ public class FileUtil {
    */
   public static String computeRelativePath(File referenceDir, File file) throws IOException {
     // get canonical path expressions
-    String refPath = referenceDir.getCanonicalPath().replace('\\', '/');
-    String filePath = file.getCanonicalPath().replace('\\', '/');
+    String refPath = normalizeToUnix(referenceDir.getCanonicalPath());
+    String filePath = normalizeToUnix(file.getCanonicalPath());
     // compute relative path from reference dir to file dir-tree
     StringBuffer relBuffer = new StringBuffer();
     while (refPath != null && !filePath.startsWith(refPath)) {
       relBuffer.append("../");
-      refPath = (new File(refPath)).getParent();
-      if (refPath != null)
-        refPath = refPath.replace('\\', '/');
+      refPath = normalizeToUnix((new File(refPath)).getParent());
     }
+
     if (refPath != null) {
       // construct relative path
       String subPath = filePath.substring(refPath.length());
-      if (relBuffer.length() == 0)
+      if (relBuffer.length() == 0) {
         relBuffer.append("./");
-      if (subPath.startsWith("/"))
+      }
+      if (subPath.startsWith("/")) {
         relBuffer.append(subPath.substring(1));
-      else
+      } else {
         relBuffer.append(subPath);
+      }
       return relBuffer.toString();
     }
     // relative path does not exist
@@ -379,37 +413,20 @@ public class FileUtil {
    *         otherwise.
    * @throws IOException
    *           If any I/O exception occurred.
+   * @deprecated use Java 7 for this see {@link java.nio.file.Files#copy(Path, Path, CopyOption...)}
    */
+  @Deprecated
   public static boolean copyFile(File source, File destination) throws IOException {
-    boolean completed = false;
-    BufferedInputStream iStream = null;
-    BufferedOutputStream oStream = null;
-    try {
-      iStream = new BufferedInputStream(new FileInputStream(source));
-      oStream = new BufferedOutputStream(new FileOutputStream(destination));
+    try (BufferedInputStream iStream = new BufferedInputStream(new FileInputStream(source));
+            BufferedOutputStream oStream = new BufferedOutputStream(
+                    new FileOutputStream(destination))) {
       byte[] block = new byte[4096];
       int bCount = 0;
       while ((bCount = iStream.read(block)) > 0) {
         oStream.write(block, 0, bCount);
       }
-      iStream.close();
-      oStream.close();
-      completed = true;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
-      if (oStream != null) {
-        try {
-          oStream.close();
-        } catch (Exception e) {
-        }
-      }
     }
-    return completed;
+    return true;
   }
 
   /**
@@ -423,37 +440,21 @@ public class FileUtil {
    *         otherwise.
    * @throws IOException
    *           If any I/O exception occurred.
+   * @deprecated use Java 7 for this see
+   *             {@link java.nio.file.Files#copy(InputStream, Path, CopyOption...)}
    */
+  @Deprecated
   public static boolean copyFile(URL sourceUrl, File destination) throws IOException {
-    boolean completed = false;
-    BufferedInputStream iStream = null;
-    BufferedOutputStream oStream = null;
-    try {
-      iStream = new BufferedInputStream(sourceUrl.openStream());
-      oStream = new BufferedOutputStream(new FileOutputStream(destination));
+    try (BufferedInputStream iStream = new BufferedInputStream(sourceUrl.openStream());
+            BufferedOutputStream oStream = new BufferedOutputStream(
+                    new FileOutputStream(destination))) {
       byte[] block = new byte[4096];
       int bCount = 0;
       while ((bCount = iStream.read(block)) > 0) {
         oStream.write(block, 0, bCount);
       }
-      iStream.close();
-      oStream.close();
-      completed = true;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
-      if (oStream != null) {
-        try {
-          oStream.close();
-        } catch (Exception e) {
-        }
-      }
     }
-    return completed;
+    return true;
   }
 
   /**
@@ -461,8 +462,8 @@ public class FileUtil {
    * 
    * @param rootDir
    *          The given root directory.
-   * @return <code>Collection</code> of <code>File</code> objects, representing subdirectories
-   *         in the given root directory and all its subdirectories.
+   * @return <code>Collection</code> of <code>File</code> objects, representing subdirectories in
+   *         the given root directory and all its subdirectories.
    * 
    * @throws java.io.IOException
    *           If any I/O exception occurs.
@@ -472,12 +473,12 @@ public class FileUtil {
   }
 
   /**
-   * Creates list of subdirectories in a given root directory. If a given <code>boolean</code>
-   * flag is <code>true</code>, all the subdirectories of the given root directory are also
-   * scanned, otherwise only subdirectories in the given root directory are included.
+   * Creates list of subdirectories in a given root directory. If a given <code>boolean</code> flag
+   * is <code>true</code>, all the subdirectories of the given root directory are also scanned,
+   * otherwise only subdirectories in the given root directory are included.
    * 
-   * @return <code>Collection</code> of <code>File</code> objects, representing subdirectories
-   *         in the given root directory.
+   * @return <code>Collection</code> of <code>File</code> objects, representing subdirectories in
+   *         the given root directory.
    * @param rootDir
    *          The given root directory.
    * @param includeSubdirs
@@ -488,17 +489,20 @@ public class FileUtil {
    * @exception java.io.IOException
    *              If any I/O exception occurs.
    */
-  public static Collection<File> createDirList(File rootDir, boolean includeSubdirs) throws IOException {
-    ArrayList<File> listOfDirs = new ArrayList<File>();
+  public static Collection<File> createDirList(File rootDir, boolean includeSubdirs)
+          throws IOException {
+    ArrayList<File> listOfDirs = new ArrayList<>();
     File[] allDirFiles = rootDir.listFiles();
-    if (allDirFiles == null)
+    if (allDirFiles == null) {
       throw new FileNotFoundException("invalid directory specified");
+    }
     for (int i = 0; i < allDirFiles.length; i++) {
       File aFile = allDirFiles[i];
       if (aFile.isDirectory()) {
         listOfDirs.add(aFile);
-        if (includeSubdirs)
+        if (includeSubdirs) {
           listOfDirs.addAll(createDirList(aFile, includeSubdirs));
+        }
       }
     }
     return listOfDirs;
@@ -511,15 +515,15 @@ public class FileUtil {
    * 
    * @param archive
    *          The input archive (JAR) file.
-   * @return <code>Collection</code> of <code>File</code> objects, representing directories in
-   *         the given archive file.
+   * @return <code>Collection</code> of <code>File</code> objects, representing directories in the
+   *         given archive file.
    * @throws IOException
    *           If any I/O exception occurs.
    */
   public static Collection<File> createDirList(JarFile archive) throws IOException {
-    ArrayList<File> listOfDirs = new ArrayList<File>();
+    ArrayList<File> listOfDirs = new ArrayList<>();
     // set root_dir_path = archive_file_path (w/o file name extension)
-    int nameEndIndex = archive.getName().lastIndexOf('.');
+    int nameEndIndex = archive.getName().lastIndexOf(DOT);
     String rootDirPath = (nameEndIndex > 0) ? archive.getName().substring(0, nameEndIndex)
             : archive.getName();
     File rootDir = new File(rootDirPath);
@@ -528,14 +532,15 @@ public class FileUtil {
     while (entries.hasMoreElements()) {
       JarEntry entry = entries.nextElement();
       File file = new File(rootDir, entry.getName());
-      if (entry.isDirectory())
+      if (entry.isDirectory()) {
         listOfDirs.add(file);
-      else {
+      } else {
         // make sure the parent dir is added
         File parentDir = file.getParentFile();
         while (!parentDir.equals(rootDir)) {
-          if (!listOfDirs.contains(parentDir))
+          if (!listOfDirs.contains(parentDir)) {
             listOfDirs.add(parentDir);
+          }
           parentDir = parentDir.getParentFile();
         }
       }
@@ -546,8 +551,8 @@ public class FileUtil {
   /**
    * Creates list of files in a given directory, including all its subdirectories.
    * 
-   * @return <code>Collection</code> of <code>File</code> objects in the given directory,
-   *         including all its subdirectories.
+   * @return <code>Collection</code> of <code>File</code> objects in the given directory, including
+   *         all its subdirectories.
    * @param filesDir
    *          The given directory.
    * 
@@ -560,8 +565,8 @@ public class FileUtil {
 
   /**
    * Creates list of files in a given directory. If a given <code>boolean</code> flag is
-   * <code>true</code>, all the sub-directories of the given directory are also scanned,
-   * otherwise only files in the given directory are included.
+   * <code>true</code>, all the sub-directories of the given directory are also scanned, otherwise
+   * only files in the given directory are included.
    * 
    * @return <code>Collection</code> of <code>File</code> objects in the given directory.
    * @param filesDir
@@ -574,17 +579,20 @@ public class FileUtil {
    * @exception java.io.IOException
    *              If any I/O exception occurs.
    */
-  public static Collection<File> createFileList(File filesDir, boolean includeSubdirs) throws IOException {
-    ArrayList<File> listOfFiles = new ArrayList<File>();
+  public static Collection<File> createFileList(File filesDir, boolean includeSubdirs)
+          throws IOException {
+    ArrayList<File> listOfFiles = new ArrayList<>();
     File[] allDirFiles = filesDir.listFiles();
-    if (allDirFiles == null)
+    if (allDirFiles == null) {
       throw new FileNotFoundException("invalid directory specified");
+    }
     for (int i = 0; i < allDirFiles.length; i++) {
       File aFile = allDirFiles[i];
-      if (aFile.isDirectory() && includeSubdirs)
+      if (aFile.isDirectory() && includeSubdirs) {
         listOfFiles.addAll(createFileList(aFile, includeSubdirs));
-      else if (!aFile.isDirectory())
+      } else if (!aFile.isDirectory()) {
         listOfFiles.add(aFile);
+      }
     }
     return listOfFiles;
   }
@@ -595,15 +603,15 @@ public class FileUtil {
    * 
    * @param archive
    *          The input archive (JAR) file.
-   * @return <code>Collection</code> of <code>File</code> objects, representing files in the
-   *         given archive file.
+   * @return <code>Collection</code> of <code>File</code> objects, representing files in the given
+   *         archive file.
    * @throws IOException
    *           If any I/O exception occurs.
    */
   public static Collection<File> createFileList(JarFile archive) throws IOException {
-    ArrayList<File> listOfFiles = new ArrayList<File>();
+    ArrayList<File> listOfFiles = new ArrayList<>();
     // set root_dir_path = archive_file_path (w/o file name extension)
-    int nameEndIndex = archive.getName().lastIndexOf('.');
+    int nameEndIndex = archive.getName().lastIndexOf(DOT);
     String rootDirPath = (nameEndIndex > 0) ? archive.getName().substring(0, nameEndIndex)
             : archive.getName();
     File rootDir = new File(rootDirPath);
@@ -612,8 +620,9 @@ public class FileUtil {
     while (entries.hasMoreElements()) {
       JarEntry entry = entries.nextElement();
       File file = new File(rootDir, entry.getName());
-      if (!entry.isDirectory())
+      if (!entry.isDirectory()) {
         listOfFiles.add(file);
+      }
     }
     return listOfFiles;
   }
@@ -632,24 +641,30 @@ public class FileUtil {
    * @return The <code>File</code> object denoting the newly created file.
    * @throws IOException
    *           If a temporary directory not found or other I/O exception occurred.
+   * @deprecated use Java 7 method for this see
+   *             {@link java.io.File#createTempFile(String, String, File)}
    */
+  @Deprecated
   public static File createTempFile(String prefix, String suffix) throws IOException {
     String tempDirPath = System.getProperty("java.io.tmpdir");
-    if (tempDirPath == null)
+    if (tempDirPath == null) {
       tempDirPath = System.getProperty("user.home");
-    if (tempDirPath == null)
+    }
+    if (tempDirPath == null) {
       throw new IOException("could not find temporary directory");
+    }
     File tempDir = new File(tempDirPath);
-    if (!tempDir.isDirectory())
+    if (!tempDir.isDirectory()) {
       throw new IOException("temporary directory not available");
+    }
     return File.createTempFile(prefix, suffix, tempDir);
   }
 
   /**
    * Deletes a given directory, including all its subdirectories and files. Returns
-   * <code>true</code> if the deletion was successful, otherwise returns <code>false</code>. In
-   * case of unsuccessful deletion, calls <code>deleteOnExit()</code> method to request that files
-   * and subdirs be deleted when the virtual machine terminates.
+   * <code>true</code> if the deletion was successful, otherwise returns <code>false</code>. In case
+   * of unsuccessful deletion, calls <code>deleteOnExit()</code> method to request that files and
+   * subdirs be deleted when the virtual machine terminates.
    * 
    * @param dir
    *          The given directory to be deleted.
@@ -664,9 +679,9 @@ public class FileUtil {
     // first, delete plain files and sub-directories (recursive)
     for (int i = 0; i < fileList.length; i++) {
       File entry = fileList[i];
-      if (entry.isDirectory())
+      if (entry.isDirectory()) {
         done = deleteDirectory(entry);
-      else if (!entry.delete()) {
+      } else if (!entry.delete()) {
         entry.deleteOnExit();
         done = false;
       }
@@ -751,48 +766,48 @@ public class FileUtil {
           throws IOException {
     long totalBytes = 0;
     byte[] block = new byte[4096];
+
+    String prefix = normalizeToUnix(targetDir.getCanonicalPath());
+    if (!prefix.endsWith(UNIX_SEPARATOR)) {
+      prefix = prefix + UNIX_SEPARATOR_CHAR;
+    }
+
     Enumeration<JarEntry> jarList = jarFile.entries();
     while (jarList.hasMoreElements()) {
       JarEntry jarEntry = jarList.nextElement();
-      if (!jarEntry.isDirectory()) {
-        // check that file is accepted
-        if (filter != null && !filter.accept(new File(jarEntry.getName())))
-          continue;
-        // extract file
-        File file = new File(targetDir, jarEntry.getName());
-        // make sure the file directory exists
-        File dir = file.getParentFile();
-        if (!dir.exists() && !dir.mkdirs())
-          throw new IOException("could not create directory " + dir.getAbsolutePath());
-        BufferedInputStream iStream = null;
-        BufferedOutputStream oStream = null;
-        try {
-          iStream = new BufferedInputStream(jarFile.getInputStream(jarEntry));
-          oStream = new BufferedOutputStream(new FileOutputStream(file));
-          int bCount = 0;
-          while ((bCount = iStream.read(block)) > 0) {
-            totalBytes += bCount;
-            oStream.write(block, 0, bCount);
-          }
-          iStream.close();
-          oStream.close();
-        } finally {
-          // close streams
-          if (iStream != null) {
-            try {
-              iStream.close();
-            } catch (Exception e) {
-            }
-          }
-          if (oStream != null) {
-            try {
-              oStream.close();
-            } catch (Exception e) {
-            }
-          }
+      if (jarEntry.isDirectory()) {
+        continue;
+      }
+
+      // check that file is accepted
+      if (filter != null && !filter.accept(new File(jarEntry.getName()))) {
+        continue;
+      }
+
+      // make sure the file directory exists
+      File file = new File(targetDir, jarEntry.getName());
+
+      if (!normalizeToUnix(file.getCanonicalPath()).startsWith(prefix)) {
+        throw new IOException("Can only write within target folder [" + targetDir.getAbsolutePath()
+                + "]. Please validate ZIP contents.");
+      }
+
+      File dir = file.getParentFile();
+      if (!dir.exists() && !dir.mkdirs()) {
+        throw new IOException("Could not create directory [" + dir.getAbsolutePath() + "]");
+      }
+
+      // extract file
+      try (BufferedInputStream iStream = new BufferedInputStream(jarFile.getInputStream(jarEntry));
+              BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(file))) {
+        int bCount = 0;
+        while ((bCount = iStream.read(block)) > 0) {
+          totalBytes += bCount;
+          oStream.write(block, 0, bCount);
         }
       }
     }
+
     return totalBytes;
   }
 
@@ -820,15 +835,16 @@ public class FileUtil {
    */
   public static String getFileNameExtension(String fileName) {
     StringBuffer buffer = new StringBuffer();
-    int begIndex = fileName.lastIndexOf('.');
+    int begIndex = fileName.lastIndexOf(DOT);
     if (begIndex > 0) {
-      buffer.append('.');
+      buffer.append(DOT);
       for (int i = begIndex + 1; i < fileName.length(); i++) {
         char ch = fileName.charAt(i);
-        if (Character.isLetterOrDigit(ch))
+        if (Character.isLetterOrDigit(ch)) {
           buffer.append(ch);
-        else
+        } else {
           break;
+        }
       }
     }
     return buffer.toString();
@@ -840,14 +856,16 @@ public class FileUtil {
    * @param fileLocation
    *          The given file location - local file path or URL.
    * @return The given file size, if the specified file can be accessed, -1 otherwise.
+   * @deprecated use Java 7 method for this see {@link java.nio.file.Files#size(Path)}
    */
+  @Deprecated
   public static long getFileSize(String fileLocation) {
     long fileSize = 0;
     // choose file size method: local FS or HTTP
     File file = new File(fileLocation);
-    if (file.isFile())
+    if (file.isFile()) {
       fileSize = file.length();
-    else {
+    } else {
       try {
         URL fileUrl = new URL(fileLocation);
         URLConnection urlConn = fileUrl.openConnection();
@@ -872,46 +890,50 @@ public class FileUtil {
    * @return The relative path of the given object, located in the given root directory.
    */
   public static String getRelativePath(File rootDir, String absolutePath) {
-    String rootDirPath = rootDir.getAbsolutePath().replace('\\', '/');
-    String objectPath = absolutePath.replace('\\', '/');
-    if (objectPath.startsWith(rootDirPath))
+    String rootDirPath = normalizeToUnix(rootDir.getAbsolutePath());
+    String objectPath = normalizeToUnix(absolutePath);
+    if (objectPath.startsWith(rootDirPath)) {
       objectPath = objectPath.substring(rootDirPath.length());
-    if (objectPath.startsWith("/"))
+    }
+    if (objectPath.startsWith("/")) {
       objectPath = objectPath.substring(1);
+    }
     return objectPath;
   }
 
   /**
    * Makes and attempt to identify possible UTF signature (BOM) in a given sequence of bytes.
-   * Returns the identified UTF signature name or <code>null</code>, if the signature could not
-   * be identified. For more on UTF and its signatures see <a
-   * href="http://www.unicode.org/faq/utf_bom.html" target="_blank"> FAQ - UTF and BOM</a>.
+   * Returns the identified UTF signature name or <code>null</code>, if the signature could not be
+   * identified. For more on UTF and its signatures see
+   * <a href="http://www.unicode.org/faq/utf_bom.html" target="_blank"> FAQ - UTF and BOM</a>.
    * 
    * @param prefix
    *          The given sequence of bytes to analyze.
    * @param length
    *          The length of the given sequence of bytes.
-   * @return The UTF signature name or <code>null</code>, if the signature could not be
-   *         identified.
+   * @return The UTF signature name or <code>null</code>, if the signature could not be identified.
    */
   public static String identifyUtfSignature(int[] prefix, int length) {
     String utfSignature = null;
     if (length == 3) {
       // check for UTF-8 signature
-      if (prefix[0] == 0xEF && prefix[1] == 0xBB && prefix[2] == 0xBF)
-        utfSignature = "UTF-8";
+      if (prefix[0] == 0xEF && prefix[1] == 0xBB && prefix[2] == 0xBF) {
+        utfSignature = UTF8_ENCODING;
+      }
     } else if (length == 2) {
       // check for UTF-16 signature
-      if (prefix[0] == 0xFE && prefix[1] == 0xFF)
+      if (prefix[0] == 0xFE && prefix[1] == 0xFF) {
         utfSignature = "UTF-16BE";
-      else if (prefix[0] == 0xFF && prefix[1] == 0xFE)
+      } else if (prefix[0] == 0xFF && prefix[1] == 0xFE) {
         utfSignature = "UTF-16LE";
+      }
     } else if (length == 4) {
       // check for UTF-32 signature
-      if (prefix[0] == 0x00 && prefix[1] == 0x00 && prefix[2] == 0xFE && prefix[3] == 0xFF)
+      if (prefix[0] == 0x00 && prefix[1] == 0x00 && prefix[2] == 0xFE && prefix[3] == 0xFF) {
         utfSignature = "UTF-32BE";
-      else if (prefix[0] == 0xFF && prefix[1] == 0xFE && prefix[2] == 0x00 && prefix[3] == 0x00)
+      } else if (prefix[0] == 0xFF && prefix[1] == 0xFE && prefix[2] == 0x00 && prefix[3] == 0x00) {
         utfSignature = "UTF-32LE";
+      }
     }
     return utfSignature;
   }
@@ -928,23 +950,9 @@ public class FileUtil {
    *           If an I/O exception occurred.
    */
   public static boolean isAsciiFile(File textFile) throws IOException {
-    boolean isAscii = true;
-    FileInputStream iStream = null;
-    try {
-      iStream = new FileInputStream(textFile);
-      isAscii = isAsciiStream(iStream);
-      iStream.close();
-    } catch (IOException exc) {
-      isAscii = false;
-      throw exc;
-    } finally {
-      if (iStream != null)
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
+    try (FileInputStream iStream = new FileInputStream(textFile)) {
+      return isAsciiStream(iStream);
     }
-    return isAscii;
   }
 
   /**
@@ -960,17 +968,12 @@ public class FileUtil {
    */
   public static boolean isAsciiStream(InputStream iStream) throws IOException {
     boolean isAscii = true;
-    try {
-      int nextByte = 0;
-      while ((nextByte = iStream.read()) >= 0) {
-        if (nextByte > 127) {
-          isAscii = false;
-          break;
-        }
+    int nextByte = 0;
+    while ((nextByte = iStream.read()) >= 0) {
+      if (nextByte > 127) {
+        isAscii = false;
+        break;
       }
-    } catch (IOException exc) {
-      isAscii = false;
-      throw exc;
     }
     return isAscii;
   }
@@ -986,18 +989,19 @@ public class FileUtil {
    */
   public static String[] loadListOfStrings(BufferedReader iStream) throws IOException {
     String[] outputArray = null;
-    List<String> outputList = new ArrayList<String>();
+    List<String> outputList = new ArrayList<>();
     String line = null;
     while ((line = iStream.readLine()) != null) {
       String string = line.trim();
-      if (string.length() > 0)
+      if (string.length() > 0) {
         outputList.add(string);
+      }
     }
     if (outputList.size() > 0) {
       outputArray = new String[outputList.size()];
       outputList.toArray(outputArray);
     }
-    return (outputArray != null) ? outputArray : new String[0];
+    return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
   }
 
   /**
@@ -1009,24 +1013,17 @@ public class FileUtil {
    * @return The array of non-empty strings loaded from the given text file.
    * @throws IOException
    *           If any I/O exception occurred.
+   * @deprecated use Java 7 method for this see
+   *             {@link java.nio.file.Files#readAllLines(Path, Charset)}
    */
+  @Deprecated
   public static String[] loadListOfStrings(File textFile) throws IOException {
-    BufferedReader iStream = null;
-    String[] outputArray = null;
-    try {
-      iStream = new BufferedReader(new InputStreamReader(new FileInputStream(textFile)));
+    String[] outputArray;
+    try (BufferedReader iStream = new BufferedReader(
+            new InputStreamReader(new FileInputStream(textFile)))) {
       outputArray = loadListOfStrings(iStream);
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
     }
-    return (outputArray != null) ? outputArray : new String[0];
+    return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
   }
 
   /**
@@ -1042,22 +1039,12 @@ public class FileUtil {
     URLConnection urlConnection = textFileURL.openConnection();
     // See https://issues.apache.org/jira/browse/UIMA-1746
     urlConnection.setUseCaches(false);
-    BufferedReader iStream = null;
-    String[] outputArray = null;
-    try {
-      iStream = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
+    String[] outputArray;
+    try (BufferedReader iStream = new BufferedReader(
+            new InputStreamReader(urlConnection.getInputStream()))) {
       outputArray = loadListOfStrings(iStream);
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
     }
-    return (outputArray != null) ? outputArray : new String[0];
+    return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
   }
 
   /**
@@ -1067,29 +1054,20 @@ public class FileUtil {
    *          The given properties file path in the JAR file.
    * @param jarFile
    *          The given JAR file.
-   * @return <code>Properties</code> object containing loaded properties, or <code>null</code>,
-   *         if the properties file was not found in the given JAR file.
+   * @return <code>Properties</code> object containing loaded properties, or <code>null</code>, if
+   *         the properties file was not found in the given JAR file.
    * @throws IOException
    *           If any I/O exception occurred.
    */
   public static Properties loadPropertiesFromJar(String propFilePath, JarFile jarFile)
           throws IOException {
     Properties properties = null;
-    String name = propFilePath.replace('\\', '/');
+    String name = normalizeToUnix(propFilePath);
     JarEntry jarEntry = jarFile.getJarEntry(name);
     if (jarEntry != null) {
-      InputStream iStream = null;
-      try {
-        iStream = jarFile.getInputStream(jarEntry);
+      try (InputStream iStream = jarFile.getInputStream(jarEntry)) {
         properties = new Properties();
         properties.load(iStream);
-      } finally {
-        if (iStream != null) {
-          try {
-            iStream.close();
-          } catch (Exception e) {
-          }
-        }
       }
     }
     return properties;
@@ -1105,26 +1083,13 @@ public class FileUtil {
    *           If any I/O exception occurs.
    */
   public static String loadTextFile(BufferedReader iStream) throws IOException {
-    StringWriter buffer = null;
-    PrintWriter writer = null;
-    try {
-      buffer = new StringWriter();
-      writer = new PrintWriter(buffer);
+    try (StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer);) {
       String line = null;
-      while ((line = iStream.readLine()) != null)
+      while ((line = iStream.readLine()) != null) {
         writer.println(line);
-      writer.flush();
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (writer != null) {
-        try {
-          writer.close();
-        } catch (Exception e) {
-        }
       }
+      return buffer.toString();
     }
-    return buffer.toString();
   }
 
   /**
@@ -1135,24 +1100,15 @@ public class FileUtil {
    *          The given text file.
    * @throws IOException
    *           If any I/O exception occurs.
+   * @deprecated use main file util for this, see
+   *             {@link org.apache.uima.util.FileUtils#file2String(File)} if using the default
+   *             charset is OK
    */
+  @Deprecated
   public static String loadTextFile(File textFile) throws IOException {
-    BufferedReader iStream = null;
-    String content = null;
-    try {
-      iStream = new BufferedReader(new FileReader(textFile));
-      content = loadTextFile(iStream);
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
+    try (BufferedReader iStream = new BufferedReader(new FileReader(textFile))) {
+      return loadTextFile(iStream);
     }
-    return content;
   }
 
   /**
@@ -1165,24 +1121,16 @@ public class FileUtil {
    *          The given text file encoding name.
    * @throws IOException
    *           If any I/O exception occurs.
+   * @deprecated use main file util for this, see
+   *             {@link org.apache.uima.util.FileUtils#file2String(File, String)} if using the
+   *             default Charset is OK
    */
+  @Deprecated
   public static String loadTextFile(File textFile, String encoding) throws IOException {
-    BufferedReader iStream = null;
-    String content = null;
-    try {
-      iStream = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), encoding));
-      content = loadTextFile(iStream);
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
+    try (BufferedReader iStream = new BufferedReader(
+            new InputStreamReader(new FileInputStream(textFile), encoding))) {
+      return loadTextFile(iStream);
     }
-    return content;
   }
 
   /**
@@ -1197,7 +1145,7 @@ public class FileUtil {
   public static String loadTextFile(URL textFileURL) throws IOException {
     URLConnection urlConnection = textFileURL.openConnection();
     // See https://issues.apache.org/jira/browse/UIMA-1746
-    urlConnection.setUseCaches(false);    
+    urlConnection.setUseCaches(false);
     return loadTextFile(urlConnection);
   }
 
@@ -1211,24 +1159,12 @@ public class FileUtil {
    *           If any I/O exception occurs.
    */
   public static String loadTextFile(URLConnection urlConnection) throws IOException {
-    BufferedReader iStream = null;
-    String content = null;
     // See https://issues.apache.org/jira/browse/UIMA-1746
-    urlConnection.setUseCaches(false);    
-    try {
-      iStream = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
-      content = loadTextFile(iStream);
-    } catch (IOException exc) {
-      throw exc;
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
+    urlConnection.setUseCaches(false);
+    try (BufferedReader iStream = new BufferedReader(
+            new InputStreamReader(urlConnection.getInputStream()))) {
+      return loadTextFile(iStream);
     }
-    return content;
   }
 
   /**
@@ -1238,27 +1174,19 @@ public class FileUtil {
    *          The specified text file path inside the JAR file.
    * @param jarFile
    *          The given JAR file.
-   * @return The content of the text specified file, or <code>null</code>, if the text file was
-   *         not found in the given JAR file.
+   * @return The content of the text specified file, or <code>null</code>, if the text file was not
+   *         found in the given JAR file.
    * @throws IOException
    *           If any I/O exception occurs.
    */
   public static String loadTextFileFromJar(String filePath, JarFile jarFile) throws IOException {
     String content = null;
-    String name = filePath.replace('\\', '/');
+    String name = normalizeToUnix(filePath);
     JarEntry jarEntry = jarFile.getJarEntry(name);
     if (jarEntry != null) {
-      BufferedReader iStream = null;
-      try {
-        iStream = new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)));
+      try (BufferedReader iStream = new BufferedReader(
+              new InputStreamReader(jarFile.getInputStream(jarEntry)))) {
         content = loadTextFile(iStream);
-      } finally {
-        if (iStream != null) {
-          try {
-            iStream.close();
-          } catch (Exception e) {
-          }
-        }
       }
     }
     return content;
@@ -1274,7 +1202,7 @@ public class FileUtil {
   public static String localPathToFileUrl(String path) {
     // get absolute path
     File file = new File(path);
-    String absPath = file.getAbsolutePath().replace('\\', '/');
+    String absPath = normalizeToUnix(file.getAbsolutePath());
     // construct file URL
     StringBuffer urlBuffer = new StringBuffer("file:///");
     urlBuffer.append(absPath.replace(':', '|'));
@@ -1299,12 +1227,15 @@ public class FileUtil {
    *         otherwise.
    * @throws IOException
    *           If any I/O exception occurred.
+   * @deprecated use Java 7 for this see {@link java.nio.file.Files#move(Path, Path, CopyOption...)}
    */
+  @Deprecated
   public static boolean moveFile(File source, File destinationDir) throws IOException {
     boolean completed = false;
     File destination = new File(destinationDir, source.getName());
-    if (destination.exists())
+    if (destination.exists()) {
       destination.delete();
+    }
     if (copyFile(source, destination)) {
       completed = source.delete();
     }
@@ -1312,9 +1243,9 @@ public class FileUtil {
   }
 
   /**
-   * Replaces all occurrences of a given regular expression with a given string in a given text file.
-   * Supports only 1 file encoding - ASCII - for all general text files. Supports 2 encodings -
-   * UTF-8 (ASCII) and UTF-16 for XML files.
+   * Replaces all occurrences of a given regular expression with a given string in a given text
+   * file. Supports only 1 file encoding - ASCII - for all general text files. Supports 2 encodings
+   * - UTF-8 (ASCII) and UTF-16 for XML files.
    * 
    * @param textFile
    *          The given text file.
@@ -1330,23 +1261,22 @@ public class FileUtil {
           throws IOException {
     int counter = 0;
     // for general text file - supporting ASCII encoding only
-    String encoding = "ASCII";
+    String encoding = ASCII_ENCODING;
     // check file extension
-    int extIndex = textFile.getName().lastIndexOf('.');
+    int extIndex = textFile.getName().lastIndexOf(DOT);
     String fileExt = (extIndex > 0) ? textFile.getName().substring(extIndex) : null;
-    if (".xml".equalsIgnoreCase(fileExt)) {
+    if (XML_EXTENSION.equalsIgnoreCase(fileExt)) {
       // for XML file - supporting UTF-8 (ASCII) and UTF-16 encodings
       String xmlEncoding = XMLUtil.detectXmlFileEncoding(textFile);
       if (xmlEncoding != null) {
         encoding = xmlEncoding;
       } else {
-        encoding = "UTF-8";
+        encoding = UTF8_ENCODING;
       }
     }
     // load text file, using supported encoding
     String fileContent = loadTextFile(textFile, encoding);
-    BufferedReader sReader = null;
-    PrintStream fStream = null;
+
     boolean done = false;
     File backupFile = null;
     // get pattern for given regex
@@ -1355,51 +1285,48 @@ public class FileUtil {
     String replaceWith = StringUtil.toRegExpReplacement(replacement);
     try {
       // save backup copy of input file
-      backupFile = new File(textFile.getAbsolutePath() + ".bak");
-      if (backupFile.exists())
+      backupFile = new File(textFile.getAbsolutePath() + BACKUP_EXTENSION);
+      if (backupFile.exists()) {
         backupFile.delete();
-      if (!textFile.renameTo(backupFile))
+      }
+      if (!textFile.renameTo(backupFile)) {
         throw new IOException("can't save backup copy of " + textFile.getAbsolutePath());
-      sReader = new BufferedReader(new StringReader(fileContent));
-      fStream = new PrintStream(new FileOutputStream(textFile), true, encoding);
-      String srcLine = null;
-      while ((srcLine = sReader.readLine()) != null) {
-        // count pattern matches in the source string
-        Matcher matcher = pattern.matcher(srcLine);
-        while (matcher.find())
-          counter++;
-        // replace all pattern matches in the source string
-        String resLine = srcLine.replaceAll(subStringRegex, replaceWith);
-        fStream.println(resLine);
       }
-      fStream.close();
+      try (BufferedReader sReader = new BufferedReader(new StringReader(fileContent));
+              PrintStream fStream = new PrintStream(new FileOutputStream(textFile), true,
+                      encoding);) {
+        String srcLine = null;
+        while ((srcLine = sReader.readLine()) != null) {
+          // count pattern matches in the source string
+          Matcher matcher = pattern.matcher(srcLine);
+          while (matcher.find()) {
+            counter++;
+          }
+          // replace all pattern matches in the source string
+          String resLine = srcLine.replaceAll(subStringRegex, replaceWith);
+          fStream.println(resLine);
+        }
+      }
       done = true;
     } catch (IOException exc) {
       throw exc;
     } catch (Throwable err) {
-      if (err instanceof IOException)
+      if (err instanceof IOException) {
         throw new IOException(err.toString() + " in " + textFile.getAbsolutePath());
+      }
       throw new RuntimeException(err.toString() + " in " + textFile.getAbsolutePath());
     } finally {
-      if (sReader != null) {
-        try {
-          sReader.close();
-        } catch (Exception e) {
-        }
-      }
-      if (fStream != null) {
-        try {
-          fStream.close();
-        } catch (Exception e) {
-        }
-      }
       if (done) {
         // remove backup file
-        backupFile.delete();
+        if (backupFile != null) {
+          backupFile.delete();
+        }
       } else {
         // restore input file
         textFile.delete();
-        backupFile.renameTo(textFile);
+        if (backupFile != null) {
+          backupFile.renameTo(textFile);
+        }
       }
     }
     return counter;
@@ -1413,7 +1340,7 @@ public class FileUtil {
    * @return The list of files sorted by the 'last modified' time in the descending order.
    */
   public static SortedSet<File> sortFileListByTime(Collection<File> fileList) {
-    TreeSet<File> set = new TreeSet<File>(new FileTimeComparator());
+    TreeSet<File> set = new TreeSet<>(new FileTimeComparator());
     set.addAll(fileList);
     return set;
   }
@@ -1430,7 +1357,7 @@ public class FileUtil {
    */
   public static File zipDirectory(File dir2zip) throws IOException {
     // construct zipped file path
-    String zipFileName = dir2zip.getName() + ".zip";
+    String zipFileName = dir2zip.getName() + ZIP_EXTENSION;
     File zipFile = new File(dir2zip, zipFileName);
     return zipDirectory(dir2zip, zipFile);
   }
@@ -1447,21 +1374,12 @@ public class FileUtil {
    *           If any I/O exception occurred.
    */
   public static File zipDirectory(File dir2zip, File zippedFile) throws IOException {
-    ZipOutputStream zoStream = null;
-    try {
+    try (ZipOutputStream zoStream = new ZipOutputStream(new FileOutputStream(zippedFile))) {
       // open compressed output stream
-      zoStream = new ZipOutputStream(new FileOutputStream(zippedFile));
       // add output zip file to exclusions
       File[] excludeFiles = new File[1];
       excludeFiles[0] = zippedFile;
       zipDirectory(dir2zip, zoStream, dir2zip, excludeFiles);
-    } finally {
-      if (zoStream != null) {
-        try {
-          zoStream.close();
-        } catch (Exception e) {
-        }
-      }
     }
     return zippedFile;
   }
@@ -1469,8 +1387,8 @@ public class FileUtil {
   /**
    * Zips the contents of a given directory to a given ZIP output stream. Paths of file entries in
    * the ZIP stream are taken relatively to a given reference directory. If the reference directory
-   * is <code>null</code>, the file paths are taken relatively to the given directory to be
-   * zipped. The method allows to specify the list of files (or dirs) that should not be zipped.
+   * is <code>null</code>, the file paths are taken relatively to the given directory to be zipped.
+   * The method allows to specify the list of files (or dirs) that should not be zipped.
    * 
    * @param dir2zip
    *          The given directory to be zipped.
@@ -1488,48 +1406,43 @@ public class FileUtil {
           File referenceDir, File[] excludeFiles) throws IOException {
     byte[] block = new byte[4096];
     int inBytes = 0;
-    FileInputStream iStream = null;
-    try {
-      // get list of all files/dirs in the given directory
-      File[] dirFileList = dir2zip.listFiles();
-      // compress all files and sub-dirs
-      for (int i = 0; i < dirFileList.length; i++) {
-        File entry = dirFileList[i];
-        // check if this entry is not in the list of exclusions
-        boolean isExcluded = false;
-        for (int n = 0; n < excludeFiles.length; n++) {
-          if (entry.equals(excludeFiles[n])) {
-            isExcluded = true;
-            break;
-          }
+
+    // get list of all files/dirs in the given directory
+    File[] dirFileList = dir2zip.listFiles();
+    // compress all files and sub-dirs
+    for (int i = 0; i < dirFileList.length; i++) {
+      File entry = dirFileList[i];
+      // check if this entry is not in the list of exclusions
+      boolean isExcluded = false;
+      for (int n = 0; n < excludeFiles.length; n++) {
+        if (entry.equals(excludeFiles[n])) {
+          isExcluded = true;
+          break;
         }
-        if (isExcluded)
-          continue;
-        // for each file - add ZipEntry and compress the file
-        if (entry.isFile()) {
-          // open input stream
-          iStream = new FileInputStream(entry);
+      }
+      if (isExcluded) {
+        continue;
+      }
+      // for each file - add ZipEntry and compress the file
+      if (entry.isFile()) {
+        // open input stream
+        try (FileInputStream iStream = new FileInputStream(entry)) {
           // put ZipEntry for the file
-          String zipEntryName = (referenceDir != null) ? getRelativePath(referenceDir, entry
-                  .getAbsolutePath()) : getRelativePath(dir2zip, entry.getAbsolutePath());
+          String zipEntryName = (referenceDir != null)
+                  ? getRelativePath(referenceDir, entry.getAbsolutePath())
+                  : getRelativePath(dir2zip, entry.getAbsolutePath());
           ZipEntry zipEntry = new ZipEntry(zipEntryName);
           zoStream.putNextEntry(zipEntry);
           // read input stream and write to output stream
-          while ((inBytes = iStream.read(block)) > 0)
+          while ((inBytes = iStream.read(block)) > 0) {
             zoStream.write(block, 0, inBytes);
-          // close input stream
-          iStream.close();
-        } else if (entry.isDirectory()) // zip sub-dir recursively
-          zipDirectory(entry, zoStream, referenceDir, excludeFiles);
-      }
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
+          }
         }
+      } else if (entry.isDirectory()) { // zip sub-dir recursively
+        zipDirectory(entry, zoStream, referenceDir, excludeFiles);
       }
     }
+
     return zoStream;
   }
 
@@ -1546,9 +1459,9 @@ public class FileUtil {
   public static File zipFile(File file2zip) throws IOException {
     // construct zipped file path
     String zipFileName = file2zip.getName();
-    int extIndex = zipFileName.lastIndexOf('.');
-    zipFileName = (extIndex >= 0) ? zipFileName.substring(0, extIndex) + ".zip" : zipFileName
-            + ".zip";
+    int extIndex = zipFileName.lastIndexOf(DOT);
+    zipFileName = (extIndex >= 0) ? zipFileName.substring(0, extIndex) + ZIP_EXTENSION
+            : zipFileName + ZIP_EXTENSION;
     File zipFile = new File(file2zip.getParentFile(), zipFileName);
     return zipFile(file2zip, zipFile);
   }
@@ -1567,32 +1480,15 @@ public class FileUtil {
   public static File zipFile(File file2zip, File zippedFile) throws IOException {
     byte[] block = new byte[4096];
     int inBytes = 0;
-    FileInputStream iStream = null;
-    ZipOutputStream oStream = null;
-    try {
-      // open input stream
-      iStream = new FileInputStream(file2zip);
+    try (FileInputStream iStream = new FileInputStream(file2zip);
+            ZipOutputStream oStream = new ZipOutputStream(new FileOutputStream(zippedFile));) {
       // create ZipEntry, using input file name
       ZipEntry zipEntry = new ZipEntry(file2zip.getName());
-      // open compressed output stream
-      oStream = new ZipOutputStream(new FileOutputStream(zippedFile));
       // add new ZipEntry
       oStream.putNextEntry(zipEntry);
       // read input stream and write to output stream
-      while ((inBytes = iStream.read(block)) > 0)
+      while ((inBytes = iStream.read(block)) > 0) {
         oStream.write(block, 0, inBytes);
-    } finally {
-      if (iStream != null) {
-        try {
-          iStream.close();
-        } catch (Exception e) {
-        }
-      }
-      if (oStream != null) {
-        try {
-          oStream.close();
-        } catch (Exception e) {
-        }
       }
     }
     return zippedFile;
diff --git a/uimaj-core/src/main/java/org/apache/uima/util/impl/Constants.java b/uimaj-core/src/main/java/org/apache/uima/util/impl/Constants.java
new file mode 100644
index 000000000..789e802d9
--- /dev/null
+++ b/uimaj-core/src/main/java/org/apache/uima/util/impl/Constants.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.uima.util.impl;
+
+import java.io.File;
+import java.net.URL;
+
+import org.apache.uima.cas.impl.FeatureImpl;
+import org.apache.uima.jcas.cas.TOP;
+import org.apache.uima.resource.metadata.ConfigurationParameter;
+
+/**
+ * Constants
+ */
+public interface Constants {
+  String[] EMPTY_STRING_ARRAY = new String[0];
+  FeatureImpl[] EMPTY_FEATURE_ARRAY = new FeatureImpl[0];
+  int[] EMPTY_INT_ARRAY = new int[0];
+  Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
+  char[] EMPTY_CHAR_ARRAY = new char[0];
+  TOP[] EMPTY_TOP_ARRAY = new TOP[0];
+  File[] EMPTY_FILE_ARRAY = new File[0];
+  URL[] EMPTY_URL_ARRAY = new URL[0];
+  ConfigurationParameter[] EMPTY_CONFIG_PARM_ARRAY = new ConfigurationParameter[0];
+  Object[] EMPTY_OBJ_ARRAY = new Object[0];
+}
diff --git a/uimaj-core/src/test/java/org/apache/uima/util/FileUtilsTest.java b/uimaj-core/src/test/java/org/apache/uima/util/FileUtilsTest.java
index 7119bdc92..9c1b60ada 100644
--- a/uimaj-core/src/test/java/org/apache/uima/util/FileUtilsTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/util/FileUtilsTest.java
@@ -18,13 +18,24 @@
  */
 package org.apache.uima.util;
 
+import static org.apache.uima.pear.util.FileUtil.extractFilesFromJar;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.Assert.assertEquals;
+
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
-import junit.framework.TestCase;
-
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
-public class FileUtilsTest extends TestCase {
+public class FileUtilsTest {
+  @Test
   public void testFindRelativePath() throws Exception {
     File target = new File("/this/is/a/file.txt");
     File base = new File("/this/is/a/test");
@@ -41,24 +52,37 @@ public class FileUtilsTest extends TestCase {
     }
   }
 
-  public void testReadWriteTempFile() throws IOException {
-    final String tmpDirPath = System.getProperty("java.io.tmpdir");
-    assertNotNull("java.io.tmpdir system property not available", tmpDirPath);
-    File tmpDir = FileUtils.createTempDir(new File(tmpDirPath), "fileUtilsTest");
-    File tmpFile1 = FileUtils.createTempFile("test", null, tmpDir);
-    File tmpFile2 = FileUtils.createTempFile("test", null, tmpDir);
+  @Test
+  public void testReadWriteTempFile(@TempDir Path aTempDir) throws IOException {
+    File tmpFile2 = aTempDir.resolve("file2.txt").toFile();
     final String text = "This is some text to test file writing.  Add an Umlaut for encoding tests:"
-        + "\n  Greetings from T\u00FCbingen!\n";
+            + "\n  Greetings from T\u00FCbingen!\n";
     final String utf8 = "UTF-8";
 
-   //  UIMA-2050 Does not work on all platform encodings
-   //  Solution: Do not do it!
-   //  FileUtils.saveString2File(text, tmpFile1);
-   //  assertEquals(text, FileUtils.file2String(tmpFile1));
-    
+    // UIMA-2050 Does not work on all platform encodings
+    // Solution: Do not do it!
+    // FileUtils.saveString2File(text, tmpFile1);
+    // assertEquals(text, FileUtils.file2String(tmpFile1));
+
     FileUtils.saveString2File(text, tmpFile2, utf8);
-    assertEquals(text, FileUtils.file2String(tmpFile2, utf8));
-    
-    FileUtils.deleteRecursive(tmpDir);
+    assertThat(FileUtils.file2String(tmpFile2, utf8)).isEqualTo(text);
+  }
+
+  @Test
+  void thatAllFilesGoToTargetFolder(@TempDir Path aTempDir) throws Exception {
+    File zipFile = aTempDir.resolve("test.zip").toFile();
+
+    try (ZipOutputStream oStream = new ZipOutputStream(new FileOutputStream(zipFile))) {
+      ZipEntry zipEntry = new ZipEntry("../whoops.txt");
+      oStream.putNextEntry(zipEntry);
+    }
+
+    File target = aTempDir.resolve("target").toFile();
+
+    try (JarFile jarFile = new JarFile(zipFile)) {
+      assertThatExceptionOfType(IOException.class)
+              .isThrownBy(() -> extractFilesFromJar(jarFile, target))
+              .withMessageContaining("Can only write within target folder");
+    }
   }
 }