Project: cloudbees-api-client
/*
 * Copyright 2010-2011, CloudBees Inc. 
 * 
 * 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.cloudbees.upload; 
 
import com.cloudbees.utils.ZipHelper; 
import com.thoughtworks.xstream.XStream; 
 
import java.io.*; 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 
import java.util.Enumeration; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipFile; 
import java.util.zip.ZipOutputStream; 
 
/**
 * @author Fabian Donze 
 */
 
public class JarUtils { 
    private static final String META_INF = "META-INF"
    private static final String JAR_FILE = "CB-JAR.xml"
 
    public static Map<String, String> getJarHashes(File warFile) throws IOException { 
        Map<String, String> hashes = new HashMap<String, String>(); 
 
        ZipFile zipFile = new ZipFile(warFile.getAbsolutePath()); 
        Enumeration<? extends ZipEntry> e = zipFile.entries(); 
        while (e.hasMoreElements()) { 
            ZipEntry entry = e.nextElement(); 
            String name = entry.getName(); 
            if (name.endsWith(".jar")) 
                hashes.put(name, sha256(zipFile.getInputStream(entry))); 
        } 
 
        zipFile.close(); 
        return hashes; 
    } 
 
    public static File createDeltaWarFile(Map<String, String> existingArchiveJars, File warFile, String tmp) throws IOException { 
        Map<String, String> hashes = new HashMap<String, String>(); 
 
        String tmpDir = makeTmpDir(warFile, tmp); 
        ZipFile zipFile = new ZipFile(warFile.getAbsolutePath()); 
        Enumeration<? extends ZipEntry> e = zipFile.entries(); 
        while (e.hasMoreElements()) { 
            ZipEntry entry = e.nextElement(); 
            String name = entry.getName(); 
            String hash = existingArchiveJars.get(name); 
            String entrySha = sha256(zipFile.getInputStream(entry)); 
            if (hash != null && hash.equals(entrySha)) { 
                hashes.put(name, entrySha); 
            } else { 
                // Copy the entry to tmp directory 
                unArchiveZipEntry(tmpDir, zipFile, entry); 
            } 
        } 
        zipFile.close(); 
 
        // Add a file to the archive with the CRCs 
        File metaInfDir = new File(tmpDir + META_INF); 
        metaInfDir.mkdirs(); 
 
        File deltaFile = new File(metaInfDir, JAR_FILE); 
        XStream xstream = new XStream(); 
        FileOutputStream fos = new FileOutputStream(deltaFile); 
        xstream.toXML(hashes, fos); 
        fos.close(); 
 
        // Archive the deltas 
        String deltaDir = warFile.getParent() == null ? "." :  warFile.getParent(); 
        String deltaArchiveFile = deltaDir + "/JAR-" + warFile.getName(); 
        archiveDirectory(tmpDir, deltaArchiveFile); 
 
        deleteAll(new File(tmpDir)); 
 
        return new File(deltaArchiveFile); 
    } 
 
    private static void unArchiveZipEntry(String destinationDirectory, ZipFile zipfile, ZipEntry entry) throws IOException { 
        File file = ZipHelper.unzipEntryToFolder(entry, zipfile.getInputStream(entry), new File(destinationDirectory)); 
        if (entry.getTime() > -1
            file.setLastModified(entry.getTime()); 
    } 
 
 
    private static void archiveDirectory(String directory, String archiveFile) throws IOException { 
        File archive = new File(archiveFile); 
        archive.createNewFile(); 
        FileOutputStream fileOutputStream = new FileOutputStream(archive); 
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(fileOutputStream)); 
        ZipHelper.addDirectoryToZip(new File(directory), new File(directory), null, zipOutputStream); 
        zipOutputStream.close(); 
    } 
 
    private static String makeTmpDir(File file, String tmp) { 
        if (tmp == null
            tmp = "."
        String fileName = file.getName(); 
        int idx = fileName.lastIndexOf('.'); 
        if (idx > -1) { 
            fileName = fileName.substring(0,idx); 
        } 
        if (!tmp.endsWith(File.separator)) 
            tmp += File.separator; 
 
        tmp = tmp + "tmp" + fileName + File.separator; 
        File dir = new File(tmp); 
        deleteAll(dir); 
        dir.mkdirs(); 
        return tmp; 
    } 
 
    private static void deleteAll(File dir) 
    { 
        if (dir.exists()) { 
            if (dir.isDirectory()) { 
                File[] files = dir.listFiles(); 
                if (files != null) { 
                    for(File f : files) 
                    { 
                        if(f.isDirectory()) 
                            deleteAll(f); 
                        else 
                            f.delete(); 
                    } 
                } 
            } 
            dir.delete(); 
        } 
    } 
 
    private static final int STREAM_BUFFER_LENGTH = 1024
 
    static MessageDigest getDigest(String algorithm) { 
        try { 
            return MessageDigest.getInstance(algorithm); 
        } catch (NoSuchAlgorithmException e) { 
            throw new RuntimeException(e.getMessage()); 
        } 
    } 
 
    private static String sha256(InputStream data) throws IOException { 
        MessageDigest digest = getDigest("SHA-256"); 
        byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; 
        int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); 
 
        while (read > -1) { 
            digest.update(buffer, 0, read); 
            read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); 
        } 
 
        return asHex(digest.digest()); 
    } 
 
    private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); 
    private static String asHex(byte[] buf) 
    { 
        char[] chars = new char[2 * buf.length]; 
        for (int i = 0; i < buf.length; ++i) 
        { 
            chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4]; 
            chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F]; 
        } 
        return new String(chars); 
    } 
}