Java tutorial
/* * Copyright 2013 Christoph Brill <egore911@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.egore911.versioning.deployer.performer; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import de.egore911.versioning.deployer.util.ExtractionPair; import de.egore911.versioning.util.UrlUtil; /** * Performs an exctration copy operation as told by the versioning web * application. * * @author Christoph Brill <egore911@gmail.com> */ public class PerformExtraction { private static final Logger LOG = LoggerFactory.getLogger(PerformExtraction.class); private static final Map<String, Pattern> patternCache = new HashMap<>(); private final XPathExpression extractXpath; private final XPathExpression urlXpath; private final XPathExpression extractionsExtractionXpath; private final XPathExpression sourceXpath; private final XPathExpression destinationXpath; public PerformExtraction(XPath xPath) throws XPathExpressionException { extractXpath = xPath.compile("extract"); urlXpath = xPath.compile("url/text()"); extractionsExtractionXpath = xPath.compile("extractions/extraction"); sourceXpath = xPath.compile("source/text()"); destinationXpath = xPath.compile("destination/text()"); } public void perform(Node serverDeploymentsDeploymentNode) throws XPathExpressionException { NodeList extractionOperations = (NodeList) extractXpath.evaluate(serverDeploymentsDeploymentNode, XPathConstants.NODESET); for (int j = 0; j < extractionOperations.getLength(); j++) { Node extractionOperation = extractionOperations.item(j); String uri = (String) urlXpath.evaluate(extractionOperation, XPathConstants.STRING); NodeList extractions = (NodeList) extractionsExtractionXpath.evaluate(extractionOperation, XPathConstants.NODESET); List<ExtractionPair> exts = new ArrayList<>(); for (int k = 0; k < extractions.getLength(); k++) { Node extraction = extractions.item(k); String source = (String) sourceXpath.evaluate(extraction, XPathConstants.STRING); String destination = (String) destinationXpath.evaluate(extraction, XPathConstants.STRING); exts.add(new ExtractionPair(source, destination)); } extract(uri, exts); } } private static boolean extract(String uri, List<ExtractionPair> extractions) { URL url; try { url = new URL(uri); } catch (MalformedURLException e) { LOG.error("Invalid URI: {}", e.getMessage(), e); return false; } try { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); int response = connection.getResponseCode(); int lastSlash = uri.lastIndexOf('/'); if (lastSlash < 0) { LOG.error("Invalid URI: {}", uri); return false; } int lastDot = uri.lastIndexOf('.'); if (lastDot < 0) { LOG.error("Invalid URI: {}", uri); return false; } File downloadFile = File.createTempFile(uri.substring(lastSlash + 1), uri.substring(lastDot + 1)); downloadFile.deleteOnExit(); if (response == HttpURLConnection.HTTP_OK) { try (InputStream in = connection.getInputStream(); FileOutputStream out = new FileOutputStream(downloadFile)) { IOUtils.copy(in, out); } LOG.debug("Downloaded {} to {}", url, downloadFile.getAbsolutePath()); Set<ExtractionPair> usedExtractions = new HashSet<>(); // Perform extractions try (ZipFile zipFile = new ZipFile(downloadFile)) { Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); // Only extract files if (entry.isDirectory()) { continue; } for (ExtractionPair extraction : extractions) { String sourcePattern = extraction.source; if (FilenameUtils.wildcardMatch(entry.getName(), sourcePattern)) { usedExtractions.add(extraction); LOG.debug("Found matching file {} for source pattern {}", entry.getName(), sourcePattern); String filename = getSourcePatternMatch(entry.getName(), sourcePattern); // Workaround: If there is no matcher in 'sourcePattern' it will return the // complete path. Strip it down to the filename if (filename.equals(entry.getName())) { int lastIndexOf = filename.lastIndexOf('/'); if (lastIndexOf >= 0) { filename = filename.substring(lastIndexOf + 1); } } String name = UrlUtil.concatenateUrlWithSlashes(extraction.destination, filename); FileUtils.forceMkdir(new File(name).getParentFile()); try (InputStream in = zipFile.getInputStream(entry); FileOutputStream out = new FileOutputStream(name)) { IOUtils.copy(in, out); } LOG.debug("Extracted {} to {} from {}", entry.getName(), name, uri); } } } } for (ExtractionPair extraction : extractions) { if (!usedExtractions.contains(extraction)) { LOG.debug("Extraction {} to {} not used on {}", extraction.source, extraction.destination, uri); } } return true; } else { LOG.error("Could not download file: {}", uri); return false; } } catch (IOException e) { LOG.error("Could not download file: {}", e.getMessage(), e); return false; } } /** * Returns the matching part of the entry's name. * * FIXME assumes that the '*' is only used for matching files, not * directories! */ private static String getSourcePatternMatch(String entryName, String sourcePattern) { // Create a regular expression pattern for the given sourcePattern Pattern pattern = patternCache.get(sourcePattern); if (pattern == null) { // Pattern compilation is expensive, so cache it pattern = Pattern.compile(transformSourcePatternToRegularExpression(sourcePattern)); patternCache.put(sourcePattern, pattern); } // Perform the actual replacement Matcher matcher = pattern.matcher(entryName); StringBuffer buffer = new StringBuffer(); if (matcher.matches()) { if (matcher.groupCount() == 1) { matcher.appendReplacement(buffer, matcher.group(1)); } } matcher.appendTail(buffer); return buffer.toString(); } /** * Use the given source pattern (e.g. "/var/lib/tomcat7/war/*") and * transform it into a grouping regular expression (e.g. * "/var/lib/tomcat7/war/(.*)"). You get exactly one group per '*'. * Currently only one '*' is supported. '?' are also not supported. */ private static String transformSourcePatternToRegularExpression(String sourcePattern) { if (StringUtils.countMatches(sourcePattern, "?") > 0) { throw new IllegalArgumentException("No support for '?' yet."); } switch (StringUtils.countMatches(sourcePattern, "*")) { case 0: // No match, nothing to do return sourcePattern; case 1: // One match, we're fine return sourcePattern.replace(".", "\\.").replace("*", "(.*") + ")"; default: // More than one match, crash and burn throw new IllegalArgumentException("No support for multiple '*' yet."); } } }