com.liferay.sync.engine.document.library.handler.DownloadFileHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.sync.engine.document.library.handler.DownloadFileHandler.java

Source

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library 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 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library 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.
 */

package com.liferay.sync.engine.document.library.handler;

import com.liferay.sync.engine.SyncEngine;
import com.liferay.sync.engine.document.library.event.Event;
import com.liferay.sync.engine.document.library.util.FileEventUtil;
import com.liferay.sync.engine.file.system.Watcher;
import com.liferay.sync.engine.file.system.util.WatcherManager;
import com.liferay.sync.engine.model.SyncAccount;
import com.liferay.sync.engine.model.SyncFile;
import com.liferay.sync.engine.service.SyncAccountService;
import com.liferay.sync.engine.service.SyncFileService;
import com.liferay.sync.engine.session.Session;
import com.liferay.sync.engine.session.SessionManager;
import com.liferay.sync.engine.session.rate.limiter.RateLimitedInputStream;
import com.liferay.sync.engine.util.FileKeyUtil;
import com.liferay.sync.engine.util.FileUtil;
import com.liferay.sync.engine.util.GetterUtil;
import com.liferay.sync.engine.util.IODeltaUtil;
import com.liferay.sync.engine.util.MSOfficeFileUtil;
import com.liferay.sync.engine.util.StreamUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.nio.file.AccessDeniedException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;

import java.util.concurrent.ExecutorService;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.http.ConnectionClosedException;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Shinn Lok
 */
public class DownloadFileHandler extends BaseHandler {

    public DownloadFileHandler(Event event) {
        super(event);
    }

    @Override
    public void handleException(Exception e) {
        if (isEventCancelled()) {
            return;
        }

        if (e instanceof ConnectionClosedException) {
            String message = e.getMessage();

            if (message.startsWith("Premature end of Content-Length")) {
                _logger.error(message, e);

                removeEvent();

                SyncFile localSyncFile = getLocalSyncFile();

                if (localSyncFile == null) {
                    return;
                }

                FileEventUtil.downloadFile(getSyncAccountId(), localSyncFile, false);

                return;
            }
        } else if (e instanceof HttpResponseException) {
            _logger.error(e.getMessage(), e);

            HttpResponseException hre = (HttpResponseException) e;

            int statusCode = hre.getStatusCode();

            if (statusCode != HttpStatus.SC_NOT_FOUND) {
                super.handleException(e);

                return;
            }

            SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(getSyncAccountId());

            if (syncAccount.getState() != SyncAccount.STATE_CONNECTED) {
                super.handleException(e);

                return;
            }

            removeEvent();

            SyncFile syncFile = getLocalSyncFile();

            if (syncFile == null) {
                return;
            }

            if ((boolean) getParameterValue("patch")) {
                FileEventUtil.downloadFile(getSyncAccountId(), syncFile, false);
            } else {
                handleException(syncFile);
            }

            return;
        }

        super.handleException(e);
    }

    @Override
    public boolean handlePortalException(String exception) throws Exception {
        SyncFile syncFile = getLocalSyncFile();

        if (syncFile == null) {
            return true;
        }

        if (_logger.isDebugEnabled()) {
            _logger.debug("Handling exception {} file path {}", exception, syncFile.getFilePathName());
        }

        if (exception.endsWith("PrincipalException")) {
            syncFile.setState(SyncFile.STATE_ERROR);
            syncFile.setUiEvent(SyncFile.UI_EVENT_INVALID_PERMISSIONS);

            SyncFileService.update(syncFile);

            return true;
        } else if (exception.endsWith("NoSuchFileVersionException") && (boolean) getParameterValue("patch")) {

            removeEvent();

            FileEventUtil.downloadFile(getSyncAccountId(), syncFile, false);

            return true;
        } else if (exception.endsWith("NoSuchFileEntryException") || exception.endsWith("NoSuchFileException")) {

            SyncFileService.deleteSyncFile(syncFile);

            return true;
        }

        removeEvent();

        handleException(syncFile);

        return true;
    }

    protected void copyFile(final SyncFile syncFile, Path filePath, InputStream inputStream, boolean append)
            throws Exception {

        OutputStream outputStream = null;

        Watcher watcher = WatcherManager.getWatcher(getSyncAccountId());

        try {
            Path tempFilePath = FileUtil.getTempFilePath(syncFile);

            boolean exists = FileUtil.exists(filePath);

            if (append) {
                outputStream = Files.newOutputStream(tempFilePath, StandardOpenOption.APPEND);

                IOUtils.copyLarge(inputStream, outputStream);
            } else {
                if (exists && (boolean) getParameterValue("patch")) {
                    if (_logger.isDebugEnabled()) {
                        _logger.debug("Patching {}", syncFile.getFilePathName());
                    }

                    Files.copy(filePath, tempFilePath, StandardCopyOption.REPLACE_EXISTING);

                    IODeltaUtil.patch(tempFilePath, inputStream);
                } else {
                    Files.copy(inputStream, tempFilePath, StandardCopyOption.REPLACE_EXISTING);
                }
            }

            watcher.addDownloadedFilePathName(filePath.toString());

            if (GetterUtil.getBoolean(syncFile.getLocalExtraSettingValue("restoreEvent"))) {

                syncFile.unsetLocalExtraSetting("restoreEvent");

                syncFile.setUiEvent(SyncFile.UI_EVENT_RESTORED_REMOTE);
            } else if (exists) {
                syncFile.setUiEvent(SyncFile.UI_EVENT_DOWNLOADED_UPDATE);
            } else {
                syncFile.setUiEvent(SyncFile.UI_EVENT_DOWNLOADED_NEW);
            }

            FileKeyUtil.writeFileKey(tempFilePath, String.valueOf(syncFile.getSyncFileId()), false);

            FileUtil.setModifiedTime(tempFilePath, syncFile.getModifiedTime());

            if (MSOfficeFileUtil.isLegacyExcelFile(filePath)) {
                syncFile.setLocalExtraSetting("lastSavedDate", MSOfficeFileUtil.getLastSavedDate(tempFilePath));
            }

            Files.move(tempFilePath, filePath, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);

            ExecutorService executorService = SyncEngine.getExecutorService();

            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    IODeltaUtil.checksums(syncFile);

                    syncFile.setState(SyncFile.STATE_SYNCED);

                    SyncFileService.update(syncFile);
                }

            };

