package org.codehaus.mojo.wagon.shared; /* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Writer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.maven.artifact.repository.metadata.Metadata; import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; import org.apache.maven.plugin.logging.Log; import org.apache.maven.shared.model.fileset.FileSet; import org.apache.maven.wagon.ResourceDoesNotExistException; import org.apache.maven.wagon.Wagon; import org.apache.maven.wagon.WagonException; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** * A copy of stage's plugin RepositoryCopier but use WagonUpload and WagonDownload instead * * @plexus.component role="org.codehaus.mojo.wagon.shared.MavenRepoMerger" role-hint="default" */ public class DefaultMavenRepoMerger implements MavenRepoMerger { /** * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonDownload" */ private WagonDownload downloader; /** * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonUpload" */ private WagonUpload uploader; public void merge( Wagon src, Wagon target, boolean optimize, Log logger ) throws WagonException, IOException { String tempdir = System.getProperty( "java.io.tmpdir" ); //copy src to a local dir File downloadSrcDir = File.createTempFile( tempdir, "wagon" ); downloadSrcDir.delete(); WagonFileSet srcFileSet = new WagonFileSet(); srcFileSet.setDownloadDirectory( downloadSrcDir ); //ignore archiva/nexus .index at root dir String[] excludes = { ".index/**", ".indexer/**, .meta/**" }; srcFileSet.setExcludes( excludes ); try { downloader.download( src, srcFileSet, logger ); //merge metadata DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir( downloadSrcDir ); String[] includes = { "**/" + MAVEN_METADATA }; scanner.setIncludes( includes ); scanner.scan(); String[] files = scanner.getIncludedFiles(); for ( int i = 0; i < files.length; ++i ) { File srcMetadaFile = new File( downloadSrcDir, files[i] + IN_PROCESS_MARKER ); try { target.get( files[i].replace( '\\', '/' ), srcMetadaFile ); } catch ( ResourceDoesNotExistException e ) { // We don't have an equivalent on the targetRepositoryUrl side because we have something // new on the sourceRepositoryUrl side so just skip the metadata merging. continue; } try { mergeMetadata( srcMetadaFile, logger ); } catch ( XmlPullParserException e ) { throw new IOException( "Metadata file is corrupt " + files[i] + " Reason: " + e.getMessage() ); } } //upload to target FileSet tobeUploadedFileSet = new FileSet(); tobeUploadedFileSet.setDirectory( downloadSrcDir.getAbsolutePath() ); this.uploader.upload( target, tobeUploadedFileSet, optimize, logger ); } finally { FileUtils.deleteDirectory( downloadSrcDir ); } } private void mergeMetadata( File existingMetadata, Log logger ) throws IOException, XmlPullParserException { Writer stagedMetadataWriter = null; Reader existingMetadataReader = null; Reader stagedMetadataReader = null; File stagedMetadataFile = null; try { MetadataXpp3Reader xppReader = new MetadataXpp3Reader(); MetadataXpp3Writer xppWriter = new MetadataXpp3Writer(); // Existing Metadata in target stage existingMetadataReader = new FileReader( existingMetadata ); Metadata existing = xppReader.read( existingMetadataReader ); // Staged Metadata stagedMetadataFile = new File( existingMetadata.getParentFile(), MAVEN_METADATA ); stagedMetadataReader = new FileReader( stagedMetadataFile ); Metadata staged = xppReader.read( stagedMetadataReader ); // Merge and write back to staged metadata to replace the remote one existing.merge( staged ); stagedMetadataWriter = new FileWriter( stagedMetadataFile ); xppWriter.write( stagedMetadataWriter, existing ); logger.info( "Merging metadata file: " + stagedMetadataFile ); } finally { IOUtil.close( stagedMetadataWriter ); IOUtil.close( stagedMetadataReader ); IOUtil.close( existingMetadataReader ); existingMetadata.delete(); } // Mark all metadata as in-process and regenerate the checksums as they will be different // after the merger try { File newMd5 = new File( stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".md5" ); FileUtils.fileWrite( newMd5.getAbsolutePath(), checksum( stagedMetadataFile, MD5 ) ); File newSha1 = new File( stagedMetadataFile.getParentFile(), MAVEN_METADATA + ".sha1" ); FileUtils.fileWrite( newSha1.getAbsolutePath(), checksum( stagedMetadataFile, SHA1 ) ); } catch ( NoSuchAlgorithmException e ) { throw new RuntimeException( e ); } // We have the new merged copy so we're good } private String checksum( File file, String type ) throws IOException, NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance( type ); InputStream is = new FileInputStream( file ); byte[] buf = new byte[8192]; int i; while ( ( i = is.read( buf ) ) > 0 ) { md5.update( buf, 0, i ); } IOUtil.close( is ); return encode( md5.digest() ); } private String encode( byte[] binaryData ) { if ( binaryData.length != 16 && binaryData.length != 20 ) { int bitLength = binaryData.length * 8; throw new IllegalArgumentException( "Unrecognised length for binary data: " + bitLength + " bits" ); } String retValue = ""; for ( int i = 0; i < binaryData.length; i++ ) { String t = Integer.toHexString( binaryData[i] & 0xff ); if ( t.length() == 1 ) { retValue += ( "0" + t ); } else { retValue += t; } } return retValue.trim(); } }