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 064 implements MavenRepoMerger 065 { 066 /** 067 * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonDownload" 068 */ 069 private WagonDownload downloader; 070 071 /** 072 * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonUpload" 073 */ 074 private WagonUpload uploader; 075 076 public void merge( Wagon src, Wagon target, boolean optimize, Log logger ) 077 throws WagonException, IOException 078 { 079 080 // copy src to a local dir 081 File downloadSrcDir = createTempDirectory( "wagon-maven-plugin" ); 082 083 WagonFileSet srcFileSet = new WagonFileSet(); 084 srcFileSet.setDownloadDirectory( downloadSrcDir ); 085 // ignore archiva/nexus .index at root dir 086 String[] excludes = { ".index/**", ".indexer/**, .meta/**, .nexus/**" }; 087 srcFileSet.setExcludes( excludes ); 088 089 try 090 { 091 downloader.download( src, srcFileSet, logger ); 092 093 // merge metadata 094 DirectoryScanner scanner = new DirectoryScanner(); 095 scanner.setBasedir( downloadSrcDir ); 096 String[] includes = { "**/" + MAVEN_METADATA }; 097 scanner.setIncludes( includes ); 098 scanner.scan(); 099 String[] files = scanner.getIncludedFiles(); 100 101 for ( int i = 0; i < files.length; ++i ) 102 { 103 File srcMetadaFile = new File( downloadSrcDir, files[i] + IN_PROCESS_MARKER ); 104 105 try 106 { 107 target.get( files[i].replace( '\\', '/' ), srcMetadaFile ); 108 } 109 catch ( ResourceDoesNotExistException e ) 110 { 111 // We don't have an equivalent on the targetRepositoryUrl side because we have something 112 // new on the sourceRepositoryUrl side so just skip the metadata merging. 113 continue; 114 } 115 116 try 117 { 118 mergeMetadata( srcMetadaFile, logger ); 119 } 120 catch ( XmlPullParserException e ) 121 { 122 throw new IOException( "Metadata file is corrupt " + files[i] + " Reason: " + e.getMessage() ); 123 } 124 125 } 126 127 // upload to target 128 FileSet tobeUploadedFileSet = new FileSet(); 129 tobeUploadedFileSet.setDirectory( downloadSrcDir.getAbsolutePath() ); 130 131 this.uploader.upload( target, tobeUploadedFileSet, optimize, logger ); 132 133 } 134 finally 135 { 136 FileUtils.deleteDirectory( downloadSrcDir ); 137 } 138 139 } 140 141 private void mergeMetadata( File existingMetadata, Log logger ) 142 throws IOException, XmlPullParserException 143 { 144 145 Writer stagedMetadataWriter = null; 146 Reader existingMetadataReader = null; 147 Reader stagedMetadataReader = null; 148 File stagedMetadataFile = null; 149 150 try 151 { 152 MetadataXpp3Reader xppReader = new MetadataXpp3Reader(); 153 MetadataXpp3Writer xppWriter = new MetadataXpp3Writer(); 154 155 // Existing Metadata in target stage 156 existingMetadataReader = new FileReader( existingMetadata ); 157 Metadata existing = xppReader.read( existingMetadataReader ); 158 159 // Staged Metadata 160 stagedMetadataFile = new File( existingMetadata.getParentFile(), MAVEN_METADATA ); 161 stagedMetadataReader = new FileReader( stagedMetadataFile ); 162 Metadata staged = xppReader.read( stagedMetadataReader ); 163 164 // Merge and write back to staged metadata to replace the remote one 165 existing.merge( staged ); 166 167 stagedMetadataWriter = new FileWriter( stagedMetadataFile ); 168 xppWriter.write( stagedMetadataWriter, existing ); 169 170 logger.info( "Merging metadata file: " + stagedMetadataFile ); 171 172 } 173 finally 174 { 175 IOUtil.close( stagedMetadataWriter ); 176 IOUtil.close( stagedMetadataReader ); 177 IOUtil.close( existingMetadataReader ); 178 179 existingMetadata.delete(); 180 } 181 182 // Mark all metadata as in-process and regenerate the checksums as they will be different 183 // after the merger 184 185 try 186 { 187 File newMd5 = new File( stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".md5" ); 188 FileUtils.fileWrite( newMd5.getAbsolutePath(), checksum( stagedMetadataFile, MD5 ) ); 189 190 File newSha1 = new File( stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".sha1" ); 191 FileUtils.fileWrite( newSha1.getAbsolutePath(), checksum( stagedMetadataFile, SHA1 ) ); 192 } 193 catch ( NoSuchAlgorithmException e ) 194 { 195 throw new RuntimeException( e ); 196 } 197 198 // We have the new merged copy so we're good 199 200 } 201 202 private String checksum( File file, String type ) 203 throws IOException, NoSuchAlgorithmException 204 { 205 MessageDigest md5 = MessageDigest.getInstance( type ); 206 207 InputStream is = new FileInputStream( file ); 208 209 byte[] buf = new byte[8192]; 210 211 int i; 212 213 while ( ( i = is.read( buf ) ) > 0 ) 214 { 215 md5.update( buf, 0, i ); 216 } 217 218 IOUtil.close( is ); 219 220 return encode( md5.digest() ); 221 } 222 223 private String encode( byte[] binaryData ) 224 { 225 if ( binaryData.length != 16 && binaryData.length != 20 ) 226 { 227 int bitLength = binaryData.length * 8; 228 throw new IllegalArgumentException( "Unrecognised length for binary data: " + bitLength + " bits" ); 229 } 230 231 String retValue = ""; 232 233 for ( int i = 0; i < binaryData.length; i++ ) 234 { 235 String t = Integer.toHexString( binaryData[i] & 0xff ); 236 237 if ( t.length() == 1 ) 238 { 239 retValue += ( "0" + t ); 240 } 241 else 242 { 243 retValue += t; 244 } 245 } 246 247 return retValue.trim(); 248 } 249 250 public static File createTempDirectory(String prefix) 251 throws IOException 252 { 253 final File temp; 254 255 temp = File.createTempFile( prefix, Long.toString( System.nanoTime() ) ); 256 257 if ( !( temp.delete() ) ) 258 { 259 throw new IOException( "Could not delete temp file: " + temp.getAbsolutePath() ); 260 } 261 262 if ( !( temp.mkdir() ) ) 263 { 264 throw new IOException( "Could not create temp directory: " + temp.getAbsolutePath() ); 265 } 266 267 return ( temp ); 268 } 269 270 }