diff options
Diffstat (limited to 'java/com/google/gerrit/pgm/init/LibraryDownloader.java')
-rw-r--r-- | java/com/google/gerrit/pgm/init/LibraryDownloader.java | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/java/com/google/gerrit/pgm/init/LibraryDownloader.java b/java/com/google/gerrit/pgm/init/LibraryDownloader.java new file mode 100644 index 0000000000..0b31ee22ab --- /dev/null +++ b/java/com/google/gerrit/pgm/init/LibraryDownloader.java @@ -0,0 +1,316 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// 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.google.gerrit.pgm.init; + +import com.google.common.hash.Funnels; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteStreams; +import com.google.gerrit.common.Die; +import com.google.gerrit.common.IoUtil; +import com.google.gerrit.pgm.init.api.ConsoleUI; +import com.google.gerrit.server.config.SitePaths; +import com.google.inject.Inject; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.eclipse.jgit.util.HttpSupport; + +/** Get optional or required 3rd party library files into $site_path/lib. */ +class LibraryDownloader { + private final ConsoleUI ui; + private final Path lib_dir; + private final StaleLibraryRemover remover; + + private boolean required; + private String name; + private String jarUrl; + private String sha1; + private String remove; + private List<LibraryDownloader> needs; + private LibraryDownloader neededBy; + private Path dst; + private boolean download; // download or copy + private boolean exists; + private boolean skipDownload; + + @Inject + LibraryDownloader(ConsoleUI ui, SitePaths site, StaleLibraryRemover remover) { + this.ui = ui; + this.lib_dir = site.lib_dir; + this.remover = remover; + this.needs = new ArrayList<>(2); + } + + void setName(String name) { + this.name = name; + } + + void setJarUrl(String url) { + this.jarUrl = url; + download = jarUrl.startsWith("http"); + } + + void setSHA1(String sha1) { + this.sha1 = sha1; + } + + void setRemove(String remove) { + this.remove = remove; + } + + void addNeeds(LibraryDownloader lib) { + needs.add(lib); + } + + void setSkipDownload(boolean skipDownload) { + this.skipDownload = skipDownload; + } + + void downloadRequired() { + setRequired(true); + download(); + } + + void downloadOptional() { + required = false; + download(); + } + + private void setRequired(boolean r) { + required = r; + for (LibraryDownloader d : needs) { + d.setRequired(r); + } + } + + private void download() { + if (skipDownload) { + return; + } + + if (jarUrl == null || !jarUrl.contains("/")) { + throw new IllegalStateException("Invalid JarUrl for " + name); + } + + final String jarName = jarUrl.substring(jarUrl.lastIndexOf('/') + 1); + if (jarName.contains("/") || jarName.contains("\\")) { + throw new IllegalStateException("Invalid JarUrl: " + jarUrl); + } + + if (name == null) { + name = jarName; + } + + dst = lib_dir.resolve(jarName); + if (Files.exists(dst)) { + exists = true; + } else if (shouldGet()) { + doGet(); + } + + if (exists) { + for (LibraryDownloader d : needs) { + d.neededBy = this; + d.downloadRequired(); + } + } + } + + private boolean shouldGet() { + if (ui.isBatch()) { + return required; + } + final StringBuilder msg = new StringBuilder(); + msg.append("\n"); + msg.append("Gerrit Code Review is not shipped with %s\n"); + if (neededBy != null) { + msg.append(String.format("** This library is required by %s. **\n", neededBy.name)); + } else if (required) { + msg.append("** This library is required for your configuration. **\n"); + } else { + msg.append(" If available, Gerrit can take advantage of features\n"); + msg.append(" in the library, but will also function without it.\n"); + } + msg.append(String.format("%s and install it now", download ? "Download" : "Copy")); + return ui.yesno(true, msg.toString(), name); + } + + private void doGet() { + if (!Files.exists(lib_dir)) { + try { + Files.createDirectories(lib_dir); + } catch (IOException e) { + throw new Die("Cannot create " + lib_dir, e); + } + } + + try { + remover.remove(remove); + if (download) { + doGetByHttp(); + } else { + doGetByLocalCopy(); + } + verifyFileChecksum(); + } catch (IOException err) { + try { + Files.delete(dst); + } catch (IOException e) { + // Delete failed; leave alone. + } + + if (ui.isBatch()) { + throw new Die("error: Cannot get " + jarUrl, err); + } + + System.err.println(); + System.err.println(); + System.err.println("error: " + err.getMessage()); + System.err.println("Please download:"); + System.err.println(); + System.err.println(" " + jarUrl); + System.err.println(); + System.err.println("and save as:"); + System.err.println(); + System.err.println(" " + dst.toAbsolutePath()); + System.err.println(); + System.err.flush(); + + ui.waitForUser(); + + if (Files.exists(dst)) { + verifyFileChecksum(); + + } else if (!ui.yesno(!required, "Continue without this library")) { + throw new Die("aborted by user"); + } + } + + if (Files.exists(dst)) { + exists = true; + IoUtil.loadJARs(dst); + } + } + + private void doGetByLocalCopy() throws IOException { + System.err.print("Copying " + jarUrl + " ..."); + Path p = url2file(jarUrl); + if (!Files.exists(p)) { + StringBuilder msg = + new StringBuilder() + .append("\n") + .append("Can not find the %s at this location: %s\n") + .append("Please provide alternative URL"); + p = url2file(ui.readString(null, msg.toString(), name, jarUrl)); + } + Files.copy(p, dst); + } + + private static Path url2file(String urlString) throws IOException { + final URL url = new URL(urlString); + try { + return Paths.get(url.toURI()); + } catch (URISyntaxException e) { + return Paths.get(url.getPath()); + } + } + + private void doGetByHttp() throws IOException { + System.err.print("Downloading " + jarUrl + " ..."); + System.err.flush(); + try (InputStream in = openHttpStream(jarUrl); + OutputStream out = Files.newOutputStream(dst)) { + ByteStreams.copy(in, out); + System.err.println(" OK"); + System.err.flush(); + } catch (IOException err) { + deleteDst(); + System.err.println(" !! FAIL !!"); + System.err.println(err); + System.err.flush(); + throw err; + } + } + + private static InputStream openHttpStream(String urlStr) throws IOException { + ProxySelector proxySelector = ProxySelector.getDefault(); + URL url = new URL(urlStr); + Proxy proxy = HttpSupport.proxyFor(proxySelector, url); + HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy); + + switch (HttpSupport.response(c)) { + case HttpURLConnection.HTTP_OK: + return c.getInputStream(); + + case HttpURLConnection.HTTP_NOT_FOUND: + throw new FileNotFoundException(url.toString()); + + default: + throw new IOException( + url.toString() + ": " + HttpSupport.response(c) + " " + c.getResponseMessage()); + } + } + + @SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility. + private void verifyFileChecksum() { + if (sha1 == null) { + System.err.println(); + System.err.flush(); + return; + } + Hasher h = Hashing.sha1().newHasher(); + try (InputStream in = Files.newInputStream(dst); + OutputStream out = Funnels.asOutputStream(h)) { + ByteStreams.copy(in, out); + } catch (IOException e) { + deleteDst(); + throw new Die("cannot checksum " + dst, e); + } + if (sha1.equals(h.hash().toString())) { + System.err.println("Checksum " + dst.getFileName() + " OK"); + System.err.flush(); + } else if (ui.isBatch()) { + deleteDst(); + throw new Die(dst + " SHA-1 checksum does not match"); + + } else if (!ui.yesno( + null /* force an answer */, + "error: SHA-1 checksum does not match\nUse %s anyway", // + dst.getFileName())) { + deleteDst(); + throw new Die("aborted by user"); + } + } + + private void deleteDst() { + try { + Files.delete(dst); + } catch (IOException e) { + System.err.println(" Failed to clean up lib: " + dst); + } + } +} |