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; } } } }