001 /**
002 * Copyright 2008-2013 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 }