/***************************************************************************
* Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
* Licensed 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 com.vmware.bdd.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.log4j.Logger;
/**
* Compress the ngc plugin files into a zip file for vc to download after
* registration.
*
*/
public class NgcZipPacker {
private static final Logger LOGGER = Logger.getLogger(NgcZipPacker.class);
private static String pluginPath = NgcConstants.PLUGIN_PATH;
private static String pluginProperties = NgcConstants.PLUGIN_PROPERTIES;
private File ngcFile;
private ZipFile ngcZipFile;
private Properties properties;
public NgcZipPacker(Properties properties, String packageName) {
this.properties = properties;
String ngcFilePath = pluginPath + packageName+".zip";
ngcFile = new File(ngcFilePath);
}
public void repack() throws ZipException, IOException {
LOGGER.debug("Repacking NGC zipfile...");
try {
ngcZipFile = new ZipFile(ngcFile);
Enumeration<? extends ZipEntry> ngcZipEngtires = unpackZipFile();
ZipEntry serviceEntry = findServiceEntry(ngcZipEngtires);
if (serviceEntry != null) {
File propertiesFile = generatePropertiesFile();
propertiesFile.deleteOnExit();
packZipFile(serviceEntry, propertiesFile);
LOGGER.debug("NGC zipfile repacking finished.");
} else {
LOGGER.error("Packing error: service jar not found.");
}
} finally {
if (ngcZipFile != null) {
try {
ngcZipFile.close();
} catch (IOException ex) {
LOGGER.error("Error to close ngc zip file.");
}
}
}
}
private void packZipFile(ZipEntry serviceEntry, File propertiesFile)
throws IOException {
// NOTE: in order to repack the zip file, we will copy everything
// under the legacy zip file to tempfile, make modifications and
// then copy them back to the original zip file
FileOutputStream serviceEntryJarStreamTmp = null;
try {
LOGGER.info("add property file to zip package");
InputStream serviceEntryStream =
ngcZipFile.getInputStream(serviceEntry);
File serviceEntryTemp = File.createTempFile("serviceEntryTmp", null);
serviceEntryJarStreamTmp = new FileOutputStream(serviceEntryTemp);
streamWriter(serviceEntryStream, serviceEntryJarStreamTmp);
serviceEntryJarStreamTmp.close();
serviceEntryStream.close();
String propertiesFilePath = pluginProperties;
String serviceEntryJarPath = serviceEntry.getName();
addFileToJar(serviceEntryTemp, propertiesFile, propertiesFilePath);
addFileToZip(ngcFile, serviceEntryTemp, serviceEntryJarPath);
serviceEntryTemp.delete();
} catch (Exception e) {
LOGGER.error("Error to close serviceEntryJarStreamTmp.");
} finally {
if (serviceEntryJarStreamTmp != null) {
try {
serviceEntryJarStreamTmp.close();
} catch (IOException ex) {
LOGGER.error("Error to close serviceEntryJarStreamTmp.");
}
}
}
}
private File generatePropertiesFile() throws IOException {
String propertiesPath = pluginPath + pluginProperties;
File propertiesFile = new File(propertiesPath);
FileOutputStream propertiesStream = null;
try {
propertiesStream = new FileOutputStream(propertiesFile);
String comments = "Big data NGC plugin properties";
properties.store(propertiesStream, comments);
return new File(propertiesPath);
} finally {
if (propertiesStream != null) {
try {
propertiesStream.close();
} catch (IOException ex) {
LOGGER.error("Error to close propertiesStream.");
}
}
}
}
private ZipEntry findServiceEntry(
Enumeration<? extends ZipEntry> ngcZipEngtires) {
while (ngcZipEngtires.hasMoreElements()) {
ZipEntry entry = ngcZipEngtires.nextElement();
if (entry.getName().contains("service")) {
return entry;
}
}
return null;
}
private Enumeration<? extends ZipEntry> unpackZipFile() throws ZipException,
IOException {
return ngcZipFile.entries();
}
private void addFileToZip(File source, File file, String filePath)
throws IOException {
File tempZip = File.createTempFile(source.getName(), null);
tempZip.delete();
source.renameTo(tempZip);
ZipInputStream zipSource = null;
ZipOutputStream zipDest = null;
// write the file to be added first
InputStream fileIn = null;
try {
fileIn = new FileInputStream(file);
zipSource = new ZipInputStream(new FileInputStream(tempZip));
zipDest = new ZipOutputStream(new FileOutputStream(source));
zipDest.putNextEntry(new ZipEntry(filePath));
streamWriter(fileIn, zipDest);
zipDest.closeEntry();
// copy other files in the original jar file
for (ZipEntry zipEntry = zipSource.getNextEntry(); zipEntry != null; zipEntry =
zipSource.getNextEntry()) {
if (!zipEntry.getName().equals(filePath)) {
zipDest.putNextEntry(zipEntry);
streamWriter(zipSource, zipDest);
zipDest.closeEntry();
}
}
tempZip.delete();
} finally {
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException ex) {
LOGGER.error("Error to close file.");
}
}
if (zipSource != null) {
try {
zipSource.close();
} catch (IOException ex) {
LOGGER.error("Error to close zip source file.");
}
}
if (zipDest != null) {
try {
zipDest.close();
} catch (IOException ex) {
LOGGER.error("Error to close zip dest file.");
}
}
}
}
private void addFileToJar(File source, File file, String filePath)
throws IOException {
File tempJar = File.createTempFile(source.getName(), null);
tempJar.delete();
source.renameTo(tempJar);
InputStream fileIn = null;
FileInputStream fileSource = null;
FileOutputStream fileDest = null;
JarInputStream jarSource = null;
JarOutputStream jarDest = null;
try {
fileSource = new FileInputStream(tempJar);
jarSource = new JarInputStream(fileSource);
fileDest = new FileOutputStream(source);
if (jarSource.getManifest() == null) {
jarDest = new JarOutputStream(fileDest);
} else {
jarDest = new JarOutputStream(fileDest, jarSource.getManifest());
}
// write the file to be added first
fileIn = new FileInputStream(file);
jarDest.putNextEntry(new ZipEntry(filePath));
streamWriter(fileIn, jarDest);
jarDest.closeEntry();
// copy other files in the original jar file
for (ZipEntry jarEntry = jarSource.getNextEntry(); jarEntry != null; jarEntry =
jarSource.getNextEntry()) {
if (!jarEntry.getName().equals(filePath)) {
jarDest.putNextEntry(jarEntry);
streamWriter(jarSource, jarDest);
jarDest.closeEntry();
}
}
tempJar.delete();
} finally {
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException ex) {
LOGGER.error("Error to close file.");
}
}
if (jarSource != null) {
try {
jarSource.close();
} catch (IOException ex) {
LOGGER.error("Error to close jar source file.");
}
}
if (jarDest != null) {
try {
jarDest.close();
} catch (IOException ex) {
LOGGER.error("Error to close jar dest file.");
}
}
if (fileSource != null) {
try {
fileSource.close();
} catch (IOException ex) {
LOGGER.error("Error to close fileSource.");
}
}
if (fileDest != null) {
try {
fileDest.close();
} catch (IOException ex) {
LOGGER.error("Error to close fileDest.");
}
}
}
}
private void streamWriter(InputStream source, OutputStream dest)
throws IOException {
byte[] buffer = new byte[4096];
for (int read = source.read(buffer); read > -1; read =
source.read(buffer)) {
dest.write(buffer, 0, read);
}
}
}