![JAR search and dependency download from the Maven repository](/logo.png)
co.easimart.CachedCurrentUserController Maven / Gradle / Ivy
package co.easimart;
import java.util.Arrays;
import java.util.Map;
import bolts.Continuation;
import bolts.Task;
/** package */ class CachedCurrentUserController implements EasimartCurrentUserController {
/**
* Lock used to synchronize current user modifications and access.
*
* Note about lock ordering:
*
* You must NOT acquire the EasimartUser instance mutex (the "mutex" field in EasimartObject) while
* holding this static initialization lock. Doing so will cause a deadlock. Here's an example:
* https://phabricator.fb.com/P17182641
*/
private final Object mutex = new Object();
private final TaskQueue taskQueue = new TaskQueue();
private final EasimartObjectStore store;
/* package */ EasimartUser currentUser;
// Whether currentUser is known to match the serialized version on disk. This is useful for saving
// a filesystem check if you try to load currentUser frequently while there is none on disk.
/* package */ boolean currentUserMatchesDisk = false;
public CachedCurrentUserController(EasimartObjectStore store) {
this.store = store;
}
@Override
public Task setAsync(final EasimartUser user) {
return taskQueue.enqueue(new Continuation>() {
@Override
public Task then(Task toAwait) throws Exception {
return toAwait.continueWithTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
EasimartUser oldCurrentUser;
synchronized (mutex) {
oldCurrentUser = currentUser;
}
if (oldCurrentUser != null && oldCurrentUser != user) {
// We don't need to revoke the token since we're not explicitly calling logOut
// We don't need to remove persisted files since we're overwriting them
return oldCurrentUser.logOutAsync(false).continueWith(new Continuation() {
@Override
public Void then(Task task) throws Exception {
return null; // ignore errors
}
});
}
return task;
}
}).onSuccessTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
user.setIsCurrentUser(true);
return user.synchronizeAllAuthDataAsync();
}
}).onSuccessTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
return store.setAsync(user).continueWith(new Continuation() {
@Override
public Void then(Task task) throws Exception {
synchronized (mutex) {
currentUserMatchesDisk = !task.isFaulted();
currentUser = user;
}
return null;
}
});
}
});
}
});
}
@Override
public Task setIfNeededAsync(EasimartUser user) {
synchronized (mutex) {
if (!user.isCurrentUser() || currentUserMatchesDisk) {
return Task.forResult(null);
}
}
return setAsync(user);
}
@Override
public Task getAsync() {
return getAsync(EasimartUser.isAutomaticUserEnabled());
}
@Override
public Task existsAsync() {
synchronized (mutex) {
if (currentUser != null) {
return Task.forResult(true);
}
}
return taskQueue.enqueue(new Continuation>() {
@Override
public Task then(Task toAwait) throws Exception {
return toAwait.continueWithTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
return store.existsAsync();
}
});
}
});
}
@Override
public boolean isCurrent(EasimartUser user) {
synchronized (mutex) {
return currentUser == user;
}
}
@Override
public void clearFromMemory() {
synchronized (mutex) {
currentUser = null;
currentUserMatchesDisk = false;
}
}
@Override
public void clearFromDisk() {
synchronized (mutex) {
currentUser = null;
currentUserMatchesDisk = false;
}
try {
EasimartTaskUtils.wait(store.deleteAsync());
} catch (EasimartException e) {
// ignored
}
}
@Override
public Task getCurrentSessionTokenAsync() {
return getAsync(false).onSuccess(new Continuation() {
@Override
public String then(Task task) throws Exception {
EasimartUser user = task.getResult();
return user != null ? user.getSessionToken() : null;
}
});
}
@Override
public Task logOutAsync() {
return taskQueue.enqueue(new Continuation>() {
@Override
public Task then(Task toAwait) throws Exception {
// We can parallelize disk and network work, but only after we restore the current user from
// disk.
final Task userTask = getAsync(false);
return Task.whenAll(Arrays.asList(userTask, toAwait)).continueWithTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
Task logOutTask = userTask.onSuccessTask(new Continuation>() {
@Override
public Task then(Task task) throws Exception {
EasimartUser user = task.getResult();
if (user == null) {
return task.cast();
}
return user.logOutAsync();
}
});
Task diskTask = store.deleteAsync().continueWith(new Continuation() {
@Override
public Void then(Task task) throws Exception {
boolean deleted = !task.isFaulted();
synchronized (mutex) {
currentUserMatchesDisk = deleted;
currentUser = null;
}
return null;
}
});
return Task.whenAll(Arrays.asList(logOutTask, diskTask));
}
});
}
});
}
@Override
public Task getAsync(final boolean shouldAutoCreateUser) {
synchronized (mutex) {
if (currentUser != null) {
return Task.forResult(currentUser);
}
}
return taskQueue.enqueue(new Continuation>() {
@Override
public Task then(Task toAwait) throws Exception {
return toAwait.continueWithTask(new Continuation>() {
@Override
public Task then(Task ignored) throws Exception {
EasimartUser current;
boolean matchesDisk;
synchronized (mutex) {
current = currentUser;
matchesDisk = currentUserMatchesDisk;
}
if (current != null) {
return Task.forResult(current);
}
if (matchesDisk) {
if (shouldAutoCreateUser) {
return Task.forResult(lazyLogIn());
}
return null;
}
return store.getAsync().continueWith(new Continuation() {
@Override
public EasimartUser then(Task task) throws Exception {
EasimartUser current = task.getResult();
boolean matchesDisk = !task.isFaulted();
synchronized (mutex) {
currentUser = current;
currentUserMatchesDisk = matchesDisk;
}
if (current != null) {
synchronized (current.mutex) {
current.setIsCurrentUser(true);
}
return current;
}
if (shouldAutoCreateUser) {
return lazyLogIn();
}
return null;
}
});
}
});
}
});
}
private EasimartUser lazyLogIn() {
Map authData = EasimartAnonymousUtils.getAuthData();
return lazyLogIn(EasimartAnonymousUtils.AUTH_TYPE, authData);
}
/* package for tests */ EasimartUser lazyLogIn(String authType, Map authData) {
// Note: if authType != EasimartAnonymousUtils.AUTH_TYPE the user is not "lazy".
EasimartUser user = EasimartObject.create(EasimartUser.class);
synchronized (user.mutex) {
user.setIsCurrentUser(true);
user.putAuthData(authType, authData);
}
synchronized (mutex) {
currentUserMatchesDisk = false;
currentUser = user;
}
return user;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy