|
18 | 18 | */ |
19 | 19 | package co.elastic.apm.attach; |
20 | 20 |
|
| 21 | +import co.elastic.apm.agent.util.Version; |
21 | 22 | import org.apache.logging.log4j.LogManager; |
22 | 23 | import org.apache.logging.log4j.Logger; |
23 | 24 |
|
| 25 | +import java.io.BufferedReader; |
24 | 26 | import java.io.IOException; |
25 | 27 | import java.io.InputStream; |
| 28 | +import java.io.InputStreamReader; |
26 | 29 | import java.net.HttpURLConnection; |
27 | 30 | import java.net.URL; |
28 | 31 | import java.nio.file.Files; |
29 | 32 | import java.nio.file.Path; |
30 | 33 | import java.util.Locale; |
31 | 34 | import java.util.Properties; |
| 35 | +import java.util.TreeSet; |
| 36 | +import java.util.regex.Matcher; |
| 37 | +import java.util.regex.Pattern; |
32 | 38 |
|
33 | 39 | /** |
34 | 40 | * A utility for downloading any given version of the Elastic APM Java agent from maven central. |
|
37 | 43 | */ |
38 | 44 | public class AgentDownloader { |
39 | 45 |
|
| 46 | + private static final String VERSION_EXTRACTION_REGEX = "<a href.+>([0-9]+.[0-9.]+.[0-9.]+)/</a>"; |
40 | 47 | private static final String AGENT_GROUP_ID = "co.elastic.apm"; |
41 | 48 | private static final String AGENT_ARTIFACT_ID = "elastic-apm-agent"; |
42 | 49 | private static final String CLI_JAR_VERSION; |
@@ -90,7 +97,7 @@ public AgentDownloader(PgpSignatureVerifier pgpSignatureVerifier) { |
90 | 97 | */ |
91 | 98 | Path downloadAndVerifyAgent(String agentVersion) throws Exception { |
92 | 99 | logger.debug("Requested to download Elastic APM Java agent version {}", agentVersion); |
93 | | - final String mavenAgentBaseUrl = getAgentMavenBaseUrl(agentVersion); |
| 100 | + final String mavenAgentBaseUrl = getAgentMavenVersionBaseUrl(agentVersion); |
94 | 101 | Path targetDir = AgentDownloadUtils.of(agentVersion).getTargetAgentDir(); |
95 | 102 | String agentJarName = computeAgentJarName(agentVersion); |
96 | 103 | String mavenAgentJarUrl = computeFileUrl(mavenAgentBaseUrl, agentJarName); |
@@ -133,28 +140,39 @@ String computeAgentJarName(String agentVersion) { |
133 | 140 | /** |
134 | 141 | * Returns the url for an Elastic APM Agent jar in maven. |
135 | 142 | */ |
136 | | - String getAgentMavenBaseUrl(String agentVersion) throws Exception { |
137 | | - final String groupId = AGENT_GROUP_ID.replace(".", "/"); |
138 | | - final String agentMavenUrl = String.format("https://repo1.maven.org/maven2/%s/%s/%s", groupId, AGENT_ARTIFACT_ID, agentVersion); |
| 143 | + String getAgentMavenVersionBaseUrl(String agentVersion) throws Exception { |
| 144 | + final String agentMavenUrl = String.format("%s/%s", getAgentArtifactMavenBaseUrl(), agentVersion); |
139 | 145 | if (!verifyUrl(agentMavenUrl)) { |
140 | 146 | throw new IllegalArgumentException(String.format("Cannot find maven URL for version %s, make sure provided version is valid", agentVersion)); |
141 | 147 | } |
142 | 148 | return agentMavenUrl; |
143 | 149 | } |
144 | 150 |
|
| 151 | + /** |
| 152 | + * Returns the url for the Elastic APM Agent jar artifact in maven. |
| 153 | + */ |
| 154 | + static String getAgentArtifactMavenBaseUrl() throws Exception { |
| 155 | + final String groupId = AGENT_GROUP_ID.replace(".", "/"); |
| 156 | + final String agentMavenUrl = String.format("https://repo1.maven.org/maven2/%s/%s", groupId, AGENT_ARTIFACT_ID); |
| 157 | + if (!verifyUrl(agentMavenUrl)) { |
| 158 | + throw new IllegalArgumentException(String.format("Cannot find maven URL for agent artifact: %s:%s", groupId, AGENT_ARTIFACT_ID)); |
| 159 | + } |
| 160 | + return agentMavenUrl; |
| 161 | + } |
| 162 | + |
145 | 163 | /** |
146 | 164 | * Returns {@code true} if the given url exists, and {@code false} otherwise. |
147 | 165 | * <p> |
148 | 166 | * The given url must be {@code https} and existing means a {@code HEAD} request returns 200. |
149 | 167 | */ |
150 | | - private boolean verifyUrl(String urlString) throws IOException { |
| 168 | + private static boolean verifyUrl(String urlString) throws IOException { |
151 | 169 | HttpURLConnection urlConnection = openConnection(urlString); |
152 | 170 | urlConnection.setRequestMethod("HEAD"); |
153 | 171 | urlConnection.connect(); |
154 | 172 | return urlConnection.getResponseCode() == 200; |
155 | 173 | } |
156 | 174 |
|
157 | | - private HttpURLConnection openConnection(String urlString) throws IOException { |
| 175 | + private static HttpURLConnection openConnection(String urlString) throws IOException { |
158 | 176 | URL url = new URL(urlString); |
159 | 177 | HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); |
160 | 178 | urlConnection.addRequestProperty("User-Agent", USER_AGENT); |
@@ -201,6 +219,34 @@ void verifyPgpSignature(final Path agentJarFile, final String mavenAgentUrlStrin |
201 | 219 | logger.info("Elastic APM Java Agent jar PGP signature successfully verified."); |
202 | 220 | } |
203 | 221 |
|
| 222 | + static String findLatestVersion() throws Exception { |
| 223 | + String agentArtifactMavenBaseUrl = getAgentArtifactMavenBaseUrl(); |
| 224 | + HttpURLConnection httpURLConnection = openConnection(agentArtifactMavenBaseUrl); |
| 225 | + TreeSet<Version> versions = parseMavenArtifactHtml(httpURLConnection.getInputStream()); |
| 226 | + if (versions.isEmpty()) { |
| 227 | + throw new IllegalStateException("Failed to parse agent versions from the contents of " + agentArtifactMavenBaseUrl); |
| 228 | + } |
| 229 | + return versions.last().toString(); |
| 230 | + } |
| 231 | + |
| 232 | + static TreeSet<Version> parseMavenArtifactHtml(InputStream htmlInputStream) throws IOException { |
| 233 | + TreeSet<Version> versions = new TreeSet<>(); |
| 234 | + BufferedReader versionsHtmlReader = new BufferedReader(new InputStreamReader(htmlInputStream)); |
| 235 | + Pattern pattern = Pattern.compile(VERSION_EXTRACTION_REGEX); |
| 236 | + String line; |
| 237 | + while ((line = versionsHtmlReader.readLine()) != null) { |
| 238 | + try { |
| 239 | + Matcher matcher = pattern.matcher(line); |
| 240 | + if (matcher.find()) { |
| 241 | + versions.add(Version.of(matcher.group(1))); |
| 242 | + } |
| 243 | + } catch (Exception e) { |
| 244 | + // ignore, probably a regex false positive |
| 245 | + } |
| 246 | + } |
| 247 | + return versions; |
| 248 | + } |
| 249 | + |
204 | 250 | /** |
205 | 251 | * Return the public key ID of our agent signing key. |
206 | 252 | * |
|
0 commit comments