aboutsummaryrefslogtreecommitdiff
path: root/plugins/uber-jar/src/uberjar/JarUtils.scala
blob: f73947eb838a4287dc92d39ee3fc6da870df0259 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package cbt.uberjar

import java.io.File
import java.nio.file._
import java.nio.file.attribute.BasicFileAttributes
import java.util.jar.{JarFile, JarOutputStream}
import java.util.zip.{ZipEntry, ZipException}

private[cbt] trait JarUtils {

  protected val (pathSeparator, jarFileMatcher, excludeFileMatcher) = {
    val fs = FileSystems.getDefault
    (fs.getSeparator, fs.getPathMatcher("glob:**.jar"), fs.getPathMatcher("glob:**{.RSA,.DSA,.SF,.MF}"))
  }

  /**
    * If `root` is directory: writes content of directory to jar with original mapping
    * If `root` is file: writes this file to jar
    * @param root parent directory, with content should go to jar, or file to be written
    * @param out jar output stream
    * @param log logger
    * @return returns `root`
    */
  protected def writeFilesToJar(root: Path, out: JarOutputStream)(log: String => Unit): Path = {
    Files.walkFileTree(root, new SimpleFileVisitor[Path]() {
      override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
        // when we put part of compiled classes, zip already contains some entries. We should not duplicate them.
        try {
          out.putNextEntry(new ZipEntry(root.relativize(file).toString))
          Files.copy(file, out)
          out.closeEntry()
        } catch {
          case e: ZipException => log(s"Failed to add entry, skipping cause: $e")
        }
        FileVisitResult.CONTINUE
      }

      override def preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult = {
        // when we put part compiled classes, zip already contains some entries. We should not duplicate them.
        try {
          out.putNextEntry(new ZipEntry(root.relativize(dir).toString + pathSeparator))
          out.closeEntry()
        } catch {
          case e: ZipException => log(s"Failed to add entry, skipping cause: $e")
        }
        FileVisitResult.CONTINUE
      }
    })
  }

  /**
    * Extracts jars, and writes them on disk. Returns root directory of extracted jars
    * TODO: in future we probably should save extracted jars in target directory, to reuse them on second run
    * @param jars list of *.jar files
    * @param log logger
    * @return root directory of extracted jars
    */
  protected def extractJars(jars: Seq[File])(log: String => Unit): Path = {
    val destDir = {
      val path = Files.createTempDirectory("unjars")
      path.toFile.deleteOnExit()
      log(s"Unjars directory: $path")
      path
    }
    jars foreach { jar => extractJar(jar, destDir)(log) }
    destDir
  }

  /**
    * Extracts content of single jar file to destination directory.
    * When extracting jar, if same file already exists, we skip(don't write) this file.
    * TODO: maybe skipping duplicates is not best strategy. Figure out duplicate strategy.
    * @param jarFile jar file to extract
    * @param destDir destination directory
    * @param log logger
    */
  private def extractJar(jarFile: File, destDir: Path)(log: String => Unit): Unit = {
    log(s"Extracting jar: $jarFile")
    val jar = new JarFile(jarFile)
    val enumEntries = jar.entries
    while (enumEntries.hasMoreElements) {
      val entry = enumEntries.nextElement()
      //        log(s"Entry name: ${entry.getName}")
      val entryPath = destDir.resolve(entry.getName)
      if (excludeFileMatcher.matches(entryPath)) {
        log(s"Excluded file ${entryPath.getFileName} from jar: $jarFile")
      } else {
        if (Files.exists(entryPath)) {
          log(s"File $entryPath already exists, skipping.")
        } else {
          if (entry.isDirectory) {
            Files.createDirectory(entryPath)
            //              log(s"Created directory: $entryPath")
          } else {
            val is = jar.getInputStream(entry)
            Files.copy(is, entryPath)
            is.close()
            //              log(s"Wrote file: $entryPath")
          }
        }
      }
    }
  }

}