|
4 | 4 | import java.nio.file.Files; |
5 | 5 | import java.nio.file.Path; |
6 | 6 | import java.text.MessageFormat; |
| 7 | +import java.util.List; |
7 | 8 | import java.util.NoSuchElementException; |
8 | 9 | import java.util.Optional; |
| 10 | +import java.util.stream.Collectors; |
| 11 | + |
9 | 12 | import org.apache.commons.collections.CollectionUtils; |
10 | 13 | import org.apache.commons.lang3.Validate; |
11 | 14 | import org.eclipse.aether.artifact.Artifact; |
|
25 | 28 | public class SdkDownloader { |
26 | 29 | private static final Logger logger = LoggerFactory.getLogger(SdkDownloader.class); |
27 | 30 |
|
28 | | - private SdkDownloader() {} |
| 31 | + private SdkDownloader() { |
| 32 | + } |
29 | 33 |
|
30 | 34 | /** |
31 | | - * Downloads a SDK version from Maven Central (or local Maven repository) and unpacks it under the |
32 | | - * given root directory. - If a major version is requested (e.g., "1"), then the latest |
33 | | - * minor/patch will be fetched and will be stored under a directory |
34 | | - * "<root_directory>/<minor>". - If a minor version is requested (e.g., "1.1"), then |
35 | | - * the latest patch for this minor will be fetched and will be stored under a directory |
36 | | - * "<root_directory>/<minor>" - If a patch is request (e.g., "1.1.2"), then that patch |
37 | | - * will be fetched and will be stored under a directory |
38 | | - * "<root_directory>/<minor>/<patch>". If the requested patch is not found, then |
39 | | - * an {@link IllegalArgumentException} will be thrown. |
| 35 | + * Downloads a SDK version from Maven Central (or local Maven repository) and |
| 36 | + * unpacks it in a subfolder under the given root directory. |
| 37 | + * |
| 38 | + * If the given version is not specific enough (e.g. 1.0), its latest |
| 39 | + * patch version will be downloaded. |
40 | 40 | * |
41 | | - * @param sdkVersion The target SDK version (<major>.<minor>.<patch>) |
42 | | - * @param rootDir The root directory |
43 | | - * @throws IOException if the download fails |
| 41 | + * @param sdkVersion The SDK version to download. |
| 42 | + * @param rootDir The root directory where the SDK will be downloaded |
| 43 | + * @param includeSnapshots If true, the latest snapshot version will be |
| 44 | + * downloaded if the given version is not found |
| 45 | + * @throws IOException If the download fails. |
44 | 46 | */ |
45 | | - public static void downloadSdk(final SdkVersion sdkVersion, final Path rootDir) |
| 47 | + public static void downloadSdk(final SdkVersion sdkVersion, final Path rootDir, boolean includeSnapshots) |
46 | 48 | throws IOException { |
47 | | - Path sdkDir = |
48 | | - Path.of(Optional.ofNullable(rootDir).orElse(SdkConstants.DEFAULT_SDK_ROOT).toString(), |
49 | | - sdkVersion.isPatch() ? sdkVersion.toString() : sdkVersion.toStringWithoutPatch()); |
| 49 | + Path sdkDir = Path.of(Optional.ofNullable(rootDir).orElse(SdkConstants.DEFAULT_SDK_ROOT).toString(), |
| 50 | + sdkVersion.isPatch() ? sdkVersion.toString() : sdkVersion.toStringWithoutPatch()); |
50 | 51 |
|
51 | 52 | try { |
52 | | - SdkVersion artifactVersion = getLatestSdkVersion(sdkVersion); |
| 53 | + SdkVersion artifactVersion = getLatestSdkVersion(sdkVersion, includeSnapshots); |
53 | 54 |
|
54 | 55 | if (sdkExistsAt(artifactVersion, sdkDir)) { |
55 | 56 | logger.debug("SDK [{}] found at [{}]. No download required.", artifactVersion, sdkDir); |
@@ -77,16 +78,88 @@ public static void downloadSdk(final SdkVersion sdkVersion, final Path rootDir) |
77 | 78 | } |
78 | 79 | } |
79 | 80 |
|
| 81 | + /** |
| 82 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)} that excludes snapshot versions. |
| 83 | + * |
| 84 | + * @param sdkVersion The SDK version to download. |
| 85 | + * @param rootDir The root directory where the SDK will be downloaded. |
| 86 | + * @throws IOException If the download fails. |
| 87 | + */ |
| 88 | + public static void downloadSdk(final SdkVersion sdkVersion, final Path rootDir) |
| 89 | + throws IOException { |
| 90 | + downloadSdk(sdkVersion, rootDir, false); |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 95 | + * |
| 96 | + * @param sdkVersion The SDK version to download. |
| 97 | + * @throws IOException If the download fails. |
| 98 | + */ |
80 | 99 | public static void downloadSdk(final String sdkVersion) throws IOException { |
81 | | - downloadSdk(sdkVersion, SdkConstants.DEFAULT_SDK_ROOT); |
| 100 | + downloadSdk(sdkVersion, false); |
82 | 101 | } |
83 | 102 |
|
| 103 | + /** |
| 104 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 105 | + * |
| 106 | + * @param sdkVersion The SDK version to download. |
| 107 | + * @param includeSnapshots If true, then SNAPSHOT versions will be downloaded if |
| 108 | + * no other version is available. |
| 109 | + * @throws IOException If the download fails. |
| 110 | + */ |
| 111 | + public static void downloadSdk(final String sdkVersion, boolean includeSnapshots) throws IOException { |
| 112 | + downloadSdk(sdkVersion, SdkConstants.DEFAULT_SDK_ROOT, includeSnapshots); |
| 113 | + } |
| 114 | + |
| 115 | + /** |
| 116 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 117 | + * |
| 118 | + * @param sdkVersion The SDK version to download. |
| 119 | + * @throws IOException If the download fails. |
| 120 | + */ |
84 | 121 | public static void downloadSdk(final SdkVersion sdkVersion) throws IOException { |
85 | | - downloadSdk(sdkVersion, SdkConstants.DEFAULT_SDK_ROOT); |
| 122 | + downloadSdk(sdkVersion, false); |
86 | 123 | } |
87 | 124 |
|
| 125 | + /** |
| 126 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 127 | + * |
| 128 | + * @param sdkVersion The SDK version to download. |
| 129 | + * @param includeSnapshots If true, then SNAPSHOT versions will be downloaded if |
| 130 | + * no other version is available. |
| 131 | + * @throws IOException If the download fails. |
| 132 | + */ |
| 133 | + public static void downloadSdk(final SdkVersion sdkVersion, boolean includeSnapshots) throws IOException { |
| 134 | + downloadSdk(sdkVersion, SdkConstants.DEFAULT_SDK_ROOT, includeSnapshots); |
| 135 | + } |
| 136 | + |
| 137 | + /** |
| 138 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 139 | + * |
| 140 | + * @param sdkVersion The SDK version to download. |
| 141 | + * @param rootDir The root directory to download the SDK to. |
| 142 | + * @throws IOException If the download fails. |
| 143 | + */ |
88 | 144 | public static void downloadSdk(final String sdkVersion, final Path rootDir) throws IOException { |
89 | | - downloadSdk(new SdkVersion(sdkVersion), rootDir); |
| 145 | + downloadSdk(sdkVersion, rootDir, false); |
| 146 | + } |
| 147 | + |
| 148 | + /** |
| 149 | + * Overload of {@link #downloadSdk(SdkVersion, Path, boolean)}. |
| 150 | + * |
| 151 | + * If the version is not specific enough (i.e. does not include a patch |
| 152 | + * version), then the latest available version will be downloaded. |
| 153 | + * |
| 154 | + * @param sdkVersion The SDK version to download. |
| 155 | + * @param rootDir The root directory to download the SDK to. |
| 156 | + * @param includeSnapshots If true, then SNAPSHOT versions will be downloaded if |
| 157 | + * no other version is available. |
| 158 | + * @throws IOException If the download fails. |
| 159 | + */ |
| 160 | + public static void downloadSdk(final String sdkVersion, final Path rootDir, boolean includeSnapshots) |
| 161 | + throws IOException { |
| 162 | + downloadSdk(new SdkVersion(sdkVersion), rootDir, includeSnapshots); |
90 | 163 | } |
91 | 164 |
|
92 | 165 | private static Path createVersionFile(final SdkVersion sdkVersion, final Path sdkDir) |
@@ -119,70 +192,92 @@ private static boolean sdkExistsAt(final SdkVersion sdkVersion, final Path sdkDi |
119 | 192 | } |
120 | 193 |
|
121 | 194 | /** |
122 | | - * Discovers the latest available version for a given base version. If the major of the base |
123 | | - * version is zero, then the second digit is regarded as major. E.g.: - For base version 0.6, the |
124 | | - * result is the largest found version number between 0.6 and 0.7 - For base version 1.0, the |
125 | | - * result is the largest found version number between 1.0 and 1.1 |
| 195 | + * Discovers the latest available version for a given base version. |
126 | 196 | * |
127 | | - * @param sdkVersion The base version |
128 | | - * @return |
| 197 | + * @param sdkVersion The base version to look for. |
| 198 | + * @param includeSnapshots Whether to resolve SNAPSHOT versions if needed (e.g., |
| 199 | + * 1.0.0-SNAPSHOT) |
| 200 | + * @return The latest available version. |
129 | 201 | */ |
130 | | - private static SdkVersion getLatestSdkVersion(final SdkVersion baseVersion) |
| 202 | + private static SdkVersion getLatestSdkVersion(final SdkVersion baseVersion, final boolean includeSnapshots) |
131 | 203 | throws VersionRangeResolutionException { |
132 | 204 | Validate.notNull(baseVersion, "Undefined base version"); |
133 | 205 |
|
134 | | - String minVersion = baseVersion.toString(); |
135 | | - String maxVersion = baseVersion.getNextMinor(); |
136 | | - String searchPattern = "[{0},{1}-SNAPSHOT)"; |
137 | | - |
138 | | - if (baseVersion.isPatch()) { |
139 | | - minVersion = maxVersion = baseVersion.toString(); |
140 | | - searchPattern = "[{0},{1}]"; |
| 206 | + try { |
| 207 | + return getHighestVersion(resolveVersionRange(getSearchPattern(baseVersion)), includeSnapshots); |
| 208 | + } catch (NoSuchElementException e1) { |
| 209 | + throw new IllegalArgumentException( |
| 210 | + MessageFormat.format("No artifacts found for SDK {0}.", baseVersion)); |
141 | 211 | } |
| 212 | + } |
142 | 213 |
|
143 | | - Artifact artifact = new DefaultArtifact(SdkConstants.SDK_GROUP_ID, SdkConstants.SDK_ARTIFACT_ID, |
144 | | - "jar", MessageFormat.format(searchPattern, minVersion, maxVersion)); |
| 214 | + /** |
| 215 | + * Defines the search pattern for release versions of a given SDK version. |
| 216 | + * |
| 217 | + * @param sdkVersion The base version to construct the search pattern for. |
| 218 | + * @return A search pattern for release versions of the given base version. |
| 219 | + */ |
| 220 | + private static String getSearchPattern(SdkVersion sdkVersion) { |
| 221 | + if (sdkVersion.isPatch()) { |
| 222 | + return MessageFormat.format("[{0}]", sdkVersion.toString()); |
| 223 | + } |
| 224 | + return MessageFormat.format("[{0}.*]", sdkVersion.toString()); |
| 225 | + } |
145 | 226 |
|
146 | | - VersionRangeResult versions = MavenBooter.resolveVersionRange(artifact); |
147 | | - try { |
148 | | - /** |
149 | | - * If the major version is "0", Maven will return all the versions up to the next major. In |
150 | | - * this case we need to filter out all the discovered versions where the minor is not the same |
151 | | - * as that of the base version. |
152 | | - */ |
153 | | - if (baseVersion.getMajor().equals("0")) { |
154 | | - return versions.getVersions().stream() |
155 | | - .map(Object::toString) |
156 | | - .map(SdkVersion::new) |
157 | | - .filter((SdkVersion v) -> v.getMajor().equals(baseVersion.getMajor()) |
158 | | - && v.getMinor().equals(baseVersion.getMinor())) |
159 | | - .max(Comparable::compareTo) |
160 | | - .orElseThrow(); |
161 | | - } else { |
162 | | - Version highestVersion = versions.getHighestVersion(); |
| 227 | + /** |
| 228 | + * Uses Maven to resolve a version range from a given search pattern. |
| 229 | + * |
| 230 | + * @param searchPattern The search pattern to resolve. |
| 231 | + * @return A version range result. |
| 232 | + * @throws VersionRangeResolutionException If the version range could not be |
| 233 | + * resolved. |
| 234 | + */ |
| 235 | + private static VersionRangeResult resolveVersionRange(String searchPattern) throws VersionRangeResolutionException { |
| 236 | + Artifact artifact = new DefaultArtifact(SdkConstants.SDK_GROUP_ID, SdkConstants.SDK_ARTIFACT_ID, "jar", |
| 237 | + searchPattern); |
| 238 | + return MavenBooter.resolveVersionRange(artifact); |
| 239 | + } |
163 | 240 |
|
164 | | - if (highestVersion == null) { |
165 | | - throw new NoSuchElementException(); |
166 | | - } |
| 241 | + /** |
| 242 | + * Gets the highest version from a version range. |
| 243 | + * A pre-release version will be returned only if there is no released version in the range. |
| 244 | + * |
| 245 | + * @param versionRange The version range to search. |
| 246 | + * @param includeSnapshots Whether to include SNAPSHOT versions. |
| 247 | + * @return The highest version found. |
| 248 | + * @throws NoSuchElementException If the version range is empty. |
| 249 | + */ |
| 250 | + private static SdkVersion getHighestVersion(VersionRangeResult versionRange, boolean includeSnapshots) throws NoSuchElementException { |
| 251 | + |
| 252 | + List<Version> rangeVersions = versionRange.getVersions(); |
167 | 253 |
|
168 | | - return new SdkVersion(highestVersion.toString()); |
169 | | - } |
170 | | - } catch (NoSuchElementException e) { |
171 | | - String snapshotVersion = MessageFormat.format("{0}.{1}.{2}-SNAPSHOT", baseVersion.getMajor(), baseVersion.getMinor(), baseVersion.getPatch()); |
172 | | - logger.warn("No artifacts were found for SDK version [{}]. Trying with [{}]", baseVersion, |
173 | | - snapshotVersion); |
| 254 | + if (CollectionUtils.isEmpty(rangeVersions)) { |
| 255 | + throw new NoSuchElementException(); |
| 256 | + } |
174 | 257 |
|
175 | | - artifact = new DefaultArtifact(MessageFormat.format("{0}:{1}:{2}", SdkConstants.SDK_GROUP_ID, |
176 | | - SdkConstants.SDK_ARTIFACT_ID, snapshotVersion)); |
| 258 | + // Get all the versions in the range into a list of SdkVersion objects. |
| 259 | + // Exclude snapshots if requested. |
| 260 | + List<SdkVersion> allVersions = rangeVersions.stream() |
| 261 | + .map(Object::toString) |
| 262 | + .map(SdkVersion::new) |
| 263 | + .filter(v -> includeSnapshots || !v.isSnapshot()) |
| 264 | + .collect(Collectors.toList()); |
177 | 265 |
|
178 | | - versions = MavenBooter.resolveVersionRange(artifact); |
| 266 | + // Get all the release versions into a separate list. |
| 267 | + // Make sure to include snapshots if requested. |
| 268 | + List<SdkVersion> releaseVersions = allVersions.stream() |
| 269 | + .filter(v -> !v.isPreRelease() || (v.isSnapshot() && includeSnapshots)) |
| 270 | + .collect(Collectors.toList()); |
179 | 271 |
|
180 | | - if (CollectionUtils.isEmpty(versions.getVersions())) { |
181 | | - throw new IllegalArgumentException( |
182 | | - MessageFormat.format("No artifacts were found for SDK version [{0}]", snapshotVersion)); |
183 | | - } |
| 272 | + // We will only consider pre-release versions if there are no release versions. |
| 273 | + List<SdkVersion> eligibleVersions = CollectionUtils.isEmpty(releaseVersions) ? allVersions : releaseVersions; |
184 | 274 |
|
185 | | - return new SdkVersion(versions.getVersions().get(0).toString()); |
| 275 | + // If there are no eligible versions, then throw an exception. |
| 276 | + if (CollectionUtils.isEmpty(eligibleVersions)) { |
| 277 | + throw new NoSuchElementException(); |
186 | 278 | } |
| 279 | + |
| 280 | + // We can simply return the last version from the list, as Maven sorts versions as we need. |
| 281 | + return eligibleVersions.get(eligibleVersions.size() -1); |
187 | 282 | } |
188 | 283 | } |
0 commit comments