001 /** 002 * Copyright 2008-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.codehaus.mojo.wagon.shared; 017 018 /* 019 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 020 * agreements. See the NOTICE file distributed with this work for additional information regarding 021 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 022 * "License"); you may not use this file except in compliance with the License. You may obtain a 023 * copy of the License at 024 * 025 * http://www.apache.org/licenses/LICENSE-2.0 026 * 027 * Unless required by applicable law or agreed to in writing, software distributed under the License 028 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 029 * or implied. See the License for the specific language governing permissions and limitations under 030 * the License. 031 */ 032 033 import java.io.File; 034 import java.io.FileInputStream; 035 import java.io.FileReader; 036 import java.io.FileWriter; 037 import java.io.IOException; 038 import java.io.InputStream; 039 import java.io.Reader; 040 import java.io.Writer; 041 import java.security.MessageDigest; 042 import java.security.NoSuchAlgorithmException; 043 044 import org.apache.maven.artifact.repository.metadata.Metadata; 045 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; 046 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; 047 import org.apache.maven.plugin.logging.Log; 048 import org.apache.maven.shared.model.fileset.FileSet; 049 import org.apache.maven.wagon.ResourceDoesNotExistException; 050 import org.apache.maven.wagon.Wagon; 051 import org.apache.maven.wagon.WagonException; 052 import org.codehaus.plexus.util.DirectoryScanner; 053 import org.codehaus.plexus.util.FileUtils; 054 import org.codehaus.plexus.util.IOUtil; 055 import org.codehaus.plexus.util.xml.pull.XmlPullParserException; 056 057 /** 058 * A copy of stage's plugin RepositoryCopier but use WagonUpload and WagonDownload instead 059 * 060 * @plexus.component role="org.codehaus.mojo.wagon.shared.MavenRepoMerger" role-hint="default" 061 */ 062 063 public class DefaultMavenRepoMerger implements MavenRepoMerger { 064 /** 065 * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonDownload" 066 */ 067 private WagonDownload downloader; 068 069 /** 070 * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonUpload" 071 */ 072 private WagonUpload uploader; 073 074 @Override 075 public void merge(Wagon src, Wagon target, boolean optimize, Log logger) throws WagonException, IOException { 076 077 // copy src to a local dir 078 File downloadSrcDir = createTempDirectory("wagon-maven-plugin"); 079 080 WagonFileSet srcFileSet = new WagonFileSet(); 081 srcFileSet.setDownloadDirectory(downloadSrcDir); 082 // ignore archiva/nexus .index at root dir 083 String[] excludes = { ".index/**", ".indexer/**, .meta/**, .nexus/**" }; 084 srcFileSet.setExcludes(excludes); 085 086 try { 087 downloader.download(src, srcFileSet, logger, false); 088 089 // merge metadata 090 DirectoryScanner scanner = new DirectoryScanner(); 091 scanner.setBasedir(downloadSrcDir); 092 String[] includes = { "**/" + MAVEN_METADATA }; 093 scanner.setIncludes(includes); 094 scanner.scan(); 095 String[] files = scanner.getIncludedFiles(); 096 097 for (int i = 0; i < files.length; ++i) { 098 File srcMetadaFile = new File(downloadSrcDir, files[i] + IN_PROCESS_MARKER); 099 100 try { 101 target.get(files[i].replace('\\', '/'), srcMetadaFile); 102 } catch (ResourceDoesNotExistException e) { 103 // We don't have an equivalent on the targetRepositoryUrl side because we have something 104 // new on the sourceRepositoryUrl side so just skip the metadata merging. 105 continue; 106 } 107 108 try { 109 mergeMetadata(srcMetadaFile, logger); 110 } catch (XmlPullParserException e) { 111 throw new IOException("Metadata file is corrupt " + files[i] + " Reason: " + e.getMessage()); 112 } 113 114 } 115 116 // upload to target 117 FileSet tobeUploadedFileSet = new FileSet(); 118 tobeUploadedFileSet.setDirectory(downloadSrcDir.getAbsolutePath()); 119 120 this.uploader.upload(target, tobeUploadedFileSet, optimize, logger); 121 122 } finally { 123 FileUtils.deleteDirectory(downloadSrcDir); 124 } 125 126 } 127 128 private void mergeMetadata(File existingMetadata, Log logger) throws IOException, XmlPullParserException { 129 130 Writer stagedMetadataWriter = null; 131 Reader existingMetadataReader = null; 132 Reader stagedMetadataReader = null; 133 File stagedMetadataFile = null; 134 135 try { 136 MetadataXpp3Reader xppReader = new MetadataXpp3Reader(); 137 MetadataXpp3Writer xppWriter = new MetadataXpp3Writer(); 138 139 // Existing Metadata in target stage 140 existingMetadataReader = new FileReader(existingMetadata); 141 Metadata existing = xppReader.read(existingMetadataReader); 142 143 // Staged Metadata 144 stagedMetadataFile = new File(existingMetadata.getParentFile(), MAVEN_METADATA); 145 stagedMetadataReader = new FileReader(stagedMetadataFile); 146 Metadata staged = xppReader.read(stagedMetadataReader); 147 148 // Merge and write back to staged metadata to replace the remote one 149 existing.merge(staged); 150 151 stagedMetadataWriter = new FileWriter(stagedMetadataFile); 152 xppWriter.write(stagedMetadataWriter, existing); 153 154 logger.info("Merging metadata file: " + stagedMetadataFile); 155 156 } finally { 157 IOUtil.close(stagedMetadataWriter); 158 IOUtil.close(stagedMetadataReader); 159 IOUtil.close(existingMetadataReader); 160 161 existingMetadata.delete(); 162 } 163 164 // Mark all metadata as in-process and regenerate the checksums as they will be different 165 // after the merger 166 167 try { 168 File newMd5 = new File(stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".md5"); 169 FileUtils.fileWrite(newMd5.getAbsolutePath(), checksum(stagedMetadataFile, MD5)); 170 171 File newSha1 = new File(stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".sha1"); 172 FileUtils.fileWrite(newSha1.getAbsolutePath(), checksum(stagedMetadataFile, SHA1)); 173 } catch (NoSuchAlgorithmException e) { 174 throw new RuntimeException(e); 175 } 176 177 // We have the new merged copy so we're good 178 179 } 180 181 private String checksum(File file, String type) throws IOException, NoSuchAlgorithmException { 182 MessageDigest md5 = MessageDigest.getInstance(type); 183 184 InputStream is = new FileInputStream(file); 185 186 byte[] buf = new byte[8192]; 187 188 int i; 189 190 while ((i = is.read(buf)) > 0) { 191 md5.update(buf, 0, i); 192 } 193 194 IOUtil.close(is); 195 196 return encode(md5.digest()); 197 } 198 199 private String encode(byte[] binaryData) { 200 if (binaryData.length != 16 && binaryData.length != 20) { 201 int bitLength = binaryData.length * 8; 202 throw new IllegalArgumentException("Unrecognised length for binary data: " + bitLength + " bits"); 203 } 204 205 String retValue = ""; 206 207 for (int i = 0; i < binaryData.length; i++) { 208 String t = Integer.toHexString(binaryData[i] & 0xff); 209 210 if (t.length() == 1) { 211 retValue += ("0" + t); 212 } else { 213 retValue += t; 214 } 215 } 216 217 return retValue.trim(); 218 } 219 220 public static File createTempDirectory(String prefix) throws IOException { 221 final File temp = File.createTempFile(prefix, Long.toString(System.nanoTime())); 222 223 if (!temp.delete()) { 224 throw new IOException("Could not delete temp file: " + temp.getAbsolutePath()); 225 } 226 227 if (!temp.mkdir()) { 228 throw new IOException("Could not create temp directory: " + temp.getAbsolutePath()); 229 } 230 231 return temp; 232 } 233 234 }