package org.apereo.cas.util.http;
import com.github.axet.wget.SpeedInfo;
import com.github.axet.wget.WGet;
import com.github.axet.wget.info.DownloadInfo;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Abstraction for a message that is sent to an http endpoint.
*
* @author Misagh Moayyed
* @since 5.1.0
*/
public class HttpClientMultithreadedDownloader {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientMultithreadedDownloader.class);
private final Resource resourceToDownload;
private final File targetDestination;
public HttpClientMultithreadedDownloader(final Resource resourceToDownload, final File targetDestination) {
this.resourceToDownload = resourceToDownload;
this.targetDestination = targetDestination;
}
/**
* Download.
*/
public void download() {
final AtomicBoolean stop = new AtomicBoolean(false);
try {
final DownloadInfo info = new DownloadInfo(resourceToDownload.getURL());
final DownloadStatusListener status = new DownloadStatusListener(info);
// extract information from the web
info.extract(stop, status);
// enable multipart download
info.enableMultipart();
// create downloader
final WGet w = new WGet(info, this.targetDestination);
// init speed info
status.speedInfo.start(0);
// will blocks until download finishes
LOGGER.info("Starting to download resource [{}] into [{}]", this.resourceToDownload, targetDestination);
w.download(stop, status);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static class DownloadStatusListener implements Runnable {
private final DownloadInfo info;
private final SpeedInfo speedInfo = new SpeedInfo();
private long last;
/**
* Instantiates a new Download status listener.
*
* @param info the info
*/
DownloadStatusListener(final DownloadInfo info) {
this.info = info;
}
public void run() {
switch (info.getState()) {
case DONE:
speedInfo.end(info.getCount());
LOGGER.info("Download completed. [{}] average speed ([{}])", info.getState(),
FileUtils.byteCountToDisplaySize(speedInfo.getAverageSpeed()));
break;
case RETRYING:
LOGGER.debug(info.getState() + " retry: [{}], delay: [{}]", info.getRetry(), info.getDelay());
break;
case DOWNLOADING:
speedInfo.step(info.getCount());
final long now = System.currentTimeMillis();
if (now - 1_000 > last) {
last = now;
final StringBuilder partBuilder = new StringBuilder();
if (info.getParts() != null) {
info.getParts().forEach(p -> {
switch (p.getState()) {
case DOWNLOADING:
partBuilder.append(String.format("Part#%d(%.2f) ", p.getNumber(),
p.getCount() / (float) p.getLength()));
break;
case ERROR:
case RETRYING:
partBuilder.append(String.format("Part#%d(%s) ", p.getNumber(),
p.getException().getMessage() + " r:" + p.getRetry() + " d:" + p.getDelay()));
break;
default:
break;
}
});
}
final float p = info.getCount() / (float) info.getLength();
LOGGER.debug(String.format("%.2f %s (%s / %s)", p, partBuilder.toString(),
FileUtils.byteCountToDisplaySize(speedInfo.getCurrentSpeed()),
FileUtils.byteCountToDisplaySize(speedInfo.getAverageSpeed())));
}
break;
case EXTRACTING:
case EXTRACTING_DONE:
default:
LOGGER.debug(info.getState().toString());
break;
}
}
}
}