public interface ImageWriter {

    void write(RenderedImage image, String format, File path);

} public class ImageWriterImpl implements ImageWriter {
    @Override
    public void write(RenderedImage image, String format, File path) {
        try {
            ImageIO.write(image, format, path);
        } catch (IOException e) {
            throw new ImageStoreException(e);
        }
    }
} public interface ImageStoreFactory {

    File createImageStore(File basePath, String label);

} public interface JsoupDocumentFactory {

    Document create(InputStream inputStream) throws IOException;

} public interface RequestFactory {

    HttpUriRequest createGenerateSessionRequest();

    HttpUriRequest createGetCaptchaImageRequest(CEIDGCaptchaSessionResource ceidgCaptchaSessionResource);

} public class ImageStoreFactoryImpl implements ImageStoreFactory {
    @Override
    public File createImageStore(File basePath, String label) {
        File imageStore = new File(basePath, label);
        if (!imageStore.mkdir()) {
            throw new ImageStoreException("Can't create labelled store");
        }
        return imageStore;
    }
} public class JsoupDocumentFactoryImpl implements JsoupDocumentFactory {
    @Override
    public Document create(InputStream inputStream) throws IOException {
        return Jsoup.parse(inputStream, "UTF-8", "");
    }
} public class RequestFactoryImpl implements RequestFactory {

    private final CEIDGClientEndpointConfigurationResource ceidgClientEndpointConfigurationResource;
    private final CookieParser cookieParser;

    @Inject
    public RequestFactoryImpl(CEIDGClientEndpointConfigurationResource ceidgClientEndpointConfigurationResource, CookieParser cookieParser) {
        this.ceidgClientEndpointConfigurationResource = ceidgClientEndpointConfigurationResource;
        this.cookieParser = cookieParser;
    }

    @Override
    public HttpUriRequest createGenerateSessionRequest() {
        return new HttpGet(ceidgClientEndpointConfigurationResource.getSessionGenerateUrl());
    }

    @Override
    public HttpUriRequest createGetCaptchaImageRequest(CEIDGCaptchaSessionResource ceidgCaptchaSessionResource) {
        URI uri = buildURI(ceidgCaptchaSessionResource);
        String cookieString = getCookieString(ceidgCaptchaSessionResource);
        HttpUriRequest httpUriRequest = new HttpGet(uri);
        httpUriRequest.setHeader("Cookie", cookieString);
        return httpUriRequest;
    }

    private String getCookieString(CEIDGCaptchaSessionResource ceidgCaptchaSessionResource) {
        Map<String, String> cookies = new HashMap<>();
        cookies.put("ASP.NET_SessionId", ceidgCaptchaSessionResource.getSessionId());
        return cookieParser.formatCookieString(cookies);
    }

    private URI buildURI(CEIDGCaptchaSessionResource ceidgCaptchaSessionResource) {
        try {
            URIBuilder uriBuilder = new URIBuilder(ceidgClientEndpointConfigurationResource.getImageDownloadUrl())
                    .addParameter("part", "0")
                    .addParameter("id", ceidgCaptchaSessionResource.getCaptchaId());
            return uriBuilder.build();
        } catch (URISyntaxException e) {
            throw new CEIDGHttpException(e);
        }
    }

} public interface CaptchaDownloaderService {
    void downloadCaptchaImages(int imagesCount, String format);
} @Log4j
public class CaptchaDownloaderServiceImpl implements CaptchaDownloaderService {

    private final CaptchaLabeler captchaLabeler;
    private final StoreChooser storeChooser;
    private final CEIDGClient ceidgClient;
    private final ImageStoreFactory imageStoreFactory;
    private final ImageWriter imageWriter;

    @Inject
    public CaptchaDownloaderServiceImpl(CaptchaLabeler captchaLabeler, StoreChooser storeChooser, CEIDGClient ceidgClient,
                                        ImageStoreFactory imageStoreFactory, ImageWriter imageWriter) {
        this.captchaLabeler = captchaLabeler;
        this.storeChooser = storeChooser;
        this.ceidgClient = ceidgClient;
        this.imageStoreFactory = imageStoreFactory;
        this.imageWriter = imageWriter;
    }

    @Override
    public void downloadCaptchaImages(int imagesCount, String format) {
        CEIDGCaptchaSessionResource session = ceidgClient.generateSession();
        File imageStore = getStore(session);
        downloadImages(imagesCount, format, imageStore, session);
    }

    private File getStore(CEIDGCaptchaSessionResource session) {
        File storeBasePath = storeChooser.getDirectory()
                .orElseThrow(() -> new ImageStoreException("No store selected"));

        BufferedImage imageToLabel = ceidgClient.getCaptchaImage(session);
        String label = captchaLabeler.getLabel(imageToLabel)
                .orElseThrow(() -> new CaptchaLabelingException("No label provided"));
        return imageStoreFactory.createImageStore(storeBasePath, label);
    }

    private void downloadImages(int imagesCount, String format, File imageStore, CEIDGCaptchaSessionResource session) {
        for (int i = 0; i < imagesCount; i++) {
            log.info("Downloading image " + (i + 1) + " of " + imagesCount);
            BufferedImage downloadedImage = ceidgClient.getCaptchaImage(session);
            File imagePath = new File(imageStore, String.valueOf(i) + "." + format);
            imageWriter.write(downloadedImage, format, imagePath);
        }
    }

}
import com.google.inject.AbstractModule;
import net.scaliby.ceidgcaptcha.downloader.common.ImageWriter;
import net.scaliby.ceidgcaptcha.downloader.common.impl.ImageWriterImpl;
import net.scaliby.ceidgcaptcha.downloader.common.*;
import net.scaliby.ceidgcaptcha.downloader.common.impl.*;
import net.scaliby.ceidgcaptcha.downloader.factory.ImageStoreFactory;
import net.scaliby.ceidgcaptcha.downloader.factory.JsoupDocumentFactory;
import net.scaliby.ceidgcaptcha.downloader.factory.RequestFactory;
import net.scaliby.ceidgcaptcha.downloader.factory.impl.ImageStoreFactoryImpl;
import net.scaliby.ceidgcaptcha.downloader.factory.impl.JsoupDocumentFactoryImpl;
import net.scaliby.ceidgcaptcha.downloader.factory.impl.RequestFactoryImpl;
import net.scaliby.ceidgcaptcha.downloader.service.CaptchaDownloaderService;
import net.scaliby.ceidgcaptcha.downloader.service.impl.CaptchaDownloaderServiceImpl;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
package net.scaliby.ceidgcaptcha.downloader;


public class CEIDGCaptchaDownloaderModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(CEIDGClient.class).to(CEIDGClientImpl.class);
        bind(CookieParser.class).to(CookieParserImpl.class);
        bind(CaptchaLabeler.class).to(SwingCaptchaLabeler.class);
        bind(CaptchaDownloaderService.class).to(CaptchaDownloaderServiceImpl.class);
        bind(CloseableHttpClient.class).toInstance(HttpClientBuilder.create().disableCookieManagement().build());
        bind(CaptchaIdParser.class).to(CaptchaIdParserImpl.class);
        bind(ImageParser.class).to(ImageParserImpl.class);
        bind(ImageWriter.class).to(ImageWriterImpl.class);