/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2014 ForgeRock AS.
*/
package org.forgerock.openidm.patch.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.forgerock.openidm.patch.Archive;
/**
* Zip handling utility.
*/
public class ZipUtil {
private final Archive archive = Archive.getInstance();
private final static Logger logger = Logger.getLogger("PatchLog");
/**
* Extract the contents of the specified folder from the given zip file URL.
* Place the extracted files into the specified target directory.
*
* @param zipUrl The zip file URL
* @param zipDirToExtract The directory to extract from the zip file
* @param targetDir The directory to which the files will be extracted
* @throws IOException if operating on the zip file fails
*/
public void extractFolder(URL zipUrl, final String zipDirToExtract, final File targetDir) throws IOException {
ZipVisitor visitor = new ZipVisitor() {
@Override
public boolean visitEntry(ZipEntry zipEntry, ZipInputStream zipInputStream)
throws IOException {
String entryFileName = zipEntry.getName();
logger.log(Level.FINE, "Process entry name: {0}", entryFileName);
// Normalize to a File representation to compare directories
String zipDirPath = new File(zipDirToExtract).getPath() + File.separator;
if (new File(entryFileName).getPath().startsWith(zipDirPath)) {
String targetFileName = entryFileName.substring(zipDirToExtract.length());
File targetFile = new File(targetDir, targetFileName);
logger.log(Level.FINE, "Calculated target file:{0}", targetFile);
extractEntryToDirOrFile(zipEntry, zipInputStream, targetFile);
} else {
logger.log(Level.FINER, "Ignored {0}", zipEntry);
}
return false;
}
};
visitZip(zipUrl, visitor);
}
/**
* Extract the specified file from the given zip file URL.
* Write the extracted file to the specified target file.
*
* @param zipUrl The zip file URL
* @param fileToExtract The file to extract from the zip file
* @param targetFile The file to which the extracted file will be written
* @throws IOException if operating on the zip file fails
*/
public void extractFile(URL zipUrl, final File fileToExtract, final File targetFile) throws IOException {
ZipVisitor visitor = new ZipVisitor() {
@Override
public boolean visitEntry(ZipEntry zipEntry, ZipInputStream zipInputStream)
throws IOException {
String entryFileName = zipEntry.getName();
logger.log(Level.FINE, "Process entry name: {0}", entryFileName);
File entryFile = new File(entryFileName);
if (entryFile.equals(fileToExtract)) {
extractEntryToDirOrFile(zipEntry, zipInputStream, targetFile);
return true; // Stop visiting, we found the file
} else {
logger.log(Level.FINER, "Ignored {0}", zipEntry);
return false;
}
}
};
visitZip(zipUrl, visitor);
}
private void extractEntryToDirOrFile(ZipEntry zipEntry, ZipInputStream zipInputStream, File targetFile)
throws IOException {
if (zipEntry.isDirectory()) {
targetFile.mkdirs();
} else {
archive.insert(targetFile);
targetFile.getParentFile().mkdirs();
extractEntryToFile(zipInputStream, targetFile);
}
}
private void extractEntryToFile(InputStream inputStream, File targetFile) throws IOException {
int bufferSize = 1024 * 128;
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
ReadableByteChannel inChannel = Channels.newChannel(inputStream);
WritableByteChannel outChannel = Channels.newChannel(fileOutputStream);
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
fileOutputStream.close();
logger.log(Level.INFO, "Extracted {0}", targetFile.getAbsolutePath());
}
/**
* Go through the entries of a zip.
* and let the supplied visitor act upon each entry
* @param zipUrl The zip file url
* @param visitor the visitor to invoke for each entry
* @throws IOException if operating on the zip file fails
*/
public void visitZip(URL zipUrl, ZipVisitor visitor) throws IOException {
InputStream inputStream = zipUrl.openStream();
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry = zipInputStream.getNextEntry();
boolean abort = false;
while (zipEntry != null && !abort) {
abort = visitor.visitEntry(zipEntry, zipInputStream);
zipEntry = zipInputStream.getNextEntry();
}
zipInputStream.closeEntry();
zipInputStream.close();
}
}