Merge "Add declarative acceptance tests"
[ccsdk/cds.git] / ms / controllerblueprints / modules / blueprint-core / src / main / kotlin / org / onap / ccsdk / cds / controllerblueprints / core / utils / BluePrintArchiveUtils.kt
index 8517be8..2f082db 100755 (executable)
@@ -1,6 +1,7 @@
 /*
  * Copyright © 2017-2018 AT&T Intellectual Property.
  * Modifications Copyright © 2019 Bell Canada.
+ * Modifications Copyright © 2019 Nordix Foundation.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package org.onap.ccsdk.cds.controllerblueprints.core.utils
 
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
-import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
-import org.apache.commons.io.IOUtils
+import com.google.common.base.Predicates
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
 import org.slf4j.LoggerFactory
 import java.io.BufferedInputStream
+import java.io.ByteArrayOutputStream
 import java.io.File
-import java.io.FileInputStream
+import java.io.FileOutputStream
 import java.io.IOException
+import java.io.OutputStream
 import java.nio.charset.Charset
+import java.nio.file.FileVisitResult
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.SimpleFileVisitor
+import java.nio.file.attribute.BasicFileAttributes
+import java.util.function.Predicate
+import java.util.zip.Deflater
+import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
 
 class BluePrintArchiveUtils {
 
@@ -39,17 +49,17 @@ class BluePrintArchiveUtils {
          *
          * @param source the base directory
          * @param destination the output filename
-         * @param absolute store absolute filepath (from directory) or only filename
          * @return True if OK
          */
-        fun compress(source: File, destination: File, absolute: Boolean): Boolean {
+        fun compress(source: File, destination: File): Boolean {
             try {
                 if(!destination.parentFile.exists()) {
                     destination.parentFile.mkdirs()
                 }
                 destination.createNewFile()
-                ZipArchiveOutputStream(destination).use {
-                    recurseFiles(source, source, it, absolute)
+                val ignoreZipFiles = Predicate<Path> { path -> !path.endsWith(".zip") && !path.endsWith(".ZIP") }
+                FileOutputStream(destination).use { out ->
+                    compressFolder(source.toPath(), out, pathFilter = ignoreZipFiles)
                 }
             } catch (e: Exception) {
                 log.error("Fail to compress folder($source) to path(${destination.path})", e)
@@ -59,40 +69,61 @@ class BluePrintArchiveUtils {
         }
 
         /**
-         * Recursive traversal to add files
-         *
-         * @param root
-         * @param file
-         * @param zaos
-         * @param absolute
-         * @throws IOException
+         * In-memory compress an entire folder.
          */
-        @Throws(IOException::class)
-        private fun recurseFiles(root: File, file: File, zaos: ZipArchiveOutputStream,
-                                 absolute: Boolean) {
-            if (file.isDirectory) {
-                // recursive call
-                val files = file.listFiles()
-                for (fileChild in files!!) {
-                    recurseFiles(root, fileChild, zaos, absolute)
-                }
-            } else if (!file.name.endsWith(".zip") && !file.name.endsWith(".ZIP")) {
-                val filename = if (absolute) {
-                    file.absolutePath.substring(root.absolutePath.length)
-                } else {
-                    file.name
-                }
-                val zae = ZipArchiveEntry(filename)
-                zae.size = file.length()
-                zaos.putArchiveEntry(zae)
-                FileInputStream(file).use {
-                    IOUtils.copy(it, zaos)
-                    it.close()
-                }
-                zaos.closeArchiveEntry()
-            }
+        fun compressToBytes(baseDir: Path, compressionLevel: Int = Deflater.NO_COMPRESSION): ByteArray {
+            return compressFolder(baseDir, ByteArrayOutputStream(), compressionLevel = compressionLevel)
+                    .toByteArray()
         }
 
+        /**
+         * Compress an entire folder.
+         *
+         * @param baseDir path of base folder to be packaged.
+         * @param output the output stream
+         * @param pathFilter filter to ignore files based on its path.
+         * @param compressionLevel the wanted compression level.
+         * @param fixedModificationTime to force every entry to have this modification time.
+         * Useful for reproducible operations, like tests, for example.
+         */
+        private fun <T> compressFolder(baseDir: Path, output: T,
+                                       pathFilter: Predicate<Path> = Predicates.alwaysTrue(),
+                                       compressionLevel: Int = Deflater.DEFAULT_COMPRESSION,
+                                       fixedModificationTime: Long? = null): T
+                where T : OutputStream {
+            ZipOutputStream(output)
+                    .apply { setLevel(compressionLevel) }
+                    .use { zos ->
+                        Files.walkFileTree(baseDir, object : SimpleFileVisitor<Path>() {
+                            @Throws(IOException::class)
+                            override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
+                                if (pathFilter.test(file)) {
+                                    val zipEntry = ZipEntry(baseDir.relativize(file).toString())
+                                    fixedModificationTime?.let {
+                                        zipEntry.time = it
+                                    }
+                                    zipEntry.time = 0;
+                                    zos.putNextEntry(zipEntry)
+                                    Files.copy(file, zos)
+                                    zos.closeEntry()
+                                }
+                                return FileVisitResult.CONTINUE
+                            }
+
+                            @Throws(IOException::class)
+                            override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
+                                val zipEntry = ZipEntry(baseDir.relativize(dir).toString() + "/")
+                                fixedModificationTime?.let {
+                                    zipEntry.time = it
+                                }
+                                zos.putNextEntry(zipEntry)
+                                zos.closeEntry()
+                                return FileVisitResult.CONTINUE
+                            }
+                        })
+                    }
+            return output
+        }
 
         fun deCompress(zipFile: File, targetPath: String): File {
             val zip = ZipFile(zipFile, Charset.defaultCharset())