Skip to content

Commit

Permalink
Merge branch 'main' into issue-4459
Browse files Browse the repository at this point in the history
  • Loading branch information
kysmith-csg authored Jan 7, 2025
2 parents 58aba7c + 975a541 commit 4190aef
Show file tree
Hide file tree
Showing 202 changed files with 1,451 additions and 186 deletions.
4 changes: 2 additions & 2 deletions demo/bnd-pde-workspace/.mvn/maven.config
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
-Dtycho-version=4.0.0-SNAPSHOT
-Dtarget-platform=https://download.eclipse.org/releases/2022-12/
-Dtycho-version=5.0.0-SNAPSHOT
-Dtarget-platform=https://download.eclipse.org/releases/2024-12/
-Dtycho.resolver.classic=false
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Bundle-ManifestVersion: 2
Bundle-Name: An Eclipse PDE Bundle
Bundle-SymbolicName: tycho.demo.util
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-RequiredExecutionEnvironment: JavaSE-17
Export-Package: org.eclipse.tycho.demo.plugin
Import-Package: org.eclipse.tycho.demo.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2024 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.p2maven.repository;

import javax.inject.Inject;
import javax.inject.Named;

import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepositoryFactory;
import org.eclipse.equinox.p2.core.IProvisioningAgent;

@Named
public class DefaultSimpleArtifactRepositoryFactory extends SimpleArtifactRepositoryFactory {

private IProvisioningAgent agent;

@Inject
public DefaultSimpleArtifactRepositoryFactory(IProvisioningAgent agent) {
this.agent = agent;
}

@Override
protected IProvisioningAgent getAgent() {
return agent;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/*******************************************************************************
* Copyright (c) 2024 Christoph Läubrich and others.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.p2maven.transport;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

import javax.inject.Inject;
import javax.inject.Named;

import org.codehaus.plexus.logging.Logger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepositoryFactory;
import org.eclipse.equinox.internal.p2.repository.DownloadStatus;
import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumHelper;
import org.eclipse.equinox.internal.p2.repository.helpers.RepositoryHelper;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository;
import org.eclipse.tycho.TychoConstants;
import org.eclipse.tycho.helper.MavenPropertyHelper;
import org.eclipse.tycho.transport.ArtifactDownloadProvider;

/**
* Provides artifacts already available from the users bundle pools
*/
@Named
public class BundlePoolArtifactDownloadProvider implements ArtifactDownloadProvider {

private SimpleArtifactRepositoryFactory artifactRepositoryFactory;
private Map<Path, IArtifactRepository> repositoryMap = new ConcurrentHashMap<>();
private TransportCacheConfig cacheConfig;
private Logger logger;
private boolean useSharedPools;
private boolean useWorkspacePools;
private int priority;

@Inject
public BundlePoolArtifactDownloadProvider(SimpleArtifactRepositoryFactory artifactRepositoryFactory,
TransportCacheConfig cacheConfig, Logger logger, MavenPropertyHelper propertyHelper) {
this.artifactRepositoryFactory = artifactRepositoryFactory;
this.cacheConfig = cacheConfig;
this.logger = logger;
useSharedPools = propertyHelper.getGlobalBooleanProperty("tycho.p2.transport.bundlepools.shared", true);
useWorkspacePools = propertyHelper.getGlobalBooleanProperty("tycho.p2.transport.bundlepools.workspace", true);
priority = propertyHelper.getGlobalIntProperty("tycho.p2.transport.bundlepools.priority", 100);

}

@Override
public IStatus downloadArtifact(URI source, OutputStream target, IArtifactDescriptor originalDescriptor) {
return pools().parallel().flatMap(path -> {
IArtifactRepository repository = getRepository(path);
if (repository instanceof IFileArtifactRepository filerepository) {
IArtifactKey artifactKey = originalDescriptor.getArtifactKey();
File artifactFile = filerepository.getArtifactFile(artifactKey);
if (artifactFile != null) {
return Arrays.stream(repository.getArtifactDescriptors(artifactKey)).map(
descriptor -> new RepositoryCandidate(filerepository, descriptor, artifactFile.toPath()));
}
}
return Stream.empty();
}).filter(cand -> isMatch(cand, originalDescriptor)).findAny().map(candidate -> {
IArtifactRepository repository = candidate.repository();
if (cacheConfig.isInteractive()) {
logger.info("Reading from " + repository.getName() + ": " + candidate.artifactFile());
}
return copyToTarget(target, candidate.artifactFile());
}).orElse(Status.CANCEL_STATUS);
}

/**
* Test if two descriptors have at least one matching hashsum in which case we
* assume they are describing the same artifact and not only have the same
* version/id, this should not happen usually, but as we use global pools here
* it is better to be safe than sorry.
*
* @param candidate the candidate we want to use
* @param originalDescriptor the original descriptor queried
* @return <code>true</code> if at least one hashsum matches in both descriptors
*/
private boolean isMatch(RepositoryCandidate candidate, IArtifactDescriptor originalDescriptor) {
Path artifactFile = candidate.artifactFile();
if (Files.isRegularFile(artifactFile)) {
// we can only use files as we need to process them as if downloaded from a real
// server...
IArtifactDescriptor repositoryDescriptor = candidate.descriptor();
// now see if we can perform a fast check by comparing original hashsums
for (Entry<String, String> entry : originalDescriptor.getProperties().entrySet()) {
String key = entry.getKey();
if (key.startsWith(TychoConstants.PROP_DOWNLOAD_CHECKSUM_PREFIX)) {
String property = repositoryDescriptor.getProperty(key);
if (property != null) {
String value = entry.getValue();
return value.equals(property);
}
}
}
if (fileSizeMatch(repositoryDescriptor, originalDescriptor)) {
// if we are here, then it means no download checksums where present for
// comparison and we need to generate one ourself
for (Entry<String, String> entry : originalDescriptor.getProperties().entrySet()) {
String key = entry.getKey();
if (key.startsWith(TychoConstants.PROP_DOWNLOAD_CHECKSUM_PREFIX)) {
try {
String algorithm = key.substring(TychoConstants.PROP_DOWNLOAD_CHECKSUM_PREFIX.length())
.toUpperCase();
MessageDigest md = MessageDigest.getInstance(algorithm);
try (DigestOutputStream outputStream = new DigestOutputStream(
OutputStream.nullOutputStream(), md);
InputStream inputStream = Files.newInputStream(artifactFile)) {
inputStream.transferTo(outputStream);
}
return ChecksumHelper.toHexString(md.digest()).equals(entry.getValue());
} catch (Exception e) {
// can't check...
}
}
}
}
}
return false;
}

private static IStatus copyToTarget(OutputStream target, Path path) {
try {
Files.copy(path, target);
} catch (IOException e) {
return Status.error("Can't copy file to target", e);
}
DownloadStatus status = new DownloadStatus(IStatus.OK, "org.eclipse.tycho", "File " + path, null);
try {
status.setFileSize(Files.size(path));
} catch (IOException e) {
}
try {
status.setLastModified(Files.getLastModifiedTime(path).toMillis());
} catch (IOException e) {
}
return status;
}

private boolean fileSizeMatch(IArtifactDescriptor repositoryDescriptor, IArtifactDescriptor originalDescriptor) {
String originalSize = originalDescriptor.getProperty(IArtifactDescriptor.DOWNLOAD_SIZE);
if (originalSize != null) {
String property = repositoryDescriptor.getProperty(IArtifactDescriptor.DOWNLOAD_SIZE);
if (property != null) {
return originalSize.equals(property);
}
}
// assume true for further processing
return true;
}

private Stream<Path> pools() {
if (useSharedPools) {
if (useWorkspacePools) {
List<Path> sharedBundlePools = RepositoryHelper.getSharedBundlePools();
List<Path> workspaceBundlePools = RepositoryHelper.getWorkspaceBundlePools();
return Stream.concat(sharedBundlePools.stream(), workspaceBundlePools.stream()).distinct();
} else {
return RepositoryHelper.getSharedBundlePools().stream();
}
} else if (useWorkspacePools) {
return RepositoryHelper.getWorkspaceBundlePools().stream();
} else {
return Stream.empty();
}
}

private IArtifactRepository getRepository(Path path) {
return repositoryMap.computeIfAbsent(path, p -> {
try {
return artifactRepositoryFactory.load(path.toUri(), 0, null);
} catch (ProvisionException e) {
return null;
}
});
}

private static record RepositoryCandidate(IFileArtifactRepository repository, IArtifactDescriptor descriptor,
Path artifactFile) {

}

@Override
public int getPriority() {
return priority;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.net.URI;

import org.codehaus.plexus.component.annotations.Component;
import org.eclipse.tycho.transport.TransportProtocolHandler;

@Component(role = TransportProtocolHandler.class, hint = "file")
public class FileTransportProtocolHandler implements TransportProtocolHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
import org.eclipse.tycho.MavenRepositorySettings.Credentials;
import org.eclipse.tycho.transport.TransportProtocolHandler;

/**
* Handles files discovery over the FTP protocol.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.tycho.transport.TransportProtocolHandler;

@Component(role = TransportProtocolHandler.class, hint = "http")
public class HttpTransportProtocolHandler implements TransportProtocolHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.eclipse.tycho.p2maven.transport;

import org.codehaus.plexus.component.annotations.Component;
import org.eclipse.tycho.transport.TransportProtocolHandler;

@Component(role = TransportProtocolHandler.class, hint = "https")
public class HttpsTransportProtocolHandler extends HttpTransportProtocolHandler {
Expand Down
Loading

0 comments on commit 4190aef

Please sign in to comment.