/* * RHQ Management Platform * Copyright (C) 2005-2010 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.util.updater; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.Set; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.rhq.core.template.TemplateEngine; import org.rhq.core.util.MessageDigestGenerator; import org.rhq.core.util.ZipUtil; import org.rhq.core.util.stream.StreamCopyDigest; import org.rhq.core.util.stream.StreamUtil; /** * A visitor object that will extract each zip entry it visits, realizing files that * need to have their replacement variables replaced. * * @author John Mazzitelli */ public class ExtractorZipFileVisitor implements ZipUtil.ZipEntryVisitor { private final FileHashcodeMap fileHashcodeMap = new FileHashcodeMap(); private final Pattern filesToRealizeRegex; private final TemplateEngine templateEngine; private final File rootDir; private final Set<String> filesToNotExtract; private final StreamCopyDigest copierAndHashcodeGenerator; private final DeployDifferences diff; private final boolean dryRun; /** * Creates the visitor. When the visitor hits a zip entry whose name matches * filesToRealizeRegex set, that zip entry will be realized via the template engine prior * to its hashcode being computed and its file created. * If you just want this visitor to walk a zip file without realizing any files, pass in * a null pattern or pass in a null template engine. This will, in effect, * have this visitor extract all file entries as-is. * * @param rootDir the top level directory where all zip file entries will be extracted to. * In other words, all zip file entries' paths are relative to this directory. * @param filesToRealizeRegex pattern of files that are to be realized prior to hashcodes being computed * @param templateEngine the template engine that replaces replacement variables in files to be realized * @param filesToNotExtract set of files that are not to be extracted from the zip and stored; these are to be skipped * @param diff optional object that is told when files are realized * @param dryRun if <code>true</code>, this won't actually write files to the filesystem */ public ExtractorZipFileVisitor(File rootDir, Pattern filesToRealizeRegex, TemplateEngine templateEngine, Set<String> filesToNotExtract, DeployDifferences diff, boolean dryRun) { this.rootDir = rootDir; if (filesToRealizeRegex == null || templateEngine == null) { filesToRealizeRegex = null; templateEngine = null; } this.filesToRealizeRegex = filesToRealizeRegex; this.templateEngine = templateEngine; if (filesToNotExtract != null && filesToNotExtract.size() == 0) { filesToNotExtract = null; } this.filesToNotExtract = filesToNotExtract; this.copierAndHashcodeGenerator = new StreamCopyDigest(); this.diff = diff; this.dryRun = dryRun; } /** * Returns the file/hashcode data this visitor has collected. * @return map containing filenames (zip file entry names) and their hashcodes */ public FileHashcodeMap getFileHashcodeMap() { return fileHashcodeMap; } public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception { String pathname = entry.getName(); if (this.filesToNotExtract != null && this.filesToNotExtract.contains(pathname)) { return true; } File entryFile = new File(this.rootDir, pathname); if (entry.isDirectory()) { if (!dryRun) { entryFile.mkdirs(); } return true; } // make sure all parent directories are created if (!dryRun) { entryFile.getParentFile().mkdirs(); } String hashcode; if (this.filesToRealizeRegex != null && this.filesToRealizeRegex.matcher(pathname).matches()) { // this entry needs to be realized, do it now // note: tempateEngine will never be null if we got here int contentSize = (int) entry.getSize(); ByteArrayOutputStream baos = new ByteArrayOutputStream((contentSize > 0) ? contentSize : 32768); StreamUtil.copy(stream, baos, false); String content = this.templateEngine.replaceTokens(baos.toString()); baos = null; if (this.diff != null) { this.diff.addRealizedFile(pathname, content); } // now write the realized content to the filesystem byte[] bytes = content.getBytes(); if (!dryRun) { FileOutputStream fos = new FileOutputStream(entryFile); try { fos.write(bytes); } finally { fos.close(); } } MessageDigestGenerator hashcodeGenerator = this.copierAndHashcodeGenerator.getMessageDigestGenerator(); hashcodeGenerator.add(bytes); hashcode = hashcodeGenerator.getDigestString(); } else { if (!dryRun) { FileOutputStream fos = new FileOutputStream(entryFile); try { hashcode = this.copierAndHashcodeGenerator.copyAndCalculateHashcode(stream, fos); } finally { fos.close(); } } else { hashcode = MessageDigestGenerator.getDigestString(stream); } } this.fileHashcodeMap.put(pathname, hashcode); return true; } }