blob: 9b3eb3e50deaaa0a2645a90f79c934b5775363f2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.meecrowave.doc.generator;
import org.apache.commons.lang3.text.WordUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static java.util.Arrays.asList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
// helper to generate the download table content
public class Downloads {
private static final SAXParserFactory FACTORY = SAXParserFactory.newInstance();
private static final String MVN_BASE = "http://repo.maven.apache.org/maven2/";
private static final long MEGA_RATIO = 1024 * 1024;
private static final long KILO_RATIO = 1024;
static {
FACTORY.setNamespaceAware(false);
FACTORY.setValidating(false);
}
private Downloads() {
// no-op
}
public static void main(final String[] args) {
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "32");
Stream.of(
Stream.of("org/apache/meecrowave/meecrowave")
.flatMap(Downloads::toVersions)
.map(v -> v.extensions("zip"))
.map(v -> v.classifiers("source-release")),
versionStream("meecrowave-core")
.map(v -> v.classifiers("", "runner"))
.map(v -> v.extensions("jar")))
.flatMap(s -> s)
.flatMap(Downloads::toDownloadable)
.parallel()
.map(Downloads::fillDownloadable)
.filter(Objects::nonNull)
.sorted((o1, o2) -> {
final int versionComp = o2.version.compareTo(o1.version);
if (versionComp != 0) {
if (o2.version.startsWith(o1.version) && o2.version.contains("-M")) { // milestone
return -1;
}
if (o1.version.startsWith(o2.version) && o1.version.contains("-M")) { // milestone
return 1;
}
return versionComp;
}
final int nameComp = o1.name.compareTo(o2.name);
if (nameComp != 0) {
return nameComp;
}
final long dateComp = LocalDateTime.parse(o2.date, RFC_1123_DATE_TIME).toInstant(ZoneOffset.UTC).toEpochMilli()
- LocalDateTime.parse(o1.date, RFC_1123_DATE_TIME).toInstant(ZoneOffset.UTC).toEpochMilli();
if (dateComp != 0) {
return (int) dateComp;
}
return o1.url.compareTo(o2.url);
})
.collect(toList())
.forEach(d ->
System.out.println("" +
"|" + d.name + (d.classifier.isEmpty() ? "" : (" " + d.classifier)).replace("source-release", "Source Release") +
"|" + d.version +
"|" + d.date +
"|" + d.size +
"|" + d.format +
"| " + d.url + "[icon:download[] " + d.format + "] " + d.sha1 + "[icon:download[] sha1] " + d.md5 + "[icon:download[] md5] " + d.asc + "[icon:download[] asc]"));
}
private static Download fillDownloadable(final Download download) {
try {
final URL url = new URL(download.url);
final HttpURLConnection connection = HttpURLConnection.class.cast(url.openConnection());
connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(30));
final int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
if (HttpURLConnection.HTTP_NOT_FOUND != responseCode) {
System.err.println("Got " + responseCode + " for " + download.url);
}
return null;
}
download.date = connection.getHeaderField("Last-Modified").replaceAll(" +", " ");
download.size = toSize(ofNullable(connection.getHeaderField("Content-Length"))
.map(Long::parseLong).orElse(0L), ofNullable(connection.getHeaderField("Accept-Ranges")).orElse("bytes"));
connection.getInputStream().close();
} catch (final IOException e) {
e.printStackTrace();
return null;
}
return download;
}
private static String toSize(final long length, final String bytes) {
if (!"bytes".equalsIgnoreCase(bytes)) {
throw new IllegalArgumentException("Not handled unit: " + bytes);
}
final long meg = length / MEGA_RATIO;
final long kilo = (length - (meg * MEGA_RATIO)) / KILO_RATIO;
return (meg > 0 ? meg + " MB " : "") + (kilo > 0 ? kilo + " kB" : "");
}
private static Stream<Version> versionStream(final String artifactId) {
return Stream.of("org/apache/meecrowave/" + artifactId)
.flatMap(Downloads::toVersions);
}
private static Stream<Download> toDownloadable(final Version version) {
final String base = version.base;
final String artifactId = base.substring(base.lastIndexOf('/') + 1, base.length());
final String artifactBase = version.base + "/" + version.version + "/" + artifactId + "-" + version.version;
return version.extensions.stream()
.flatMap(e -> (version.classifiers.isEmpty() ? Stream.of(new ArtifactDescription("", e)) : version.classifiers.stream().map(c -> new ArtifactDescription(c, e))))
.map(a -> toDownload(artifactId, a.classifier, version.version, a.extension, artifactBase + (a.classifier.isEmpty() ? '.' + a.extension : ('-' + a.classifier + '.' + a.extension))));
}
private static Download toDownload(final String artifactId, final String classifier, final String version, final String format, final String url) {
return new Download(
WordUtils.capitalize(artifactId.replace('-', ' ')),
classifier,
version,
format,
url,
url + ".md5",
url + ".sha1",
url + ".asc");
}
private static Stream<Version> toVersions(final String baseUrl) {
final QuickMvnMetadataParser handler = new QuickMvnMetadataParser();
final String base = MVN_BASE + baseUrl;
try (final InputStream stream = new URL(base + "/maven-metadata.xml").openStream()) {
final SAXParser parser = FACTORY.newSAXParser();
parser.parse(stream, handler);
return handler.foundVersions.stream().map(v -> new Version(base, v)).parallel();
} catch (final Exception e) {
e.printStackTrace();
return Stream.empty();
}
}
public static class Version {
private final String base;
private final String version;
private final Collection<String> classifiers = new ArrayList<>();
private final Collection<String> extensions = new ArrayList<>();
public Version(final String base, final String version) {
this.base = base;
this.version = version;
}
private Version extensions(final String... values) {
extensions.addAll(asList(values));
return this;
}
private Version classifiers(final String... values) {
classifiers.addAll(asList(values));
return this;
}
}
public static class ArtifactDescription {
private final String classifier;
private final String extension;
private ArtifactDescription(final String classifier, final String extension) {
this.classifier = classifier;
this.extension = extension;
}
}
public static class Download {
private final String name;
private final String classifier;
private final String version;
private final String format;
private final String url;
private final String md5;
private final String sha1;
private final String asc;
private String date;
private String size;
public Download(final String name, final String classifier, final String version,
final String format, final String url, final String md5, final String sha1, final String asc) {
this.name = name;
this.classifier = classifier;
this.version = version;
this.format = format;
this.url = url;
this.md5 = md5;
this.sha1 = sha1;
this.asc = asc;
}
}
private static class QuickMvnMetadataParser extends DefaultHandler {
private boolean versioning = false;
private boolean versions = false;
private StringBuilder version;
private final Collection<String> foundVersions = new ArrayList<>();
@Override
public void startElement(final String uri, final String localName,
final String name, final Attributes attributes) throws SAXException {
if ("versioning".equalsIgnoreCase(name)) {
versioning = true;
} else if ("versions".equalsIgnoreCase(name)) {
versions = true;
} else if (versioning && versions && "version".equalsIgnoreCase(name)) {
version = new StringBuilder();
}
}
@Override
public void characters(final char[] ch, final int start, final int length) throws SAXException {
if (version != null) {
version.append(new String(ch, start, length));
}
}
public void endElement(final String uri, final String localName, final String name) throws SAXException {
if ("versioning".equalsIgnoreCase(name)) {
versioning = false;
} else if ("versions".equalsIgnoreCase(name)) {
versions = false;
} else if ("version".equalsIgnoreCase(name)) {
foundVersions.add(version.toString());
}
}
}
}