feat: remove idCache and switch to loading with paths

This commit is contained in:
Manuel Jenny 2021-03-24 10:50:27 +01:00
parent 86a0b1e6d5
commit 9b93fb9623
No known key found for this signature in database
GPG Key ID: 1C80FE62B2BEAA18
7 changed files with 40 additions and 247 deletions

View File

@ -11,15 +11,13 @@ class PCloudFile implements CloudFile, PCloudNode {
private final PCloudFolder parent;
private final String name;
private final String path;
private final Long fileId;
private final Optional<Long> size;
private final Optional<Date> modified;
public PCloudFile(PCloudFolder parent, String name, String path, Long fileId, Optional<Long> size, Optional<Date> modified) {
public PCloudFile(PCloudFolder parent, String name, String path, Optional<Long> size, Optional<Date> modified) {
this.parent = parent;
this.name = name;
this.path = path;
this.fileId = fileId;
this.size = size;
this.modified = modified;
}
@ -39,11 +37,6 @@ class PCloudFile implements CloudFile, PCloudNode {
return path;
}
@Override
public Long getId() {
return fileId;
}
@Override
public PCloudFolder getParent() {
return parent;

View File

@ -8,11 +8,9 @@ class PCloudFolder implements CloudFolder, PCloudNode {
private final PCloudFolder parent;
private final String name;
private final String path;
private final Long folderId;
public PCloudFolder(PCloudFolder parent, String name, String path, Long folderId) {
public PCloudFolder(PCloudFolder parent, String name, String path) {
this.parent = parent;
this.folderId = folderId;
this.name = name;
this.path = path;
}
@ -32,11 +30,6 @@ class PCloudFolder implements CloudFolder, PCloudNode {
return path;
}
@Override
public Long getId() {
return folderId;
}
@Override
public PCloudFolder getParent() {
return parent;
@ -44,6 +37,6 @@ class PCloudFolder implements CloudFolder, PCloudNode {
@Override
public PCloudFolder withCloud(Cloud cloud) {
return new PCloudFolder(parent.withCloud(cloud), name, path, folderId);
return new PCloudFolder(parent.withCloud(cloud), name, path);
}
}

View File

@ -1,77 +0,0 @@
package org.cryptomator.data.cloud.pcloud;
import android.util.LruCache;
import org.cryptomator.domain.CloudFolder;
import javax.inject.Inject;
class PCloudIdCache {
private final LruCache<String, NodeInfo> cache;
@Inject
PCloudIdCache() {
cache = new LruCache<>(1000);
}
public NodeInfo get(String path) {
return cache.get(path);
}
<T extends PCloudNode> T cache(T value) {
add(value);
return value;
}
public void add(PCloudNode node) {
add(node.getPath(), new NodeInfo(node));
}
private void add(String path, NodeInfo info) {
cache.put(path, info);
}
public void remove(PCloudNode node) {
remove(node.getPath());
}
private void remove(String path) {
removeChildren(path);
cache.remove(path);
}
private void removeChildren(String path) {
String prefix = path + '/';
for (String key : cache.snapshot().keySet()) {
if (key.startsWith(prefix)) {
cache.remove(key);
}
}
}
static class NodeInfo {
private final Long id;
private final boolean isFolder;
private NodeInfo(PCloudNode node) {
this(node.getId(), node instanceof CloudFolder);
}
NodeInfo(Long id, boolean isFolder) {
this.id = id;
this.isFolder = isFolder;
}
public Long getId() {
return id;
}
public boolean isFolder() {
return isFolder;
}
}
}

View File

@ -58,8 +58,6 @@ import static org.cryptomator.util.file.LruFileCacheUtil.storeToLruCache;
class PCloudImpl {
private final PCloudIdCache idCache;
private final PCloudClientFactory clientFactory = new PCloudClientFactory();
private final PCloud cloud;
private final RootPCloudFolder root;
@ -77,7 +75,6 @@ class PCloudImpl {
this.context = context;
this.cloud = cloud;
this.idCache = new PCloudIdCache();
this.root = new RootPCloudFolder(cloud);
this.sharedPreferencesHandler = new SharedPreferencesHandler(context);
}
@ -102,83 +99,24 @@ class PCloudImpl {
return folder;
}
private Optional<RemoteEntry> findEntry(Long folderId, String name, boolean isFolder) throws IOException, BackendException {
if (folderId == null) {
throw new NoSuchCloudFileException();
}
try {
RemoteFolder remoteFolder = client().listFolder(folderId).execute();
for (RemoteEntry remoteEntry : remoteFolder.children()) {
if (isFolder) {
if (remoteEntry.isFolder() && remoteEntry.name().equals(name)) {
return Optional.of(remoteEntry);
}
} else {
if (remoteEntry.isFile() && remoteEntry.name().equals(name)) {
return Optional.of(remoteEntry);
}
}
}
return Optional.empty();
} catch(ApiError ex) {
Set<Integer> ignoredErrorCodes = new HashSet<>();
ignoredErrorCodes.add(PCloudApiError.PCloudApiErrorCodes.DIRECTORY_DOES_NOT_EXIST.getValue());
ignoredErrorCodes.add(PCloudApiError.PCloudApiErrorCodes.FILE_OR_FOLDER_NOT_FOUND.getValue());
ignoredErrorCodes.add(PCloudApiError.PCloudApiErrorCodes.FILE_NOT_FOUND.getValue());
handleApiError(ex, ignoredErrorCodes);
return Optional.empty();
}
}
public PCloudFile file(PCloudFolder parent, String name) throws BackendException, IOException {
return file(parent, name, Optional.empty());
}
public PCloudFile file(PCloudFolder parent, String name, Optional<Long> size) throws BackendException, IOException {
if (parent.getId() == null) {
return PCloudNodeFactory.file(parent, name, size);
}
String path = PCloudNodeFactory.getNodePath(parent, name);
PCloudIdCache.NodeInfo nodeInfo = idCache.get(path);
if (nodeInfo != null && !nodeInfo.isFolder()) {
return PCloudNodeFactory.file(parent, name, size, path, nodeInfo.getId());
}
Optional<RemoteEntry> file = findEntry(parent.getId(), name, false);
if (file.isPresent()) {
return idCache.cache(PCloudNodeFactory.file(parent, file.get().asFile()));
}
return PCloudNodeFactory.file(parent, name, size);
return PCloudNodeFactory.file(parent, name, size, parent.getPath() + "/" + name);
}
public PCloudFolder folder(PCloudFolder parent, String name) throws IOException, BackendException {
if (parent.getId() == null) {
return PCloudNodeFactory.folder(parent, name);
}
String path = PCloudNodeFactory.getNodePath(parent, name);
PCloudIdCache.NodeInfo nodeInfo = idCache.get(path);
if (nodeInfo != null && nodeInfo.isFolder()) {
return PCloudNodeFactory.folder(parent, name, path, nodeInfo.getId());
}
Optional<RemoteEntry> folder = findEntry(parent.getId(), name, true);
if (folder.isPresent()) {
return idCache.cache(PCloudNodeFactory.folder(parent, folder.get().asFolder()));
}
return PCloudNodeFactory.folder(parent, name);
return PCloudNodeFactory.folder(parent, name, parent.getPath() + "/" + name);
}
public boolean exists(PCloudNode node) throws IOException, BackendException {
try {
if (node instanceof PCloudFolder) {
RemoteFolder remoteFolder = client().listFolder(node.getPath()).execute();
idCache.add(PCloudNodeFactory.folder(node.getParent(), remoteFolder));
client().loadFolder(node.getPath()).execute();
} else {
RemoteFile remoteFile = client().loadFile(node.getPath()).execute();
idCache.add(PCloudNodeFactory.file(node.getParent(), remoteFile));
client().loadFile(node.getPath()).execute();
}
return true;
} catch (ApiError ex) {
@ -195,19 +133,11 @@ class PCloudImpl {
public List<PCloudNode> list(PCloudFolder folder) throws IOException, BackendException {
List<PCloudNode> result = new ArrayList<>();
Long folderId = folder.getId();
RemoteFolder listFolderResult;
try {
if (folderId == null) {
listFolderResult = client().listFolder(folder.getPath()).execute();
} else {
listFolderResult = client() //
.listFolder(folder.getId()) //
.execute();
}
RemoteFolder listFolderResult = client().listFolder(folder.getPath()).execute();
List<RemoteEntry> entryMetadata = listFolderResult.children();
for (RemoteEntry metadata : entryMetadata) {
result.add(idCache.cache(PCloudNodeFactory.from(folder, metadata)));
result.add(PCloudNodeFactory.from(folder, metadata));
}
return result;
} catch(ApiError ex) {
@ -217,20 +147,18 @@ class PCloudImpl {
}
public PCloudFolder create(PCloudFolder folder) throws IOException, BackendException {
if (folder.getParent().getId() == null) {
if (!exists(folder.getParent())) {
folder = new PCloudFolder( //
create(folder.getParent()), //
folder.getName(), folder.getPath(), folder.getId() //
//
folder.getName(), folder.getPath() //
);
}
try {
RemoteFolder createdFolder = client() //
.createFolder(folder.getParent().getId(), folder.getName()) //
.createFolder(folder.getPath()) //
.execute();
return idCache.cache( //
PCloudNodeFactory.folder(folder.getParent(), createdFolder));
return PCloudNodeFactory.folder(folder.getParent(), createdFolder);
} catch (ApiError ex) {
handleApiError(ex);
throw new FatalBackendException(ex);
@ -242,23 +170,12 @@ class PCloudImpl {
throw new CloudNodeAlreadyExistsException(target.getName());
}
RemoteEntry relocationResult;
try {
if (source instanceof PCloudFolder) {
relocationResult = client().moveFolder(source.getId(), target.getParent().getId()).execute();
if (!relocationResult.name().equals(target.getName())) {
relocationResult = client().renameFolder(relocationResult.asFolder(), target.getName()).execute();
}
return PCloudNodeFactory.from(target.getParent(), client().moveFolder(source.getPath(), target.getPath()).execute());
} else {
relocationResult = client().moveFile(source.getId(), target.getParent().getId()).execute();
if (!relocationResult.name().equals(target.getName())) {
relocationResult = client().renameFile(relocationResult.asFile(), target.getName()).execute();
}
return PCloudNodeFactory.from(target.getParent(), client().moveFile(source.getPath(), target.getPath()).execute());
}
idCache.remove(source);
return idCache.cache(PCloudNodeFactory.from(target.getParent(), relocationResult));
} catch(ApiError ex) {
handleApiError(ex);
throw new FatalBackendException(ex);
@ -271,13 +188,9 @@ class PCloudImpl {
throw new CloudNodeAlreadyExistsException("CloudNode already exists and replace is false");
}
if (file.getParent().getId() == null) {
throw new NoSuchCloudFileException(String.format("The parent folder of %s doesn't have a folderId. The file would remain in root folder", file.getPath()));
}
progressAware.onProgress(Progress.started(UploadState.upload(file)));
UploadOptions uploadOptions = UploadOptions.DEFAULT;
if (file.getId() != null && replace) {
if (replace) {
uploadOptions = UploadOptions.OVERRIDE_FILE;
}
@ -285,7 +198,8 @@ class PCloudImpl {
progressAware.onProgress(Progress.completed(UploadState.upload(file)));
return idCache.cache(PCloudNodeFactory.file(file.getParent(), uploadedFile));
return PCloudNodeFactory.file(file.getParent(), uploadedFile);
}
private RemoteFile uploadFile(final PCloudFile file, DataSource data, final ProgressAware<UploadState> progressAware, UploadOptions uploadOptions, final long size) //
@ -310,17 +224,12 @@ class PCloudImpl {
}
};
Long parentFolderId = file.getParent().getId();
if (parentFolderId == null) {
parentFolderId = idCache.get(file.getParent().getPath()).getId();
}
String filename = file.getName();
String encodedFilename = URLEncoder.encode(filename, UTF_8);
try {
RemoteFile newFile = client() //
.createFile(parentFolderId, encodedFilename, pCloudDataSource, new Date(), listener, uploadOptions) //
.createFile(file.getParent().getPath(), encodedFilename, pCloudDataSource, new Date(), listener, uploadOptions) //
.execute();
if (!filename.equals(encodedFilename)) {
return client().renameFile(newFile.fileId(), filename).execute();
@ -335,36 +244,17 @@ class PCloudImpl {
public void read(PCloudFile file, Optional<File> encryptedTmpFile, OutputStream data, final ProgressAware<DownloadState> progressAware) throws IOException, BackendException {
progressAware.onProgress(Progress.started(DownloadState.download(file)));
Long fileId = file.getId();
if (fileId == null) {
PCloudIdCache.NodeInfo nodeInfo = idCache.get(file.getPath());
if (nodeInfo != null) {
fileId = nodeInfo.getId();
}
}
RemoteFile remoteFile = null;
if (fileId == null) {
Optional<RemoteEntry> remoteEntryOptional = findEntry(file.getParent().getId(), file.getName(), false);
if (remoteEntryOptional.isPresent()) {
remoteFile = remoteEntryOptional.get().asFile();
fileId = remoteFile.fileId();
} else {
throw new NoSuchCloudFileException(file.getName());
}
}
Optional<String> cacheKey = Optional.empty();
Optional<File> cacheFile = Optional.empty();
RemoteFile remoteFile;
if (sharedPreferencesHandler.useLruCache() && createLruCache(sharedPreferencesHandler.lruCacheSize())) {
if (remoteFile == null) {
try {
remoteFile = client().loadFile(fileId).execute().asFile();
cacheKey = Optional.of(remoteFile.fileId() + remoteFile.hash());
} catch(ApiError ex) {
handleApiError(ex);
}
try {
remoteFile = client().loadFile(file.getPath()).execute().asFile();
cacheKey = Optional.of(remoteFile.fileId() + remoteFile.hash());
} catch(ApiError ex) {
handleApiError(ex);
}
File cachedFile = diskLruCache.get(cacheKey.get());
@ -376,23 +266,22 @@ class PCloudImpl {
retrieveFromLruCache(cacheFile.get(), data);
} catch (IOException e) {
Timber.tag("PCloudImpl").w(e, "Error while retrieving content from Cache, get from web request");
writeToData(file, fileId, data, encryptedTmpFile, cacheKey, progressAware);
writeToData(file, data, encryptedTmpFile, cacheKey, progressAware);
}
} else {
writeToData(file, fileId, data, encryptedTmpFile, cacheKey, progressAware);
writeToData(file, data, encryptedTmpFile, cacheKey, progressAware);
}
progressAware.onProgress(Progress.completed(DownloadState.download(file)));
}
private void writeToData(final PCloudFile file, //
final long fileId, //
final OutputStream data, //
final Optional<File> encryptedTmpFile, //
final Optional<String> cacheKey, //
final ProgressAware<DownloadState> progressAware) throws IOException, BackendException {
try {
FileLink fileLink = client().createFileLink(fileId, DownloadOptions.DEFAULT).execute();
FileLink fileLink = client().createFileLink(file.getPath(), DownloadOptions.DEFAULT).execute();
ProgressListener listener = (done, total) -> progressAware.onProgress( //
progress(DownloadState.download(file)) //
@ -426,12 +315,11 @@ class PCloudImpl {
try {
if (node instanceof PCloudFolder) {
client() //
.deleteFolder(node.getId(), true).execute();
.deleteFolder(node.getPath(), true).execute();
} else {
client() //
.deleteFile(node.getId()).execute();
.deleteFile(node.getPath()).execute();
}
idCache.remove(node);
} catch(ApiError ex) {
handleApiError(ex);
}

View File

@ -4,8 +4,6 @@ import org.cryptomator.domain.CloudNode;
interface PCloudNode extends CloudNode {
Long getId();
@Override
PCloudFolder getParent();

View File

@ -9,27 +9,27 @@ import org.cryptomator.util.Optional;
class PCloudNodeFactory {
public static PCloudFile file(PCloudFolder parent, RemoteFile file) {
return new PCloudFile(parent, file.name(), getNodePath(parent, file.name()), file.fileId(), Optional.ofNullable(file.size()), Optional.ofNullable(file.lastModified()));
return new PCloudFile(parent, file.name(), getNodePath(parent, file.name()), Optional.ofNullable(file.size()), Optional.ofNullable(file.lastModified()));
}
public static PCloudFile file(PCloudFolder parent, String name, Optional<Long> size) {
return new PCloudFile(parent, name, getNodePath(parent, name), null, size, Optional.empty());
return new PCloudFile(parent, name, getNodePath(parent, name), size, Optional.empty());
}
public static PCloudFile file(PCloudFolder folder, String name, Optional<Long> size, String path, Long fileId) {
return new PCloudFile(folder, name, path, fileId, size, Optional.empty());
public static PCloudFile file(PCloudFolder folder, String name, Optional<Long> size, String path) {
return new PCloudFile(folder, name, path, size, Optional.empty());
}
public static PCloudFolder folder(PCloudFolder parent, RemoteFolder folder) {
return new PCloudFolder(parent, folder.name(), getNodePath(parent, folder.name()), folder.folderId());
return new PCloudFolder(parent, folder.name(), getNodePath(parent, folder.name()));
}
public static PCloudFolder folder(PCloudFolder parent, String name) {
return new PCloudFolder(parent, name, getNodePath(parent, name), null);
return new PCloudFolder(parent, name, getNodePath(parent, name));
}
public static PCloudFolder folder(PCloudFolder parent, String name, String path, Long folderId) {
return new PCloudFolder(parent, name, path, folderId);
public static PCloudFolder folder(PCloudFolder parent, String name, String path) {
return new PCloudFolder(parent, name, path);
}
public static String getNodePath(PCloudFolder parent, String name) {

View File

@ -1,7 +1,5 @@
package org.cryptomator.data.cloud.pcloud;
import com.pcloud.sdk.RemoteFolder;
import org.cryptomator.domain.Cloud;
import org.cryptomator.domain.PCloud;
@ -10,7 +8,7 @@ class RootPCloudFolder extends PCloudFolder {
private final PCloud cloud;
public RootPCloudFolder(PCloud cloud) {
super(null, "", "", (long) RemoteFolder.ROOT_FOLDER_ID);
super(null, "", "");
this.cloud = cloud;
}