/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.dropbox.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import com.dropbox.core.DbxClient;
import com.dropbox.core.DbxEntry;
import com.dropbox.core.DbxException;
import com.dropbox.core.DbxWriteMode;
import org.apache.camel.Exchange;
import org.apache.camel.component.dropbox.dto.DropboxDelResult;
import org.apache.camel.component.dropbox.dto.DropboxFileDownloadResult;
import org.apache.camel.component.dropbox.dto.DropboxFileUploadResult;
import org.apache.camel.component.dropbox.dto.DropboxMoveResult;
import org.apache.camel.component.dropbox.dto.DropboxSearchResult;
import org.apache.camel.component.dropbox.util.DropboxException;
import org.apache.camel.component.dropbox.util.DropboxResultCode;
import org.apache.camel.component.dropbox.util.DropboxUploadMode;
import org.apache.camel.converter.stream.OutputStreamBuilder;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.camel.component.dropbox.util.DropboxConstants.DROPBOX_FILE_SEPARATOR;
public final class DropboxAPIFacade {
private static final transient Logger LOG = LoggerFactory.getLogger(DropboxAPIFacade.class);
private final DbxClient client;
private final Exchange exchange;
/**
* @param client the DbxClient performing dropbox low level operations
* @param exchange the current Exchange
*/
public DropboxAPIFacade(DbxClient client, Exchange exchange) {
this.client = client;
this.exchange = exchange;
}
/**
* Put or upload a new file or an entire directory to dropbox
* @param localPath the file path or the dir path on the local filesystem
* @param remotePath the remote path destination on dropbox
* @param mode how a file should be saved on dropbox;
* in case of "add" the new file will be renamed in case
* a file with the same name already exists on dropbox.
* in case of "force" the file already existing with the same name will be overridden.
* @return a result object reporting for each remote path the result of the operation.
* @throws DropboxException
*/
public DropboxFileUploadResult put(String localPath, String remotePath, DropboxUploadMode mode) throws DropboxException {
//in case the remote path is not specified, the remotePath = localPath
String dropboxPath = remotePath == null ? localPath : remotePath;
DbxEntry entry;
try {
entry = client.getMetadata(dropboxPath);
} catch (DbxException e) {
throw new DropboxException(dropboxPath + " does not exist or can't obtain metadata");
}
File fileLocalPath = new File(localPath);
//verify uploading of a single file
if (fileLocalPath.isFile()) {
//check if dropbox file exists
if (entry != null && !entry.isFile()) {
throw new DropboxException(dropboxPath + " exists on dropbox and is not a file!");
}
//in case the entry not exists on dropbox check if the filename should be appended
if (entry == null) {
if (dropboxPath.endsWith(DROPBOX_FILE_SEPARATOR)) {
dropboxPath = dropboxPath + fileLocalPath.getName();
}
}
DropboxFileUploadResult result;
try {
DbxEntry.File uploadedFile = putSingleFile(fileLocalPath, dropboxPath, mode);
if (uploadedFile == null) {
result = new DropboxFileUploadResult(dropboxPath, DropboxResultCode.KO);
} else {
result = new DropboxFileUploadResult(dropboxPath, DropboxResultCode.OK);
}
} catch (Exception ex) {
result = new DropboxFileUploadResult(dropboxPath, DropboxResultCode.KO);
}
return result;
} else { //verify uploading of a list of files inside a dir
LOG.debug("Uploading a dir...");
//check if dropbox folder exists
if (entry != null && !entry.isFolder()) {
throw new DropboxException(dropboxPath + " exists on dropbox and is not a folder!");
}
if (!dropboxPath.endsWith(DROPBOX_FILE_SEPARATOR)) {
dropboxPath = dropboxPath + DROPBOX_FILE_SEPARATOR;
}
//revert to old path
String oldDropboxPath = dropboxPath;
//list all files in a dir
Collection<File> listFiles = FileUtils.listFiles(fileLocalPath, null, true);
if (listFiles.isEmpty()) {
throw new DropboxException(localPath + " doesn't contain any files");
}
HashMap<String, DropboxResultCode> resultMap = new HashMap<>(listFiles.size());
for (File file : listFiles) {
String absPath = file.getAbsolutePath();
int indexRemainingPath = localPath.length();
if (!localPath.endsWith("/")) {
indexRemainingPath += 1;
}
String remainingPath = absPath.substring(indexRemainingPath);
dropboxPath = dropboxPath + remainingPath;
try {
LOG.debug("Uploading: {},{}", fileLocalPath, dropboxPath);
DbxEntry.File uploadedFile = putSingleFile(file, dropboxPath, mode);
if (uploadedFile == null) {
resultMap.put(dropboxPath, DropboxResultCode.KO);
} else {
resultMap.put(dropboxPath, DropboxResultCode.OK);
}
} catch (Exception ex) {
resultMap.put(dropboxPath, DropboxResultCode.KO);
}
dropboxPath = oldDropboxPath;
}
return new DropboxFileUploadResult(resultMap);
}
}
private DbxEntry.File putSingleFile(File inputFile, String dropboxPath, DropboxUploadMode mode) throws Exception {
FileInputStream inputStream = new FileInputStream(inputFile);
DbxEntry.File uploadedFile;
try {
DbxWriteMode uploadMode;
if (mode == DropboxUploadMode.force) {
uploadMode = DbxWriteMode.force();
} else {
uploadMode = DbxWriteMode.add();
}
uploadedFile = client.uploadFile(dropboxPath, uploadMode, inputFile.length(), inputStream);
return uploadedFile;
} finally {
inputStream.close();
}
}
/**
* Search inside a remote path including its sub directories.
* The query param can be null.
* @param remotePath the remote path where starting the search from
* @param query a space-separated list of substrings to search for. A file matches only if it contains all the substrings
* @return a result object containing all the files found.
* @throws DropboxException
*/
public DropboxSearchResult search(String remotePath, String query) throws DropboxException {
DbxEntry.WithChildren listing;
if (query == null) {
LOG.debug("Search no query");
try {
listing = client.getMetadataWithChildren(remotePath);
return new DropboxSearchResult(listing.children);
} catch (DbxException e) {
throw new DropboxException(remotePath + " does not exist or can't obtain metadata");
}
} else {
LOG.debug("Search by query: {}", query);
try {
List<DbxEntry> entries = client.searchFileAndFolderNames(remotePath, query);
return new DropboxSearchResult(entries);
} catch (DbxException e) {
throw new DropboxException(remotePath + " does not exist or can't obtain metadata");
}
}
}
/**
* Delete every files and subdirectories inside the remote directory.
* In case the remotePath is a file, delete the file.
* @param remotePath the remote location to delete
* @return a result object with the result of the delete operation.
* @throws DropboxException
*/
public DropboxDelResult del(String remotePath) throws DropboxException {
try {
client.delete(remotePath);
} catch (DbxException e) {
throw new DropboxException(remotePath + " does not exist or can't obtain metadata");
}
return new DropboxDelResult(remotePath);
}
/**
* Rename a remote path with the new path location.
* @param remotePath the existing remote path to be renamed
* @param newRemotePath the new remote path substituting the old one
* @return a result object with the result of the move operation.
* @throws DropboxException
*/
public DropboxMoveResult move(String remotePath, String newRemotePath) throws DropboxException {
try {
client.move(remotePath, newRemotePath);
return new DropboxMoveResult(remotePath, newRemotePath);
} catch (DbxException e) {
throw new DropboxException(remotePath + " does not exist or can't obtain metadata");
}
}
/**
* Get the content of every file inside the remote path.
* @param remotePath the remote path where to download from
* @return a result object with the content (ByteArrayOutputStream) of every files inside the remote path.
* @throws DropboxException
*/
public DropboxFileDownloadResult get(String remotePath) throws DropboxException {
return new DropboxFileDownloadResult(downloadFilesInFolder(remotePath));
}
public boolean isDirectory(String path) throws DropboxException {
try {
DbxEntry.WithChildren listing = client.getMetadataWithChildren(path);
return listing.children != null;
} catch (DbxException e) {
throw new DropboxException(path + " does not exist or can't obtain metadata");
}
}
private Map<String, Object> downloadFilesInFolder(String path) throws DropboxException {
try {
DbxEntry.WithChildren listing = client.getMetadataWithChildren(path);
if (listing == null) {
return Collections.emptyMap();
} else if (listing.children == null) {
LOG.debug("downloading a single file...");
Map.Entry<String, Object> entry = downloadSingleFile(path);
return Collections.singletonMap(entry.getKey(), entry.getValue());
}
Map<String, Object> result = new HashMap<>();
for (DbxEntry entry : listing.children) {
if (entry.isFile()) {
try {
Map.Entry<String, Object> singleFile = downloadSingleFile(entry.path);
result.put(singleFile.getKey(), singleFile.getValue());
} catch (DropboxException e) {
LOG.warn("Cannot download from path={}, reason={}. This exception is ignored.", entry.path, e.getMessage());
}
} else {
Map<String, Object> filesInFolder = downloadFilesInFolder(entry.path);
result.putAll(filesInFolder);
}
}
return result;
} catch (DbxException e) {
throw new DropboxException(e);
}
}
private Map.Entry<String, Object> downloadSingleFile(String path) throws DropboxException {
try {
OutputStreamBuilder target = OutputStreamBuilder.withExchange(exchange);
DbxEntry.File downloadedFile = client.getFile(path, null, target);
if (downloadedFile != null) {
LOG.debug("downloaded path={}", path);
return new AbstractMap.SimpleEntry<>(path, target.build());
} else {
return null;
}
} catch (DbxException e) {
throw new DropboxException(path + " does not exist or can't obtain metadata");
} catch (IOException e) {
throw new DropboxException(path + " can't obtain a stream");
}
}
}