            executorService.execute(runnable);
        } catch (FileSystemException fse) {
            if (fse instanceof AccessDeniedException) {
                _logger.error(fse.getMessage(), fse);

                syncFile.setState(SyncFile.STATE_ERROR);
                syncFile.setUiEvent(SyncFile.UI_EVENT_ACCESS_DENIED_LOCAL);

                SyncFileService.update(syncFile);

                return;
            } else if (fse instanceof NoSuchFileException) {
                if (isEventCancelled()) {
                    SyncFileService.deleteSyncFile(syncFile);

                    return;
                }
            }

            watcher.removeDownloadedFilePathName(filePath.toString());

            String message = fse.getMessage();

            _logger.error(message, fse);

            syncFile.setState(SyncFile.STATE_ERROR);

            if (message.contains("File name too long")) {
                syncFile.setUiEvent(SyncFile.UI_EVENT_FILE_NAME_TOO_LONG);
            }

            SyncFileService.update(syncFile);
        } finally {
            StreamUtil.cleanUp(outputStream);
        }
    }

    @Override
    protected void doHandleResponse(HttpResponse httpResponse) throws Exception {

        Header errorHeader = httpResponse.getFirstHeader("Sync-Error");

        if (errorHeader != null) {
            handleSiteDeactivatedException();
        }

        long syncAccountId = getSyncAccountId();

        final Session session = SessionManager.getSession(syncAccountId);

        Header tokenHeader = httpResponse.getFirstHeader("Sync-JWT");

        if (tokenHeader != null) {
            session.addHeader("Sync-JWT", tokenHeader.getValue());
        }

        InputStream inputStream = null;

        SyncFile syncFile = getLocalSyncFile();

        if ((syncFile == null) || isUnsynced(syncFile)) {
            return;
        }

        Path filePath = Paths.get(syncFile.getFilePathName());

        try {
            HttpEntity httpEntity = httpResponse.getEntity();

            inputStream = new CountingInputStream(httpEntity.getContent()) {

                @Override
                protected synchronized void afterRead(int n) {
                    session.incrementDownloadedBytes(n);

                    super.afterRead(n);
                }

            };

            inputStream = new RateLimitedInputStream(inputStream, syncAccountId);

            if (httpResponse.getFirstHeader("Accept-Ranges") != null) {
                copyFile(syncFile, filePath, inputStream, true);
            } else {
                copyFile(syncFile, filePath, inputStream, false);
            }
        } finally {
            StreamUtil.cleanUp(inputStream);
        }
    }

    protected void handleException(SyncFile syncFile) {
        syncFile.setState(SyncFile.STATE_ERROR);
        syncFile.setUiEvent(SyncFile.UI_EVENT_DOWNLOAD_EXCEPTION);

        SyncFileService.update(syncFile);

        Path filePathName = Paths.get(syncFile.getFilePathName());

        if (FileUtil.notExists(filePathName)) {
            try {
                Files.createFile(filePathName);
            } catch (IOException ioe) {
                _logger.error(ioe.getMessage(), ioe);
            }
        }
    }

    protected boolean isUnsynced(SyncFile syncFile) {
        if (syncFile != null) {
            syncFile = SyncFileService.fetchSyncFile(syncFile.getSyncFileId());
        }

        if (syncFile == null) {
            return true;
        }

        if (syncFile.getState() == SyncFile.STATE_UNSYNCED) {
            if (_logger.isDebugEnabled()) {
                _logger.debug("Skipping file {}. File is unsynced.", syncFile.getName());
            }

            return true;
        }

        Path filePath = Paths.get(syncFile.getFilePathName());

        if (FileUtil.notExists(filePath.getParent())) {
            if (_logger.isDebugEnabled()) {
                _logger.debug("Skipping file {}. Missing parent file path {}.", syncFile.getName(),
                        filePath.getParent());
            }

            syncFile.setState(SyncFile.STATE_ERROR);
            syncFile.setUiEvent(SyncFile.UI_EVENT_PARENT_MISSING);

            SyncFileService.update(syncFile);

            return true;
        }

        return false;
    }

    private static final Logger _logger = LoggerFactory.getLogger(DownloadFileHandler.class);

}