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