Skip to main content
summaryrefslogtreecommitdiffstats
blob: 21127075e87a39285ec750692d2fe98aebc11131 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/*******************************************************************************
 *  Copyright (c) 2007, 2017 IBM Corporation and others.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 * 
 *  Contributors:
 *     IBM Corporation - initial API and implementation
 *  	Compeople AG (Stefan Liebig) - various ongoing maintenance
 *      Sonatype, Inc. - transport split
 *******************************************************************************/
package org.eclipse.equinox.p2.internal.repository.mirroring;

import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository;
import org.eclipse.equinox.internal.p2.artifact.repository.RawMirrorRequest;
import org.eclipse.equinox.internal.p2.repository.Transport;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.internal.repository.tools.Activator;
import org.eclipse.equinox.p2.internal.repository.tools.Messages;
import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.repository.artifact.*;
import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor;
import org.eclipse.equinox.p2.repository.tools.comparator.ArtifactComparatorFactory;
import org.eclipse.equinox.p2.repository.tools.comparator.IArtifactComparator;
import org.eclipse.osgi.util.NLS;

/**
 * A utility class that performs mirroring of artifacts between repositories.
 */
public class Mirroring {
	private IArtifactRepository source;
	private IArtifactRepository destination;
	private IArtifactRepository baseline;
	private boolean raw;
	private boolean compare = false;
	private boolean validate = false;
	private IArtifactComparator comparator;
	private IQuery<IArtifactDescriptor> compareExclusionQuery = null;
	private Set<IArtifactDescriptor> compareExclusions = Collections.<IArtifactDescriptor> emptySet();
	private String comparatorID;
	private List<IArtifactKey> keysToMirror;
	private IArtifactMirrorLog comparatorLog;
	private Transport transport;
	private boolean includePacked = true;
	private boolean mirrorProperties = false;

	private IArtifactComparator getComparator() {
		if (comparator == null)
			comparator = ArtifactComparatorFactory.getArtifactComparator(comparatorID);
		return comparator;
	}

	public Mirroring(IArtifactRepository source, IArtifactRepository destination, boolean raw) {
		this.source = source;
		this.destination = destination;
		this.raw = raw;
	}

	public void setCompare(boolean compare) {
		this.compare = compare;
	}

	public void setComparatorId(String id) {
		this.comparatorID = id;
	}

	public void setComparatorLog(IArtifactMirrorLog comparatorLog) {
		this.comparatorLog = comparatorLog;
	}

	public void setBaseline(IArtifactRepository baseline) {
		this.baseline = baseline;
	}

	public void setValidate(boolean validate) {
		this.validate = validate;
	}

	public void setMirrorProperties(boolean properties) {
		this.mirrorProperties = properties;
	}

	public MultiStatus run(boolean failOnError, boolean verbose) {
		if (!destination.isModifiable())
			throw new IllegalStateException(NLS.bind(Messages.exception_destinationNotModifiable, destination.getLocation()));
		if (compare)
			getComparator(); //initialize the comparator. Only needed if we're comparing. Used to force error if comparatorID is invalid.
		MultiStatus multiStatus = new MultiStatus(Activator.ID, IStatus.OK, Messages.message_mirroringStatus, null);
		Iterator<IArtifactKey> keys = null;
		if (keysToMirror != null)
			keys = keysToMirror.iterator();
		else {
			IQueryResult<IArtifactKey> result = source.query(ArtifactKeyQuery.ALL_KEYS, null);
			keys = result.iterator();
		}

		if (compareExclusionQuery != null) {
			IQueryResult<IArtifactDescriptor> exclusions = source.descriptorQueryable().query(compareExclusionQuery, null);
			compareExclusions = exclusions.toUnmodifiableSet();
		}

		while (keys.hasNext()) {
			IArtifactKey key = keys.next();
			IArtifactDescriptor[] descriptors = source.getArtifactDescriptors(key);
			for (int j = 0; j < descriptors.length; j++) {
				IStatus result = mirror(descriptors[j], verbose);
				//Only log INFO and WARNING if we want verbose logging. Always log ERRORs
				if (!result.isOK() && (verbose || result.getSeverity() == IStatus.ERROR))
					multiStatus.add(result);
				//stop mirroring as soon as we have an error
				if (failOnError && multiStatus.getSeverity() == IStatus.ERROR)
					return multiStatus;
			}
		}

		// mirror the source repository's properties unless they are already set up
		// in the destination repository
		if (mirrorProperties) {
			IArtifactRepository toCopyFrom = source;
			if (toCopyFrom instanceof CompositeArtifactRepository) {
				List<IArtifactRepository> children = ((CompositeArtifactRepository) toCopyFrom).getLoadedChildren();
				if (children.size() > 0)
					toCopyFrom = children.get(0);
			}
			Map<String, String> sourceProperties = toCopyFrom.getProperties();
			for (String key : sourceProperties.keySet()) {
				if (!destination.getProperties().containsKey(key))
					destination.setProperty(key, sourceProperties.get(key));
			}
		}

		if (validate) {
			// Simple validation of the mirror
			IStatus validation = validateMirror(verbose);
			if (!validation.isOK() && (verbose || validation.getSeverity() == IStatus.ERROR))
				multiStatus.add(validation);
		}
		return multiStatus;
	}

