Skip to content

Commit 9cae897

Browse files
committed
Reduce flakiness of license fetcher.
This commit is a simple fix to reduce the flakiness of downloading licenses. The fetcher will now try up to three times and will insert a small delay between each attempt. This delay will only affect builds that would otherwise fail.
1 parent 74905c8 commit 9cae897

File tree

2 files changed

+95
-90
lines changed

2 files changed

+95
-90
lines changed

buildSrc/src/main/groovy/com/google/firebase/gradle/plugins/license/DownloadLicenseTask.groovy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ class DownloadLicenseTask extends DefaultTask {
3030
@TaskAction
3131
void execute(IncrementalTaskInputs inputs) {
3232
parsers.each { parser ->
33-
println "Downloading license from ${parser.getServiceUri()} ..."
33+
println "Downloading license from ${parser.getRemoteUrl()} ..."
3434

35-
String licenseContent = parser.get()
35+
String licenseContent = parser.getText()
3636

3737
if (!outputDir.exists()) {
3838
downloadsDir.mkdirs()
3939
}
4040

4141
//We use the URI string's hashcode as the filename to store the download
4242
//This is safe since java String 's hashcode generation algorithm is part of the api spec
43-
File licenseFile = new File(outputDir, "${parser.getServiceUri().toString().hashCode()}")
43+
File licenseFile = new File(outputDir, "${parser.getRemoteUrl().toString().hashCode()}")
4444
licenseFile.write licenseContent
4545
}
4646
}
Lines changed: 92 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright 2018 Google LLC
22
//
3-
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// Licensed under the Apache License, Version 2.0 (the "License")
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
66
//
@@ -14,155 +14,160 @@
1414

1515
package com.google.firebase.gradle.plugins.license
1616

17+
import java.io.IOException
1718
import org.jsoup.Jsoup
1819
import org.jsoup.examples.HtmlToPlainText
20+
import org.jsoup.nodes.Document
1921

2022
/**
2123
* Parse licenses from remote urls*/
22-
interface RemoteLicenseFetcher extends Serializable {
23-
static final TEXT_FORMATTER = new HtmlToPlainText()
24+
class RemoteLicenseFetcher implements Serializable {
25+
private static final HtmlToPlainText TEXT_FORMATTER = new HtmlToPlainText()
2426

25-
URI getServiceUri()
27+
private final String remoteUrl
2628

27-
String get()
28-
29-
static final class AndroidSdkTermsFetcher implements RemoteLicenseFetcher {
30-
private URI ANDROID_SDK_TERMS_URI = URI.create("https://developer.android.com/studio/terms.html")
29+
protected RemoteLicenseFetcher(String remoteUrl) {
30+
this.remoteUrl = remoteUrl
31+
}
3132

32-
@Override
33-
URI getServiceUri() {
34-
ANDROID_SDK_TERMS_URI
35-
}
33+
public final String getRemoteUrl() {
34+
return remoteUrl
35+
}
3636

37-
@Override
38-
String get() {
39-
def doc = Jsoup.connect(ANDROID_SDK_TERMS_URI.toString()).get()
37+
/**
38+
* Downloads and returns the text of the license.
39+
*
40+
* <p>This method will try at most three times to download the license. Failures will be silently
41+
* ignored if the license can be successfully downloaded on the second or third attempt. This
42+
* method performs a simple, linear backoff in the case of failures to improve chances of
43+
* successful connections.
44+
*/
45+
public final String getText() {
46+
IOException storedEx = null
47+
48+
for (int i = 0; i < 3; ++i) {
49+
try {
50+
if (i > 0) {
51+
Thread.sleep(i * 1000)
52+
}
53+
54+
return processDocument(Jsoup.connect(remoteUrl).get())
55+
} catch (IOException ex) {
56+
if (storedEx == null) {
57+
storedEx = ex
58+
} else {
59+
storedEx.addSuppressed(ex)
60+
}
61+
}
62+
}
63+
64+
throw storedEx
65+
}
4066

41-
TEXT_FORMATTER.getPlainText(doc.select('#body-content > div.jd-descr > div')[0])
42-
}
67+
/** Extracts the license text from the rest of the document. */
68+
String processDocument(Document doc) {
69+
return TEXT_FORMATTER.getPlainText(doc)
4370
}
4471

45-
static final class Apache2LicenseFetcher implements RemoteLicenseFetcher {
46-
private URI APACHE_2_LICENSE_URI = URI.create("http://www.apache.org/licenses/LICENSE-2.0.txt")
72+
static final class AndroidSdkTermsFetcher extends RemoteLicenseFetcher {
4773

48-
@Override
49-
URI getServiceUri() {
50-
APACHE_2_LICENSE_URI
74+
AndroidSdkTermsFetcher() {
75+
super("https://developer.android.com/studio/terms.html")
5176
}
5277

5378
@Override
54-
String get() {
55-
APACHE_2_LICENSE_URI.toURL().getText()
79+
String processDocument(Document doc) {
80+
// TODO(vkryachko, allisonbm92): Fix this silent failure.
81+
// This evaluates to an empty string. The HTML for this page must have changed since this
82+
// filter was original written. Interestingly, this is a hard-failure if run from Java.
83+
return TEXT_FORMATTER.getPlainText(doc.select("#body-content > div.jd-descr > div")[0])
5684
}
5785
}
5886

59-
static final class AnotherApache2LicenseFetcher implements RemoteLicenseFetcher {
60-
private URI APACHE_2_LICENSE_URI = URI.create("https://opensource.org/licenses/Apache-2.0")
87+
static final class Apache2LicenseFetcher extends RemoteLicenseFetcher {
6188

62-
@Override
63-
URI getServiceUri() {
64-
APACHE_2_LICENSE_URI
89+
Apache2LicenseFetcher() {
90+
super("http://www.apache.org/licenses/LICENSE-2.0.txt")
6591
}
92+
}
6693

67-
@Override
68-
String get() {
69-
def doc = Jsoup.connect(APACHE_2_LICENSE_URI.toString()).get()
94+
static final class AnotherApache2LicenseFetcher extends RemoteLicenseFetcher {
7095

71-
TEXT_FORMATTER.getPlainText(doc.select('#content-wrapper'))
96+
AnotherApache2LicenseFetcher() {
97+
super("https://opensource.org/licenses/Apache-2.0")
7298
}
73-
}
74-
75-
static final class YetAnotherApache2LicenseFetcher implements RemoteLicenseFetcher {
76-
private URI APACHE_2_LICENSE_URI = URI.create("http://www.apache.org/licenses/LICENSE-2.0")
7799

78100
@Override
79-
URI getServiceUri() {
80-
APACHE_2_LICENSE_URI
101+
String processDocument(Document doc) {
102+
return TEXT_FORMATTER.getPlainText(doc.select("#content-wrapper").get(0))
81103
}
104+
}
82105

83-
@Override
84-
String get() {
85-
APACHE_2_LICENSE_URI.toURL().getText()
106+
static final class YetAnotherApache2LicenseFetcher extends RemoteLicenseFetcher {
107+
108+
YetAnotherApache2LicenseFetcher() {
109+
super("http://www.apache.org/licenses/LICENSE-2.0")
86110
}
87111
}
88112

89-
static final class BSDLicenseFetcher implements RemoteLicenseFetcher {
90-
private URI BSD_LICENSE_URI = URI.create("http://www.opensource.org/licenses/bsd-license.php")
113+
static final class BSDLicenseFetcher extends RemoteLicenseFetcher {
91114

92-
@Override
93-
URI getServiceUri() {
94-
BSD_LICENSE_URI
115+
BSDLicenseFetcher() {
116+
super("http://www.opensource.org/licenses/bsd-license.php")
95117
}
96118

97119
@Override
98-
String get() {
99-
def doc = Jsoup.connect(BSD_LICENSE_URI.toString()).get()
100-
101-
TEXT_FORMATTER.getPlainText(doc.select('#content-wrapper')[0])
120+
String processDocument(Document doc) {
121+
return TEXT_FORMATTER.getPlainText(doc.select("#content-wrapper").get(0))
102122
}
103123
}
104124

105-
static final class CreativeCommonsLicenseFetcher implements RemoteLicenseFetcher {
106-
private URI CREATIVE_COMMONS_LICENSE_URI = URI.create("http://creativecommons.org/publicdomain/zero/1.0/")
125+
static final class CreativeCommonsLicenseFetcher extends RemoteLicenseFetcher {
107126

108-
@Override
109-
URI getServiceUri() {
110-
CREATIVE_COMMONS_LICENSE_URI
127+
CreativeCommonsLicenseFetcher() {
128+
super("http://creativecommons.org/publicdomain/zero/1.0/")
111129
}
112130

113131
@Override
114-
String get() {
115-
def doc = Jsoup.connect(CREATIVE_COMMONS_LICENSE_URI.toString()).get()
116-
117-
TEXT_FORMATTER.getPlainText(doc.select('#deed'))
132+
String processDocument(Document doc) {
133+
return TEXT_FORMATTER.getPlainText(doc.select("#deed").get(0))
118134
}
119135
}
120136

121-
static final class MITLicenseFetcher implements RemoteLicenseFetcher {
122-
private URI MIT_LICENSE_URI = URI.create("http://www.opensource.org/licenses/mit-license.php")
137+
static final class MITLicenseFetcher extends RemoteLicenseFetcher {
123138

124-
@Override
125-
URI getServiceUri() {
126-
MIT_LICENSE_URI
139+
MITLicenseFetcher() {
140+
super("http://www.opensource.org/licenses/mit-license.php")
127141
}
128142

129143
@Override
130-
String get() {
131-
def doc = Jsoup.connect(MIT_LICENSE_URI.toString()).get()
132-
133-
TEXT_FORMATTER.getPlainText(doc.select('#content-wrapper'))
144+
String processDocument(Document doc) {
145+
return TEXT_FORMATTER.getPlainText(doc.select("#content-wrapper").get(0))
134146
}
135147
}
136148

137-
static final class AnotherMITLicenseFetcher implements RemoteLicenseFetcher {
138-
private URI MIT_LICENSE_URI = URI.create("http://opensource.org/licenses/MIT")
149+
static final class AnotherMITLicenseFetcher extends RemoteLicenseFetcher {
139150

140-
@Override
141-
URI getServiceUri() {
142-
MIT_LICENSE_URI
151+
AnotherMITLicenseFetcher() {
152+
super("http://opensource.org/licenses/MIT")
143153
}
144154

145155
@Override
146-
String get() {
147-
def doc = Jsoup.connect(MIT_LICENSE_URI.toString()).get()
148-
149-
TEXT_FORMATTER.getPlainText(doc.select('#content-wrapper'))
156+
String processDocument(Document doc) {
157+
return TEXT_FORMATTER.getPlainText(doc.select("#content-wrapper").get(0))
150158
}
151159
}
152160

153-
static final class GnuClasspathLicenseFetcher implements RemoteLicenseFetcher {
154-
private URI GNU_CLASSPATH_LICENSE_URI = URI.create("http://www.gnu.org/software/classpath/license.html")
161+
static final class GnuClasspathLicenseFetcher extends RemoteLicenseFetcher {
155162

156-
@Override
157-
URI getServiceUri() {
158-
GNU_CLASSPATH_LICENSE_URI
163+
// TODO(allisonbm92, vkryachko): Fetch the actual license. This only fetches the extension.
164+
GnuClasspathLicenseFetcher() {
165+
super("http://www.gnu.org/software/classpath/license.html")
159166
}
160167

161168
@Override
162-
String get() {
163-
def doc = Jsoup.connect(GNU_CLASSPATH_LICENSE_URI.toString()).get()
164-
165-
TEXT_FORMATTER.getPlainText(doc.select('body > table > tbody > tr:nth-child(2) > td:nth-child(2) > table > tbody > tr:nth-child(3) > td > en > blockquote'))
169+
String processDocument(Document doc) {
170+
return TEXT_FORMATTER.getPlainText(doc.select("body > table > tbody > tr:nth-child(2) > td:nth-child(2) > table > tbody > tr:nth-child(3) > td > en > blockquote").get(0))
166171
}
167172
}
168173
}

0 commit comments

Comments
 (0)