/*
* Copyright 2015-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.file;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.cli.DownloadConfig;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.log.Logger;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;
/**
* A {@link Downloader} which is composed of many other downloaders. When asked to download a
* resource, these are each called in order until one succeeds or the final one fails.
*/
public class StackedDownloader implements Downloader {
private static final Logger LOG = Logger.get(StackedDownloader.class);
private final ImmutableList<Downloader> delegates;
@VisibleForTesting
StackedDownloader(ImmutableList<Downloader> delegates) {
Preconditions.checkArgument(delegates.size() > 0);
this.delegates = delegates;
}
public static Downloader createFromConfig(BuckConfig config, Optional<Path> androidSdkRoot) {
ImmutableList.Builder<Downloader> downloaders = ImmutableList.builder();
DownloadConfig downloadConfig = new DownloadConfig(config);
Optional<Proxy> proxy = downloadConfig.getProxy();
HttpDownloader httpDownloader = new HttpDownloader(proxy);
for (Map.Entry<String, String> kv : downloadConfig.getAllMavenRepos().entrySet()) {
String repo = Preconditions.checkNotNull(kv.getValue());
// Check the type.
if (repo.startsWith("http:") || repo.startsWith("https://")) {
String repoName = kv.getKey();
Optional<PasswordAuthentication> credentials = downloadConfig.getRepoCredentials(repoName);
downloaders.add(new RemoteMavenDownloader(httpDownloader, repo, credentials));
} else if (repo.startsWith("file:")) {
try {
URL url = new URL(repo);
Preconditions.checkNotNull(url.getPath());
downloaders.add(
new OnDiskMavenDownloader(
config.resolvePathThatMayBeOutsideTheProjectFilesystem(
Paths.get(url.getPath()))));
} catch (FileNotFoundException e) {
throw new HumanReadableException(
e,
"Error occurred when attempting to use %s "
+ "as a local Maven repository as configured in .buckconfig. See "
+ "https://buckbuild.com/concept/buckconfig.html#maven_repositories for how to "
+ "configure this setting",
repo);
} catch (MalformedURLException e) {
throw new HumanReadableException("Unable to determine path from %s", repo);
}
} else {
try {
downloaders.add(
new OnDiskMavenDownloader(
Preconditions.checkNotNull(
config.resolvePathThatMayBeOutsideTheProjectFilesystem(Paths.get(repo)))));
} catch (FileNotFoundException e) {
throw new HumanReadableException(
e,
"Error occurred when attempting to use %s "
+ "as a local Maven repository as configured in .buckconfig. See "
+ "https://buckbuild.com/concept/buckconfig.html#maven_repositories for how to "
+ "configure this setting",
repo);
}
}
}
if (androidSdkRoot.isPresent()) {
Path androidMavenRepo = androidSdkRoot.get().resolve("extras/android/m2repository");
try {
downloaders.add(new OnDiskMavenDownloader(androidMavenRepo));
} catch (FileNotFoundException e) {
LOG.warn("Android Maven repo %s doesn't exist", androidMavenRepo.toString());
}
Path googleMavenRepo = androidSdkRoot.get().resolve("extras/google/m2repository");
try {
downloaders.add(new OnDiskMavenDownloader(googleMavenRepo));
} catch (FileNotFoundException e) {
LOG.warn("Google Maven repo '%s' doesn't exist", googleMavenRepo.toString());
}
}
// Add a default downloader
// TODO(simons): Remove the maven_repo check
Optional<String> defaultMavenRepo = downloadConfig.getMavenRepo();
if (defaultMavenRepo.isPresent()) {
LOG.warn(
"Please configure maven repos by adding them to a 'maven_repositories' "
+ "section in your buckconfig");
}
downloaders.add(
downloadConfig
.getMaxNumberOfRetries()
.map(retries -> (Downloader) RetryingDownloader.from(httpDownloader, retries))
.orElse(httpDownloader));
return new StackedDownloader(downloaders.build());
}
@Override
public boolean fetch(BuckEventBus eventBus, URI uri, Path output) throws IOException {
for (Downloader downloader : delegates) {
try {
if (downloader.fetch(eventBus, uri, output)) {
return true;
}
} catch (IOException e) {
LOG.debug(e, "Unable to download %s from %s", uri, downloader);
}
}
return false;
}
}