View Javadoc

1   /**
2    * Copyright 2008-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.codehaus.mojo.wagon.shared;
17  
18  /*
19   * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
20   * agreements. See the NOTICE file distributed with this work for additional information regarding
21   * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
22   * "License"); you may not use this file except in compliance with the License. You may obtain a
23   * copy of the License at
24   *
25   * http://www.apache.org/licenses/LICENSE-2.0
26   *
27   * Unless required by applicable law or agreed to in writing, software distributed under the License
28   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29   * or implied. See the License for the specific language governing permissions and limitations under
30   * the License.
31   */
32  
33  import java.io.File;
34  import java.io.FileInputStream;
35  import java.io.FileReader;
36  import java.io.FileWriter;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.io.Reader;
40  import java.io.Writer;
41  import java.security.MessageDigest;
42  import java.security.NoSuchAlgorithmException;
43  
44  import org.apache.maven.artifact.repository.metadata.Metadata;
45  import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
46  import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
47  import org.apache.maven.plugin.logging.Log;
48  import org.apache.maven.shared.model.fileset.FileSet;
49  import org.apache.maven.wagon.ResourceDoesNotExistException;
50  import org.apache.maven.wagon.Wagon;
51  import org.apache.maven.wagon.WagonException;
52  import org.codehaus.plexus.util.DirectoryScanner;
53  import org.codehaus.plexus.util.FileUtils;
54  import org.codehaus.plexus.util.IOUtil;
55  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
56  
57  /**
58   * A copy of stage's plugin RepositoryCopier but use WagonUpload and WagonDownload instead
59   *
60   * @plexus.component role="org.codehaus.mojo.wagon.shared.MavenRepoMerger" role-hint="default"
61   */
62  
63  public class DefaultMavenRepoMerger implements MavenRepoMerger {
64      /**
65       * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonDownload"
66       */
67      private WagonDownload downloader;
68  
69      /**
70       * @plexus.requirement role="org.codehaus.mojo.wagon.shared.WagonUpload"
71       */
72      private WagonUpload uploader;
73  
74      @Override
75      public void merge(Wagon src, Wagon target, boolean optimize, Log logger) throws WagonException, IOException {
76  
77          // copy src to a local dir
78          File downloadSrcDir = createTempDirectory("wagon-maven-plugin");
79  
80          WagonFileSet srcFileSet = new WagonFileSet();
81          srcFileSet.setDownloadDirectory(downloadSrcDir);
82          // ignore archiva/nexus .index at root dir
83          String[] excludes = { ".index/**", ".indexer/**, .meta/**, .nexus/**" };
84          srcFileSet.setExcludes(excludes);
85  
86          try {
87              downloader.download(src, srcFileSet, logger, false);
88  
89              // merge metadata
90              DirectoryScanner scanner = new DirectoryScanner();
91              scanner.setBasedir(downloadSrcDir);
92              String[] includes = { "**/" + MAVEN_METADATA };
93              scanner.setIncludes(includes);
94              scanner.scan();
95              String[] files = scanner.getIncludedFiles();
96  
97              for (int i = 0; i < files.length; ++i) {
98                  File srcMetadaFile = new File(downloadSrcDir, files[i] + IN_PROCESS_MARKER);
99  
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 }