From 4e921c23c6cd4790c984ca453330bafcd05f77ef Mon Sep 17 00:00:00 2001 From: mohit-singh4 Date: Wed, 4 Dec 2024 10:12:57 +0530 Subject: [PATCH] [fix/MNT-24209-logging-for-download-restapi] Adding loggers in downloads RestAPI --- .../repo/download/ZipDownloadExporter.java | 688 +++++++++--------- 1 file changed, 344 insertions(+), 344 deletions(-) diff --git a/repository/src/main/java/org/alfresco/repo/download/ZipDownloadExporter.java b/repository/src/main/java/org/alfresco/repo/download/ZipDownloadExporter.java index 7d423f43c9f..5e999d39fcf 100644 --- a/repository/src/main/java/org/alfresco/repo/download/ZipDownloadExporter.java +++ b/repository/src/main/java/org/alfresco/repo/download/ZipDownloadExporter.java @@ -1,344 +1,344 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.download; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.attribute.FileTime; -import java.util.Date; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; - -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; -import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; -import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.UnicodeExtraFieldPolicy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.download.DownloadStatus; -import org.alfresco.service.cmr.download.DownloadStatus.Status; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.view.ExporterContext; -import org.alfresco.service.cmr.view.ExporterException; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; - -/** - * Handler for exporting node content to a ZIP file - * - * @author Alex Miller - */ -public class ZipDownloadExporter extends BaseExporter -{ - private static Logger log = LoggerFactory.getLogger(ZipDownloadExporter.class); - - private static final String PATH_SEPARATOR = "/"; - - protected ZipArchiveOutputStream zipStream; - - private NodeRef downloadNodeRef; - private int sequenceNumber = 1; - private long total; - private long done; - private long totalFileCount; - private long filesAddedCount; - - private RetryingTransactionHelper transactionHelper; - private DownloadStorage downloadStorage; - private DictionaryService dictionaryService; - private DownloadStatusUpdateService updateService; - - private Deque> path = new LinkedList>(); - private String currentName; - - private OutputStream outputStream; - private Date zipTimestampCreated; - private Date zipTimestampModified; - - /** - * Construct - * - * @param zipFile - * File - * @param checkOutCheckInService - * CheckOutCheckInService - * @param nodeService - * NodeService - * @param transactionHelper - * RetryingTransactionHelper - * @param updateService - * DownloadStatusUpdateService - * @param downloadStorage - * DownloadStorage - * @param dictionaryService - * DictionaryService - * @param downloadNodeRef - * NodeRef - * @param total - * long - * @param totalFileCount - * long - */ - public ZipDownloadExporter(File zipFile, CheckOutCheckInService checkOutCheckInService, NodeService nodeService, - RetryingTransactionHelper transactionHelper, DownloadStatusUpdateService updateService, - DownloadStorage downloadStorage, DictionaryService dictionaryService, - NodeRef downloadNodeRef, long total, long totalFileCount) - { - super(checkOutCheckInService, nodeService); - try - { - this.outputStream = new FileOutputStream(zipFile); - this.updateService = updateService; - this.transactionHelper = transactionHelper; - this.downloadStorage = downloadStorage; - this.dictionaryService = dictionaryService; - - this.downloadNodeRef = downloadNodeRef; - this.total = total; - this.totalFileCount = totalFileCount; - } - catch (FileNotFoundException e) - { - throw new ExporterException("Failed to create zip file", e); - } - } - - @Override - public void start(final ExporterContext context) - { - zipStream = new ZipArchiveOutputStream(outputStream); - // NOTE: This encoding allows us to workaround bug... - // http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807 - zipStream.setEncoding("UTF-8"); - zipStream.setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy.ALWAYS); - zipStream.setUseLanguageEncodingFlag(true); - zipStream.setFallbackToUTF8(true); - } - - @Override - public void startNode(NodeRef nodeRef) - { - this.currentName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - this.zipTimestampCreated = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_CREATED); - this.zipTimestampModified = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); - path.push(new Pair(currentName, nodeRef)); - if (dictionaryService.isSubClass(nodeService.getType(nodeRef), ContentModel.TYPE_FOLDER)) - { - String path = getPath() + PATH_SEPARATOR; - ZipArchiveEntry archiveEntry = new ZipArchiveEntry(path); - try - { - archiveEntry.setTime(zipTimestampCreated.getTime()); - archiveEntry.setCreationTime(FileTime.fromMillis(zipTimestampCreated.getTime())); - archiveEntry.setLastModifiedTime(FileTime.fromMillis(zipTimestampModified.getTime())); - zipStream.putArchiveEntry(archiveEntry); - zipStream.closeArchiveEntry(); - } - catch (IOException e) - { - throw new ExporterException("Unexpected IOException adding folder entry", e); - } - } - } - - @Override - public void contentImpl(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index) - { - // if the content stream to output is empty, then just return content descriptor as is - if (content == null) - { - log.info("Archiving content has been removed or modified for the specified NodeReference: " + nodeRef - + ", and the size of the content is " + contentData.getSize()); - return; - } - - try - { - if (log.isDebugEnabled()) - { - log.debug("Archiving content for nodeRef: " + nodeRef + " with contentURL: " - + contentData.getContentUrl()); - } - // ALF-2016 - ZipArchiveEntry zipEntry = new ZipArchiveEntry(getPath()); - zipEntry.setTime(zipTimestampCreated.getTime()); - zipEntry.setCreationTime(FileTime.fromMillis(zipTimestampCreated.getTime())); - zipEntry.setLastModifiedTime(FileTime.fromMillis(zipTimestampModified.getTime())); - zipStream.putArchiveEntry(zipEntry); - - // copy export stream to zip - copyStream(zipStream, content); - - zipStream.closeArchiveEntry(); - filesAddedCount = filesAddedCount + 1; - } - catch (IOException e) - { - throw new ExporterException("Failed to zip export stream", e); - } - } - - @Override - public void endNode(NodeRef nodeRef) - { - path.pop(); - } - - @Override - public void end() - { - try - { - zipStream.close(); - } - catch (IOException error) - { - throw new ExporterException("Unexpected error closing zip stream!", error); - } - } - - private String getPath() - { - if (path.size() < 1) - { - throw new IllegalStateException("No elements in path!"); - } - - Iterator> iter = path.descendingIterator(); - StringBuilder pathBuilder = new StringBuilder(); - - while (iter.hasNext()) - { - Pair element = iter.next(); - - pathBuilder.append(element.getFirst()); - if (iter.hasNext()) - { - pathBuilder.append(PATH_SEPARATOR); - } - } - - return pathBuilder.toString(); - } - - /** - * Copy input stream to output stream - * - * @param output - * output stream - * @param in - * input stream - * @throws IOException - */ - private void copyStream(OutputStream output, InputStream in) throws IOException - { - byte[] buffer = new byte[2048 * 10]; - int read = in.read(buffer, 0, 2048 * 10); - int i = 0; - while (read != -1) - { - output.write(buffer, 0, read); - done = done + read; - - // ALF-16289 - only update the status every 10MB - if (i++ % 500 == 0) - { - updateStatus(); - checkCancelled(); - } - - read = in.read(buffer, 0, 2048 * 10); - } - } - - private void checkCancelled() - { - boolean downloadCancelled = transactionHelper.doInTransaction(new RetryingTransactionCallback() { - @Override - public Boolean execute() throws Throwable - { - return downloadStorage.isCancelled(downloadNodeRef); - } - }, true, true); - - if (downloadCancelled == true) - { - log.debug("Download cancelled"); - throw new DownloadCancelledException(); - } - } - - private void updateStatus() - { - transactionHelper.doInTransaction(new RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable - { - DownloadStatus status = new DownloadStatus(Status.IN_PROGRESS, done, total, filesAddedCount, totalFileCount); - - updateService.update(downloadNodeRef, status, getNextSequenceNumber()); - return null; - } - }, false, true); - } - - public int getNextSequenceNumber() - { - return sequenceNumber++; - } - - public long getDone() - { - return done; - } - - public long getTotal() - { - return total; - } - - public long getFilesAdded() - { - return filesAddedCount; - } - - public long getTotalFiles() - { - return totalFileCount; - } -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.download; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.attribute.FileTime; +import java.util.Date; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.UnicodeExtraFieldPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.download.DownloadStatus; +import org.alfresco.service.cmr.download.DownloadStatus.Status; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.view.ExporterContext; +import org.alfresco.service.cmr.view.ExporterException; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * Handler for exporting node content to a ZIP file + * + * @author Alex Miller + */ +public class ZipDownloadExporter extends BaseExporter +{ + private static Logger log = LoggerFactory.getLogger(ZipDownloadExporter.class); + + private static final String PATH_SEPARATOR = "/"; + + protected ZipArchiveOutputStream zipStream; + + private NodeRef downloadNodeRef; + private int sequenceNumber = 1; + private long total; + private long done; + private long totalFileCount; + private long filesAddedCount; + + private RetryingTransactionHelper transactionHelper; + private DownloadStorage downloadStorage; + private DictionaryService dictionaryService; + private DownloadStatusUpdateService updateService; + + private Deque> path = new LinkedList>(); + private String currentName; + + private OutputStream outputStream; + private Date zipTimestampCreated; + private Date zipTimestampModified; + + /** + * Construct + * + * @param zipFile + * File + * @param checkOutCheckInService + * CheckOutCheckInService + * @param nodeService + * NodeService + * @param transactionHelper + * RetryingTransactionHelper + * @param updateService + * DownloadStatusUpdateService + * @param downloadStorage + * DownloadStorage + * @param dictionaryService + * DictionaryService + * @param downloadNodeRef + * NodeRef + * @param total + * long + * @param totalFileCount + * long + */ + public ZipDownloadExporter(File zipFile, CheckOutCheckInService checkOutCheckInService, NodeService nodeService, + RetryingTransactionHelper transactionHelper, DownloadStatusUpdateService updateService, + DownloadStorage downloadStorage, DictionaryService dictionaryService, + NodeRef downloadNodeRef, long total, long totalFileCount) + { + super(checkOutCheckInService, nodeService); + try + { + this.outputStream = new FileOutputStream(zipFile); + this.updateService = updateService; + this.transactionHelper = transactionHelper; + this.downloadStorage = downloadStorage; + this.dictionaryService = dictionaryService; + + this.downloadNodeRef = downloadNodeRef; + this.total = total; + this.totalFileCount = totalFileCount; + } + catch (FileNotFoundException e) + { + throw new ExporterException("Failed to create zip file", e); + } + } + + @Override + public void start(final ExporterContext context) + { + zipStream = new ZipArchiveOutputStream(outputStream); + // NOTE: This encoding allows us to workaround bug... + // http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807 + zipStream.setEncoding("UTF-8"); + zipStream.setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy.ALWAYS); + zipStream.setUseLanguageEncodingFlag(true); + zipStream.setFallbackToUTF8(true); + } + + @Override + public void startNode(NodeRef nodeRef) + { + this.currentName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + this.zipTimestampCreated = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_CREATED); + this.zipTimestampModified = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + path.push(new Pair(currentName, nodeRef)); + if (dictionaryService.isSubClass(nodeService.getType(nodeRef), ContentModel.TYPE_FOLDER)) + { + String path = getPath() + PATH_SEPARATOR; + ZipArchiveEntry archiveEntry = new ZipArchiveEntry(path); + try + { + archiveEntry.setTime(zipTimestampCreated.getTime()); + archiveEntry.setCreationTime(FileTime.fromMillis(zipTimestampCreated.getTime())); + archiveEntry.setLastModifiedTime(FileTime.fromMillis(zipTimestampModified.getTime())); + zipStream.putArchiveEntry(archiveEntry); + zipStream.closeArchiveEntry(); + } + catch (IOException e) + { + throw new ExporterException("Unexpected IOException adding folder entry", e); + } + } + } + + @Override + public void contentImpl(NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index) + { + // if the content stream to output is empty, then just return content descriptor as is + if (content == null) + { + log.info("Archiving content has been removed or modified for the specified NodeReference: " + nodeRef + + ", and the size of the content is " + contentData.getSize()); + return; + } + + try + { + if (log.isDebugEnabled()) + { + log.debug("Archiving content for nodeRef: " + nodeRef + " with contentURL: " + + contentData.getContentUrl()); + } + // ALF-2016 + ZipArchiveEntry zipEntry = new ZipArchiveEntry(getPath()); + zipEntry.setTime(zipTimestampCreated.getTime()); + zipEntry.setCreationTime(FileTime.fromMillis(zipTimestampCreated.getTime())); + zipEntry.setLastModifiedTime(FileTime.fromMillis(zipTimestampModified.getTime())); + zipStream.putArchiveEntry(zipEntry); + + // copy export stream to zip + copyStream(zipStream, content); + + zipStream.closeArchiveEntry(); + filesAddedCount = filesAddedCount + 1; + } + catch (IOException e) + { + throw new ExporterException("Failed to zip export stream", e); + } + } + + @Override + public void endNode(NodeRef nodeRef) + { + path.pop(); + } + + @Override + public void end() + { + try + { + zipStream.close(); + } + catch (IOException error) + { + throw new ExporterException("Unexpected error closing zip stream!", error); + } + } + + private String getPath() + { + if (path.size() < 1) + { + throw new IllegalStateException("No elements in path!"); + } + + Iterator> iter = path.descendingIterator(); + StringBuilder pathBuilder = new StringBuilder(); + + while (iter.hasNext()) + { + Pair element = iter.next(); + + pathBuilder.append(element.getFirst()); + if (iter.hasNext()) + { + pathBuilder.append(PATH_SEPARATOR); + } + } + + return pathBuilder.toString(); + } + + /** + * Copy input stream to output stream + * + * @param output + * output stream + * @param in + * input stream + * @throws IOException + */ + private void copyStream(OutputStream output, InputStream in) throws IOException + { + byte[] buffer = new byte[2048 * 10]; + int read = in.read(buffer, 0, 2048 * 10); + int i = 0; + while (read != -1) + { + output.write(buffer, 0, read); + done = done + read; + + // ALF-16289 - only update the status every 10MB + if (i++ % 500 == 0) + { + updateStatus(); + checkCancelled(); + } + + read = in.read(buffer, 0, 2048 * 10); + } + } + + private void checkCancelled() + { + boolean downloadCancelled = transactionHelper.doInTransaction(new RetryingTransactionCallback() { + @Override + public Boolean execute() throws Throwable + { + return downloadStorage.isCancelled(downloadNodeRef); + } + }, true, true); + + if (downloadCancelled == true) + { + log.debug("Download cancelled"); + throw new DownloadCancelledException(); + } + } + + private void updateStatus() + { + transactionHelper.doInTransaction(new RetryingTransactionCallback() { + @Override + public Object execute() throws Throwable + { + DownloadStatus status = new DownloadStatus(Status.IN_PROGRESS, done, total, filesAddedCount, totalFileCount); + + updateService.update(downloadNodeRef, status, getNextSequenceNumber()); + return null; + } + }, false, true); + } + + public int getNextSequenceNumber() + { + return sequenceNumber++; + } + + public long getDone() + { + return done; + } + + public long getTotal() + { + return total; + } + + public long getFilesAdded() + { + return filesAddedCount; + } + + public long getTotalFiles() + { + return totalFileCount; + } +}