	private IStatus mirror(IArtifactDescriptor sourceDescriptor, boolean verbose) {
		if (!includePacked && IArtifactDescriptor.FORMAT_PACKED.equals(sourceDescriptor.getProperty(IArtifactDescriptor.FORMAT)))
			return Status.OK_STATUS;

		IArtifactDescriptor targetDescriptor = raw ? sourceDescriptor : new ArtifactDescriptor(sourceDescriptor);
		IArtifactDescriptor baselineDescriptor = getBaselineDescriptor(sourceDescriptor);

		if (verbose)
			System.out.println("Mirroring: " + sourceDescriptor.getArtifactKey() + " (Descriptor: " + sourceDescriptor + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

		MultiStatus compareStatus = new MultiStatus(Activator.ID, IStatus.OK, null, null);
		boolean comparing = compare && !compareExclusions.contains(sourceDescriptor);
		if (comparing) {
			if (baselineDescriptor != null) {
				//compare source & baseline
				compareStatus.add(compare(baseline, baselineDescriptor, source, sourceDescriptor));
				//compare baseline & destination
				if (destination.contains(baselineDescriptor)) {
					compareStatus.add(compareToDestination(baselineDescriptor));
					return compareStatus;
				}
			} else if (destination.contains(targetDescriptor)) {
				compareStatus.add(compareToDestination(sourceDescriptor));
				return compareStatus;
			}
		}

		//from source or baseline
		IArtifactRepository sourceRepository = baselineDescriptor != null ? baseline : source;
		sourceDescriptor = baselineDescriptor != null ? baselineDescriptor : sourceDescriptor;
		targetDescriptor = baselineDescriptor != null ? baselineDescriptor : targetDescriptor;
		IStatus status = null;
		if (!destination.contains(targetDescriptor))
			//actual download
			status = downloadArtifact(sourceRepository, targetDescriptor, sourceDescriptor);
		else {
			String message = NLS.bind(Messages.mirror_alreadyExists, sourceDescriptor, destination);
			status = new Status(IStatus.INFO, Activator.ID, ProvisionException.ARTIFACT_EXISTS, message, null);
		}

		if (comparing) {
			compareStatus.add(status);
			return compareStatus;
		}
		return status;
	}

	/**
	 * Takes an IArtifactDescriptor descriptor and the ProvisionException that was thrown when destination.getOutputStream(descriptor)
	 * and compares descriptor to the duplicate descriptor in the destination.
	 * 
	 * Callers should verify the ProvisionException was thrown due to the artifact existing in the destination before invoking this method.
	 * @param descriptor
	 * @return the status of the compare
	 */
	private IStatus compareToDestination(IArtifactDescriptor descriptor) {
		IArtifactDescriptor[] destDescriptors = destination.getArtifactDescriptors(descriptor.getArtifactKey());
		IArtifactDescriptor destDescriptor = null;
		for (int i = 0; destDescriptor == null && i < destDescriptors.length; i++) {
			if (destDescriptors[i].equals(descriptor))
				destDescriptor = destDescriptors[i];
		}
		if (destDescriptor == null)
			return new Status(IStatus.INFO, Activator.ID, ProvisionException.ARTIFACT_EXISTS, Messages.Mirroring_noMatchingDescriptor, null);
		return compare(source, descriptor, destination, destDescriptor);
	}

	private IStatus compare(IArtifactRepository sourceRepository, IArtifactDescriptor sourceDescriptor, IArtifactRepository destRepository, IArtifactDescriptor destDescriptor) {
		IStatus comparison = getComparator().compare(sourceRepository, sourceDescriptor, destRepository, destDescriptor);
		if (comparatorLog != null && !comparison.isOK())
			comparatorLog.log(sourceDescriptor, comparison);
		return comparison;
	}

	/*
	 * Create, and execute a MirrorRequest for a given descriptor.
	 */
	private IStatus downloadArtifact(IArtifactRepository sourceRepo, IArtifactDescriptor destDescriptor, IArtifactDescriptor srcDescriptor) {
		RawMirrorRequest request = new RawMirrorRequest(srcDescriptor, destDescriptor, destination, transport);
		request.perform(sourceRepo, new NullProgressMonitor());

		return request.getResult();
	}

	public void setArtifactKeys(IArtifactKey[] keys) {
		this.keysToMirror = Arrays.asList(keys);
	}

	/*
	 *  Get the equivalent descriptor from the baseline repository
	 */
	private IArtifactDescriptor getBaselineDescriptor(IArtifactDescriptor descriptor) {
		if (baseline == null || !baseline.contains(descriptor))
			return null;

		IArtifactDescriptor[] baselineDescriptors = baseline.getArtifactDescriptors(descriptor.getArtifactKey());
		for (int i = 0; i < baselineDescriptors.length; i++) {
			if (baselineDescriptors[i].equals(descriptor))
				return baselineDescriptors[i];
		}
		return null;
	}

	/* 
	 * Simple validation of a mirror to see if all source descriptors are present in the destination
	 */
	private IStatus validateMirror(boolean verbose) {
		MultiStatus status = new MultiStatus(Activator.ID, 0, Messages.Mirroring_ValidationError, null);

		// The keys that were mirrored in this session
		Iterator<IArtifactKey> keys = null;
		if (keysToMirror != null) {
			keys = keysToMirror.iterator();
		} else {
			IQueryResult<IArtifactKey> result = source.query(ArtifactKeyQuery.ALL_KEYS, null);
			keys = result.iterator();
		}
		while (keys.hasNext()) {
			IArtifactKey artifactKey = keys.next();
			IArtifactDescriptor[] srcDescriptors = source.getArtifactDescriptors(artifactKey);
			IArtifactDescriptor[] destDescriptors = destination.getArtifactDescriptors(artifactKey);

			Arrays.sort(srcDescriptors, new ArtifactDescriptorComparator());
			Arrays.sort(destDescriptors, new ArtifactDescriptorComparator());

			int src = 0;
			int dest = 0;
			while (src < srcDescriptors.length && dest < destDescriptors.length) {
				if (!destDescriptors[dest].equals(srcDescriptors[src])) {
					if (destDescriptors[dest].toString().compareTo((srcDescriptors[src].toString())) > 0) {
						// Missing an artifact
						if (verbose)
							System.out.println(NLS.bind(Messages.Mirroring_missingDescriptor, srcDescriptors[src]));
						status.add(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Mirroring_missingDescriptor, srcDescriptors[src++])));
					} else {
						// Its okay if there are extra descriptors in the destination
						dest++;
					}
				} else {
					// check properties for differences
					Map<String, String> destMap = destDescriptors[dest].getProperties();
					Map<String, String> srcProperties = null;
					if (baseline != null) {
						IArtifactDescriptor baselineDescriptor = getBaselineDescriptor(destDescriptors[dest]);
						if (baselineDescriptor != null)
							srcProperties = baselineDescriptor.getProperties();
					}
					// Baseline not set, or could not find descriptor so we'll use the source descriptor
					if (srcProperties == null)
						srcProperties = srcDescriptors[src].getProperties();

					// Cycle through properties of the originating descriptor & compare
					for (String key : srcProperties.keySet()) {
						if (!srcProperties.get(key).equals(destMap.get(key))) {
							if (verbose)
								System.out.println(NLS.bind(Messages.Mirroring_differentDescriptorProperty, new Object[] {destDescriptors[dest], key, srcProperties.get(key), destMap.get(key)}));
							status.add(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.Mirroring_differentDescriptorProperty, new Object[] {destDescriptors[dest], key, srcProperties.get(key), destMap.get(key)})));
						}
					}
					src++;
					dest++;
				}
			}

			// If there are still source descriptors they're missing from the destination repository 
			while (src < srcDescriptors.length) {
				if (verbose)
					System.out.println(NLS.bind(Messages.Mirroring_missingDescriptor, srcDescriptors[src]));
				status.add(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.Mirroring_missingDescriptor, srcDescriptors[src++])));
			}
		}

		return status;
	}

	// Simple comparator for ArtifactDescriptors
	protected class ArtifactDescriptorComparator implements Comparator<IArtifactDescriptor> {

		@Override
		public int compare(IArtifactDescriptor arg0, IArtifactDescriptor arg1) {
			if (arg0 != null && arg1 != null)
				return arg0.toString().compareTo(arg1.toString());
			else if (arg1 == null && arg0 == null)
				return 0;
			else if (arg1 == null)
				return 1;
			return -1;
		}
	}

	public void setCompareExclusions(IQuery<IArtifactDescriptor> excludedKeys) {
		compareExclusionQuery = excludedKeys;
	}

	public void setTransport(Transport transport) {
		this.transport = transport;
	}

	public void setIncludePacked(boolean includePacked) {
		this.includePacked = includePacked;
	}
}

Back to the top