class AssetRenditionsZipperImpl implements AssetRenditionsDownloadOrchestrator {

    @Override
    public void execute(final SlingHttpServletRequest request,
                        final SlingHttpServletResponse response,
                        final List<AssetModel> assets,
                        final List<String> renditionNames) throws IOException {
        final String filename = StringUtils.defaultIfBlank(getFileName(request.getResource().getValueMap()), DEFAULT_FILE_ATTACHMENT_NAME);

        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
        response.setContentType("application/zip");

        final ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());

        final boolean groupAssetRenditionsByFolder = true;

        long size = 0L;

        final Set<String> zipEntryFileNames = new HashSet<>();
        final Set<String> zipEntryFolderNames = new HashSet<>();

        for (final AssetModel asset : assets) {
            String folderName = null;

            if (groupAssetRenditionsByFolder) {
                folderName = generateUniqueZipEntry(asset.getName(), zipEntryFolderNames);
            }

            for (final String renditionName : renditionNames) {
                AssetRenditionStreamer.AssetRenditionStream stream = null;

                try {
                    stream = assetRenditionStreamer.getAssetRendition(request, response, asset, renditionName);

                    if (stream.getOutputStream().size() > 0) {
                        size += stream.getOutputStream().size();

                        checkForMaxSize(size);

                        if (folderName != null && !zipEntryFolderNames.contains(folderName)) {
                            addFolderAsZipEntry(folderName + "/", zipOutputStream);
                            zipEntryFolderNames.add(folderName);
                        }

                        final String zipEntryName = getZipEntryName(folderName, asset, renditionName, stream.getContentType(), zipEntryFileNames);

                        addAssetRenditionAsZipEntry(folderName, zipEntryName, zipOutputStream, stream.getOutputStream());
                    }
                } catch (AssetRenditionsException ex) {
                    log.error("Unable to obtain the AssetRendition as an output stream. Skipping...", ex);
                    continue;
                } catch (IOException ex) {
                    log.error("Unable to add entry to Zip that is streamed to HTTP Response. Skipping...", ex);
                    continue;
                } finally {
                    if (stream != null && stream.getOutputStream() != null) {
                        stream.getOutputStream().close();
                    }
                }
            }
        }

        if (size == 0) {
            final String fileName = request.getResource().getValueMap().get(PN_NO_CONTENT_FILE_NAME, String.class);
            String message = request.getResource().getValueMap().get(PN_NO_CONTENT_MESSAGE, String.class);
            addNoContentFile(fileName, message, zipOutputStream);
        }

        zipOutputStream.close();
    }

}