2 * Copyright © 2017-2018 AT&T Intellectual Property.
3 * Modifications Copyright © 2019 Bell Canada.
4 * Modifications Copyright © 2019 Nordix Foundation.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org.onap.ccsdk.cds.controllerblueprints.core.utils
21 import com.google.common.base.Predicates
22 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
23 import org.slf4j.LoggerFactory
24 import java.io.BufferedInputStream
25 import java.io.ByteArrayInputStream
26 import java.io.ByteArrayOutputStream
27 import java.io.Closeable
29 import java.io.FileOutputStream
30 import java.io.InputStream
31 import java.io.InputStreamReader
32 import java.io.IOException
33 import java.io.OutputStream
34 import java.nio.file.FileVisitResult
35 import java.nio.file.Files
36 import java.nio.file.Path
37 import java.nio.file.SimpleFileVisitor
38 import java.nio.file.attribute.BasicFileAttributes
39 import java.util.function.Predicate
40 import org.apache.commons.compress.archivers.ArchiveEntry
41 import org.apache.commons.compress.archivers.ArchiveInputStream
42 import org.apache.commons.compress.archivers.ArchiveOutputStream
43 import org.apache.commons.compress.archivers.tar.TarArchiveEntry
44 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
45 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
46 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
47 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
48 import org.apache.commons.compress.archivers.zip.ZipFile
49 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
50 import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
51 import java.util.Enumeration
52 import java.util.zip.Deflater
54 enum class ArchiveType {
59 class BluePrintArchiveUtils {
62 private val log = LoggerFactory.getLogger(BluePrintArchiveUtils::class.java)
65 * Create a new Zip from a root directory
67 * @param source the base directory
68 * @param destination the output filename
71 fun compress(source: File, destination: File, archiveType: ArchiveType = ArchiveType.Zip): Boolean {
73 if (!destination.parentFile.exists()) {
74 destination.parentFile.mkdirs()
76 destination.createNewFile()
77 val ignoreZipFiles = Predicate<Path> { path -> !path.endsWith(".zip") && !path.endsWith(".ZIP") }
78 FileOutputStream(destination).use { out ->
79 compressFolder(source.toPath(), out, archiveType, pathFilter = ignoreZipFiles)
81 } catch (e: Exception) {
82 log.error("Fail to compress folder($source) to path(${destination.path})", e)
89 * In-memory compress an entire folder.
93 archiveType: ArchiveType = ArchiveType.Zip,
94 compressionLevel: Int = Deflater.NO_COMPRESSION
96 return compressFolder(baseDir, ByteArrayOutputStream(), archiveType, compressionLevel = compressionLevel)
101 * Compress an entire folder.
103 * @param baseDir path of base folder to be packaged.
104 * @param output the output stream
105 * @param pathFilter filter to ignore files based on its path.
106 * @param compressionLevel the wanted compression level.
107 * @param fixedModificationTime to force every entry to have this modification time.
108 * Useful for reproducible operations, like tests, for example.
110 private fun <T> compressFolder(
113 archiveType: ArchiveType,
114 pathFilter: Predicate<Path> = Predicates.alwaysTrue(),
115 compressionLevel: Int = Deflater.DEFAULT_COMPRESSION,
116 fixedModificationTime: Long? = null
118 where T : OutputStream {
119 val stream: ArchiveOutputStream = if (archiveType == ArchiveType.Zip)
120 ZipArchiveOutputStream(output).apply { setLevel(compressionLevel) }
122 TarArchiveOutputStream(GzipCompressorOutputStream(output))
125 Files.walkFileTree(baseDir, object : SimpleFileVisitor<Path>() {
126 @Throws(IOException::class)
127 override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
128 if (pathFilter.test(file)) {
129 var archiveEntry: ArchiveEntry = aos.createArchiveEntry(file.toFile(),
130 baseDir.relativize(file).toString())
131 if (archiveType == ArchiveType.Zip) {
132 val entry = archiveEntry as ZipArchiveEntry
133 fixedModificationTime?.let {
138 aos.putArchiveEntry(archiveEntry)
139 Files.copy(file, aos)
140 aos.closeArchiveEntry()
142 return FileVisitResult.CONTINUE
145 @Throws(IOException::class)
146 override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
147 var archiveEntry: ArchiveEntry?
148 if (archiveType == ArchiveType.Zip) {
149 val entry = ZipArchiveEntry(baseDir.relativize(dir).toString() + "/")
150 fixedModificationTime?.let {
155 archiveEntry = TarArchiveEntry(baseDir.relativize(dir).toString() + "/")
156 aos.putArchiveEntry(archiveEntry)
157 aos.closeArchiveEntry()
158 return FileVisitResult.CONTINUE
165 private fun getDefaultEncoding(): String? {
166 val bytes = byteArrayOf('D'.toByte())
167 val inputStream: InputStream = ByteArrayInputStream(bytes)
168 val reader = InputStreamReader(inputStream)
169 return reader.encoding
172 fun deCompress(archiveFile: File, targetPath: String, archiveType: ArchiveType = ArchiveType.Zip): File {
173 var enumeration: ArchiveEnumerator? = null
174 if (archiveType == ArchiveType.Zip) {
175 val zipArchive = ZipFile(archiveFile, getDefaultEncoding())
176 enumeration = ArchiveEnumerator(zipArchive)
178 var tarGzArchiveIs: InputStream = BufferedInputStream(archiveFile.inputStream())
179 tarGzArchiveIs = GzipCompressorInputStream(tarGzArchiveIs)
180 val tarGzArchive: ArchiveInputStream = TarArchiveInputStream(tarGzArchiveIs)
181 enumeration = ArchiveEnumerator(tarGzArchive)
185 while (enumeration!!.hasMoreElements()) {
186 val entry: ArchiveEntry? = enumeration.nextElement()
187 val destFilePath = File(targetPath, entry!!.name)
188 destFilePath.parentFile.mkdirs()
190 if (entry!!.isDirectory)
193 val bufferedIs = BufferedInputStream(enumeration.getInputStream(entry))
194 destFilePath.outputStream().buffered(1024).use { bos ->
195 bufferedIs.copyTo(bos)
198 if (!enumeration.getHasSharedEntryInputStream())
203 val destinationDir = File(targetPath)
204 check(destinationDir.isDirectory && destinationDir.exists()) {
205 throw BluePrintProcessorException("failed to decompress blueprint(${archiveFile.absolutePath}) to ($targetPath) ")
208 return destinationDir
212 class ArchiveEnumerator : Enumeration<ArchiveEntry>, Closeable {
213 private val zipArchive: ZipFile?
214 private val zipEnumeration: Enumeration<ZipArchiveEntry>?
215 private val archiveStream: ArchiveInputStream?
216 private var nextEntry: ArchiveEntry? = null
217 private val hasSharedEntryInputStream: Boolean
219 constructor(zipFile: ZipFile) {
221 zipEnumeration = zipFile.entries
223 hasSharedEntryInputStream = false
226 constructor(archiveStream: ArchiveInputStream) {
227 this.archiveStream = archiveStream
229 zipEnumeration = null
230 hasSharedEntryInputStream = true
233 fun getHasSharedEntryInputStream(): Boolean {
234 return hasSharedEntryInputStream
237 fun getInputStream(entry: ArchiveEntry): InputStream? {
238 return if (zipArchive != null)
239 zipArchive?.getInputStream(entry as ZipArchiveEntry?)
244 override fun hasMoreElements(): Boolean {
245 if (zipEnumeration != null)
246 return zipEnumeration?.hasMoreElements()
247 else if (archiveStream != null) {
248 nextEntry = archiveStream.nextEntry
249 if (nextEntry != null && !archiveStream.canReadEntryData(nextEntry))
250 return hasMoreElements()
251 return nextEntry != null
256 override fun nextElement(): ArchiveEntry? {
257 if (zipEnumeration != null)
258 nextEntry = zipEnumeration.nextElement()
259 else if (archiveStream != null) {
260 if (nextEntry == null)
261 nextEntry = archiveStream.nextEntry
266 override fun close() {
267 if (zipArchive != null)
269 else archiveStream?.close()