diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/DefaultHttpCacheConfig.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/DefaultHttpCacheConfig.java new file mode 100644 index 0000000000..de8facf7c8 --- /dev/null +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/DefaultHttpCacheConfig.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2022 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 org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.LegacySupport; +import org.apache.maven.repository.RepositorySystem; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; + +@Component(role = HttpCacheConfig.class) +public class DefaultHttpCacheConfig implements HttpCacheConfig, Initializable { + + private boolean offline; + private boolean update; + + @Requirement + private LegacySupport legacySupport; + private File cacheLocation; + + @Override + public void initialize() throws InitializationException { + File repoDir; + MavenSession session = legacySupport.getSession(); + if (session == null) { + repoDir = RepositorySystem.defaultUserLocalRepository; + offline = false; + update = false; + } else { + offline = session.isOffline(); + repoDir = new File(session.getLocalRepository().getBasedir()); + update = session.getRequest().isUpdateSnapshots(); + } + + cacheLocation = new File(repoDir, ".cache/tycho"); + cacheLocation.mkdirs(); + } + + @Override + public boolean isOffline() { + return offline; + } + + @Override + public boolean isUpdate() { + return update; + } + + @Override + public File getCacheLocation() { + return cacheLocation; + } + +} diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCache.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCache.java new file mode 100644 index 0000000000..565c1db3cb --- /dev/null +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCache.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2022 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.FileNotFoundException; +import java.net.URI; + +import org.codehaus.plexus.logging.Logger; + +public interface HttpCache { + + /** + * Fetches the cache entry for this URI + * + * @param uri + * @return + * @throws FileNotFoundException + * if the URI is know to be not found + */ + CacheEntry getCacheEntry(URI uri, Logger logger) throws FileNotFoundException; + + HttpCacheConfig getCacheConfig(); + +} \ No newline at end of file diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCacheConfig.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCacheConfig.java new file mode 100644 index 0000000000..78b605d1f3 --- /dev/null +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/HttpCacheConfig.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2022 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; + +public interface HttpCacheConfig { + + boolean isOffline(); + + boolean isUpdate(); + + File getCacheLocation(); +} diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteArtifactRepositoryManagerAgentFactory.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteArtifactRepositoryManagerAgentFactory.java index 4239e1d175..469c696300 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteArtifactRepositoryManagerAgentFactory.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteArtifactRepositoryManagerAgentFactory.java @@ -27,6 +27,9 @@ public class RemoteArtifactRepositoryManagerAgentFactory implements IAgentServic @Requirement Logger logger; + @Requirement + IRepositoryIdManager repositoryIdManager; + @Override public Object createService(IProvisioningAgent agent) { IArtifactRepositoryManager plainRepoManager = (IArtifactRepositoryManager) new ArtifactRepositoryComponent() @@ -35,8 +38,7 @@ public Object createService(IProvisioningAgent agent) { plainRepoManager = new P2MirrorDisablingArtifactRepositoryManager(plainRepoManager, logger); } - IRepositoryIdManager loadingHelper = agent.getService(IRepositoryIdManager.class); - return new RemoteArtifactRepositoryManager(plainRepoManager, loadingHelper); + return new RemoteArtifactRepositoryManager(plainRepoManager, repositoryIdManager); } private boolean getDisableP2MirrorsConfiguration() { diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteMetadataRepositoryManagerAgentFactory.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteMetadataRepositoryManagerAgentFactory.java index 2cbe067c27..5b5dc8315b 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteMetadataRepositoryManagerAgentFactory.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/RemoteMetadataRepositoryManagerAgentFactory.java @@ -27,12 +27,14 @@ public class RemoteMetadataRepositoryManagerAgentFactory implements IAgentServic @Requirement Logger logger; + @Requirement + IRepositoryIdManager repositoryIdManager; + @Override public Object createService(IProvisioningAgent agent) { IMetadataRepositoryManager plainMetadataRepoManager = (IMetadataRepositoryManager) new MetadataRepositoryComponent() .createService(agent); - IRepositoryIdManager loadingHelper = agent.getService(IRepositoryIdManager.class); - return new RemoteMetadataRepositoryManager(plainMetadataRepoManager, loadingHelper, logger); + return new RemoteMetadataRepositoryManager(plainMetadataRepoManager, repositoryIdManager, logger); } } diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java index 7d9a9e0d13..fdc0f5ed66 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java @@ -24,12 +24,10 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Properties; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -37,12 +35,16 @@ import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException; -public class SharedHttpCacheStorage { +@Component(role = HttpCache.class) +public class SharedHttpCacheStorage implements HttpCache { - /** + private static final int MAX_CACHE_LINES = Integer.getInteger("tycho.p2.transport.max-cache-lines", 1000); + /** * Assumes the following minimum caching period for remote files in minutes */ //TODO can we sync this with the time where maven updates snapshots? @@ -56,18 +58,17 @@ public class SharedHttpCacheStorage { private static final String ETAG_HEADER = "ETag"; - private static final Map storageMap = new HashMap<>(); - private static final int MAX_IN_MEMORY = 1000; + @Requirement + HttpCacheConfig cacheConfig; + private final Map entryCache; - private CacheConfig cacheConfig; - private SharedHttpCacheStorage(CacheConfig cacheConfig) { + public SharedHttpCacheStorage() { - this.cacheConfig = cacheConfig; - entryCache = new LinkedHashMap<>(100, 0.75f, true) { + entryCache = new LinkedHashMap<>(MAX_CACHE_LINES, 0.75f, true) { private static final long serialVersionUID = 1L; @@ -79,6 +80,11 @@ protected boolean removeEldestEntry(final Map.Entry eldest) { }; } + @Override + public HttpCacheConfig getCacheConfig() { + return cacheConfig; + } + /** * Fetches the cache entry for this URI * @@ -87,9 +93,10 @@ protected boolean removeEldestEntry(final Map.Entry eldest) { * @throws FileNotFoundException * if the URI is know to be not found */ + @Override public CacheEntry getCacheEntry(URI uri, Logger logger) throws FileNotFoundException { CacheLine cacheLine = getCacheLine(uri); - if (!cacheConfig.update) { //if not updates are forced ... + if (!cacheConfig.isUpdate()) { // if not updates are forced ... int code = cacheLine.getResponseCode(); if (code == HttpURLConnection.HTTP_NOT_FOUND) { throw new FileNotFoundException(uri.toASCIIString()); @@ -103,7 +110,7 @@ public CacheEntry getCacheEntry(URI uri, Logger logger) throws FileNotFoundExcep @Override public long getLastModified(HttpTransportFactory transportFactory) throws IOException { - if (cacheConfig.offline) { + if (cacheConfig.isOffline()) { return cacheLine.getLastModified(uri, transportFactory, SharedHttpCacheStorage::mavenIsOffline, logger); } @@ -113,7 +120,7 @@ public long getLastModified(HttpTransportFactory transportFactory) //for not found and failed authentication we can't do anything useful throw e; } catch (IOException e) { - if (!cacheConfig.update && cacheLine.getResponseCode() > 0) { + if (!cacheConfig.isUpdate() && cacheLine.getResponseCode() > 0) { //if we have something cached, use that ... logger.warn("Request to " + uri + " failed, trying cache instead..."); return cacheLine.getLastModified(uri, transportFactory, nil -> e, logger); @@ -125,7 +132,7 @@ public long getLastModified(HttpTransportFactory transportFactory) @Override public File getCacheFile(HttpTransportFactory transportFactory) throws IOException { - if (cacheConfig.offline) { + if (cacheConfig.isOffline()) { return cacheLine.getFile(uri, transportFactory, SharedHttpCacheStorage::mavenIsOffline, logger); } @@ -135,7 +142,7 @@ public File getCacheFile(HttpTransportFactory transportFactory) //for not found and failed authentication we can't do anything useful throw e; } catch (IOException e) { - if (!cacheConfig.update && cacheLine.getResponseCode() > 0) { + if (!cacheConfig.isUpdate() && cacheLine.getResponseCode() > 0) { //if we have something cached, use that ... logger.warn("Request to " + uri + " failed, trying cache instead..."); return cacheLine.getFile(uri, transportFactory, nil -> e, logger); @@ -148,7 +155,8 @@ public File getCacheFile(HttpTransportFactory transportFactory) } private synchronized CacheLine getCacheLine(URI uri) { - File file = new File(cacheConfig.location, uri.normalize().toASCIIString().replace(':', '/').replace('?', '/') + File file = new File(cacheConfig.getCacheLocation(), uri.normalize().toASCIIString().replace(':', '/') + .replace('?', '/') .replace('&', '/').replaceAll("/+", "/")); File location; try { @@ -292,7 +300,7 @@ public synchronized File getFile(URI uri, HttpTransportFactory transportFactory, } private boolean mustValidate() { - if (cacheConfig.update) { + if (cacheConfig.isUpdate()) { //user enforced validation return true; } @@ -429,42 +437,8 @@ private static boolean isNotFound(int code) { return code == HttpURLConnection.HTTP_NOT_FOUND; } - public static SharedHttpCacheStorage getStorage(File location, boolean offline, boolean update) { - return storageMap.computeIfAbsent(new CacheConfig(location, offline, update), SharedHttpCacheStorage::new); - } - private static IOException mavenIsOffline(URI uri) { return new IOException("maven is currently in offline mode requested URL " + uri + " does not exist locally!"); } - private static final class CacheConfig { - - private final File location; - private final boolean offline; - private final boolean update; - - public CacheConfig(File location, boolean offline, boolean update) { - this.location = location; - this.offline = offline; - this.update = update; - } - - @Override - public int hashCode() { - return Objects.hash(location, offline, update); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CacheConfig other = (CacheConfig) obj; - return Objects.equals(location, other.location) && offline == other.offline && update == other.update; - } - } - } diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransport.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransport.java index f123e31794..90f53d2736 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransport.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransport.java @@ -21,9 +21,13 @@ import java.net.URI; import java.net.URLConnection; import java.text.NumberFormat; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.atomic.LongAdder; import org.apache.commons.io.IOUtils; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -34,26 +38,29 @@ import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; +@Component(role = org.eclipse.equinox.internal.p2.repository.Transport.class, hint = "tycho") public class TychoRepositoryTransport extends org.eclipse.equinox.internal.p2.repository.Transport implements IAgentServiceFactory { + static final String TRANSPORT_TYPE = System.getProperty("tycho.p2.transport.type", + URLHttpTransportFactory.HINT); private static final boolean DEBUG_REQUESTS = Boolean.getBoolean("tycho.p2.transport.debug"); private NumberFormat numberFormat = NumberFormat.getNumberInstance(); - private Logger logger; - private SharedHttpCacheStorage httpCache; + @Requirement + Map transportFactoryMap; + + @Requirement + Logger logger; + @Requirement + HttpCache httpCache; + private LongAdder requests = new LongAdder(); private LongAdder indexRequests = new LongAdder(); - private HttpTransportFactory transportFactory; - - public TychoRepositoryTransport(Logger logger, SharedHttpCacheStorage httpCache, - HttpTransportFactory transportFactory) { - this.logger = logger; - this.transportFactory = transportFactory; + public TychoRepositoryTransport() { numberFormat.setMaximumFractionDigits(2); - this.httpCache = httpCache; } @Override @@ -132,7 +139,8 @@ public long getLastModified(URI toDownload, IProgressMonitor monitor) //TODO P2 cache manager relies on this method to throw an exception to work correctly try { if (isHttp(toDownload)) { - return httpCache.getCacheEntry(toDownload, logger).getLastModified(transportFactory); + return httpCache.getCacheEntry(toDownload, logger) + .getLastModified(getTransportFactory()); } URLConnection connection = toDownload.toURL().openConnection(); long lastModified = connection.getLastModified(); @@ -146,19 +154,23 @@ public long getLastModified(URI toDownload, IProgressMonitor monitor) } } + private HttpTransportFactory getTransportFactory() { + return Objects.requireNonNull(transportFactoryMap.get(TRANSPORT_TYPE), "Invalid transport configuration"); + } + @Override public Object createService(IProvisioningAgent agent) { return this; } - public SharedHttpCacheStorage getHttpCache() { + public HttpCache getHttpCache() { return httpCache; } public File getCachedFile(URI remoteFile) throws IOException { if (isHttp(remoteFile)) { - return httpCache.getCacheEntry(remoteFile, logger).getCacheFile(transportFactory); + return httpCache.getCacheEntry(remoteFile, logger).getCacheFile(getTransportFactory()); } return null; } diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransportAgentFactory.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransportAgentFactory.java index c7c26fd5ea..3d59f2e13f 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransportAgentFactory.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/TychoRepositoryTransportAgentFactory.java @@ -12,12 +12,6 @@ *******************************************************************************/ package org.eclipse.tycho.p2maven.transport; -import java.io.File; -import java.util.Map; - -import org.apache.maven.execution.MavenSession; -import org.apache.maven.plugin.LegacySupport; -import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; @@ -25,71 +19,41 @@ import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; -import org.eclipse.sisu.equinox.EquinoxServiceFactory; import org.eclipse.tycho.MavenRepositorySettings; -import org.eclipse.tycho.p2maven.helper.ProxyHelper; @Component(role = IAgentServiceFactory.class, hint = "org.eclipse.equinox.internal.p2.repository.Transport") public class TychoRepositoryTransportAgentFactory implements IAgentServiceFactory, Initializable { - private static final String TRANSPORT_TYPE = System.getProperty("tycho.p2.transport.type", - URLHttpTransportFactory.HINT); - @Requirement(hint = "connect") - private EquinoxServiceFactory serviceFactory; - - @Requirement - private LegacySupport legacySupport; - @Requirement private MavenRepositorySettings mavenRepositorySettings; @Requirement private Logger logger; @Requirement - private ProxyHelper proxyHelper; + HttpCache cache; - @Requirement - MavenAuthenticator mavenAuthenticator; + @Requirement(hint = "tycho") + org.eclipse.equinox.internal.p2.repository.Transport repositoryTransport; - @Requirement - private Map transportFactoryMap; - - private File repoDir; - - private boolean offline; - private boolean update; @Override public Object createService(IProvisioningAgent agent) { - - File cacheLocation = new File(repoDir, ".cache/tycho"); - cacheLocation.mkdirs(); + HttpCacheConfig config = cache.getCacheConfig(); logger.info("### Using TychoRepositoryTransport for remote P2 access ###"); - logger.info(" Cache location: " + cacheLocation); - logger.info(" Transport mode: " + (offline ? "offline" : "online")); - logger.info(" Transport type: " + TRANSPORT_TYPE); - logger.info(" Update mode: " + (update ? "forced" : "cache first")); + logger.info(" Cache location: " + config.getCacheLocation()); + logger.info(" Transport mode: " + (config.isOffline() ? "offline" : "online")); + logger.info(" Transport type: " + TychoRepositoryTransport.TRANSPORT_TYPE); + logger.info(" Update mode: " + (config.isUpdate() ? "forced" : "cache first")); logger.info(" Minimum cache duration: " + SharedHttpCacheStorage.MIN_CACHE_PERIOD + " minutes"); logger.info( " (you can configure this with -Dtycho.p2.transport.min-cache-minutes=)"); - SharedHttpCacheStorage cache = SharedHttpCacheStorage.getStorage(cacheLocation, offline, update); - - return new TychoRepositoryTransport(logger, cache, transportFactoryMap.get(TRANSPORT_TYPE)); + return repositoryTransport; } @Override public void initialize() throws InitializationException { - MavenSession session = legacySupport.getSession(); - if (session == null) { - repoDir = RepositorySystem.defaultUserLocalRepository; - offline = false; - update = false; - } else { - offline = session.isOffline(); - repoDir = new File(session.getLocalRepository().getBasedir()); - update = session.getRequest().isUpdateSnapshots(); - } + }