use of io.realm.internal.async.RealmAsyncTaskImpl in project realm-java by realm.
the class SyncUser method loginAsync.
/**
* Logs in the user to the Realm Object Server. A logged in user is required to be able to create a
* {@link SyncConfiguration}.
*
* @param credentials credentials to use.
* @param authenticationUrl server that the user is authenticated against.
* @param callback callback when login has completed or failed. The callback will always happen on the same thread
* as this this method is called on.
* @throws IllegalArgumentException if not on a Looper thread.
*/
public static RealmAsyncTask loginAsync(final SyncCredentials credentials, final String authenticationUrl, final Callback callback) {
if (Looper.myLooper() == null) {
throw new IllegalStateException("Asynchronous login is only possible from looper threads.");
}
final Handler handler = new Handler(Looper.myLooper());
ThreadPoolExecutor networkPoolExecutor = SyncManager.NETWORK_POOL_EXECUTOR;
Future<?> authenticateRequest = networkPoolExecutor.submit(new Runnable() {
@Override
public void run() {
try {
SyncUser user = login(credentials, authenticationUrl);
postSuccess(user);
} catch (ObjectServerError e) {
postError(e);
}
}
private void postError(final ObjectServerError error) {
if (callback != null) {
handler.post(new Runnable() {
@Override
public void run() {
try {
callback.onError(error);
} catch (Exception e) {
RealmLog.info("onError has thrown an exception but is ignoring it: %s", Util.getStackTrace(e));
}
}
});
}
}
private void postSuccess(final SyncUser user) {
if (callback != null) {
handler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(user);
}
});
}
}
});
return new RealmAsyncTaskImpl(authenticateRequest, networkPoolExecutor);
}
use of io.realm.internal.async.RealmAsyncTaskImpl in project realm-java by realm.
the class RealmCache method doCreateRealmOrGetFromCacheAsync.
private synchronized <T extends BaseRealm> RealmAsyncTask doCreateRealmOrGetFromCacheAsync(RealmConfiguration configuration, BaseRealm.InstanceCallback<T> callback, Class<T> realmClass) {
Capabilities capabilities = new AndroidCapabilities();
capabilities.checkCanDeliverNotification(ASYNC_NOT_ALLOWED_MSG);
// noinspection ConstantConditions
if (callback == null) {
throw new IllegalArgumentException(ASYNC_CALLBACK_NULL_MSG);
}
// If there is no Realm file it means that we need to sync the initial remote data in the worker thread.
if (configuration.isSyncConfiguration() && !configuration.realmExists()) {
pendingRealmFileCreation.add(configuration.getPath());
}
// Always create a Realm instance in the background thread even when there are instances existing on current
// thread. This to ensure that onSuccess will always be called in the following event loop but not current one.
CreateRealmRunnable<T> createRealmRunnable = new CreateRealmRunnable<T>(new AndroidRealmNotifier(null, capabilities), configuration, callback, realmClass);
Future<?> future = BaseRealm.asyncTaskExecutor.submitTransaction(createRealmRunnable);
createRealmRunnable.setFuture(future);
// For Realms using Async Open on the server, we need to create the session right away
// in order to interact with it in a imperative way, e.g. by attaching download progress
// listeners
ObjectServerFacade.getSyncFacadeIfPossible().createNativeSyncSession(configuration);
return new RealmAsyncTaskImpl(future, BaseRealm.asyncTaskExecutor);
}
use of io.realm.internal.async.RealmAsyncTaskImpl in project realm-java by realm.
the class DynamicRealm method executeTransactionAsync.
/**
* Similar to {@link #executeTransactionAsync(Transaction)}, but also accepts an OnSuccess and OnError callbacks.
*
* @param transaction {@link Transaction} to execute.
* @param onSuccess callback invoked when the transaction succeeds.
* @param onError callback invoked when the transaction fails.
* @return a {@link RealmAsyncTask} representing a cancellable task.
* @throws IllegalArgumentException if the {@code transaction} is {@code null}, or if the realm is opened from
* another thread.
*/
public RealmAsyncTask executeTransactionAsync(final Transaction transaction, @Nullable final Transaction.OnSuccess onSuccess, @Nullable final Transaction.OnError onError) {
checkIfValid();
// noinspection ConstantConditions
if (transaction == null) {
throw new IllegalArgumentException("Transaction should not be null");
}
if (isFrozen()) {
throw new IllegalStateException("Write transactions on a frozen Realm is not allowed.");
}
// Avoid to call canDeliverNotification() in bg thread.
final boolean canDeliverNotification = sharedRealm.capabilities.canDeliverNotification();
// the results.
if ((onSuccess != null || onError != null)) {
sharedRealm.capabilities.checkCanDeliverNotification("Callback cannot be delivered on current thread.");
}
// We need to use the same configuration to open a background OsSharedRealm (i.e Realm)
// to perform the transaction
final RealmConfiguration realmConfiguration = getConfiguration();
// We need to deliver the callback even if the Realm is closed. So acquire a reference to the notifier here.
final RealmNotifier realmNotifier = sharedRealm.realmNotifier;
final Future<?> pendingTransaction = asyncTaskExecutor.submitTransaction(new Runnable() {
@Override
public void run() {
if (Thread.currentThread().isInterrupted()) {
return;
}
OsSharedRealm.VersionID versionID = null;
Throwable exception = null;
final DynamicRealm bgRealm = DynamicRealm.getInstance(realmConfiguration);
bgRealm.beginTransaction();
try {
transaction.execute(bgRealm);
if (Thread.currentThread().isInterrupted()) {
return;
}
bgRealm.commitTransaction();
// The bgRealm needs to be closed before post event to caller's handler to avoid concurrency
// problem. This is currently guaranteed by posting callbacks later below.
versionID = bgRealm.sharedRealm.getVersionID();
} catch (final Throwable e) {
exception = e;
} finally {
try {
if (bgRealm.isInTransaction()) {
bgRealm.cancelTransaction();
}
} finally {
bgRealm.close();
}
}
final Throwable backgroundException = exception;
final OsSharedRealm.VersionID backgroundVersionID = versionID;
// Cannot be interrupted anymore.
if (canDeliverNotification) {
if (backgroundVersionID != null && onSuccess != null) {
realmNotifier.post(new Runnable() {
@Override
public void run() {
if (isClosed()) {
// The caller Realm is closed. Just call the onSuccess. Since the new created Realm
// cannot be behind the background one.
onSuccess.onSuccess();
return;
}
if (sharedRealm.getVersionID().compareTo(backgroundVersionID) < 0) {
sharedRealm.realmNotifier.addTransactionCallback(new Runnable() {
@Override
public void run() {
onSuccess.onSuccess();
}
});
} else {
onSuccess.onSuccess();
}
}
});
} else if (backgroundException != null) {
realmNotifier.post(new Runnable() {
@Override
public void run() {
if (onError != null) {
onError.onError(backgroundException);
} else {
throw new RealmException("Async transaction failed", backgroundException);
}
}
});
}
} else {
if (backgroundException != null) {
// Throw in the worker thread since the caller thread cannot get notifications.
throw new RealmException("Async transaction failed", backgroundException);
}
}
}
});
return new RealmAsyncTaskImpl(pendingTransaction, asyncTaskExecutor);
}
use of io.realm.internal.async.RealmAsyncTaskImpl in project realm-java by realm.
the class SyncSession method scheduleRefreshAccessToken.
private void scheduleRefreshAccessToken(final AuthenticationServer authServer, long expireDateInMs) {
// calculate the delay time before which we should refresh the access_token,
// we adjust to 10 second to proactively refresh the access_token before the session
// hit the expire date on the token
long refreshAfter = expireDateInMs - System.currentTimeMillis() - REFRESH_MARGIN_DELAY;
if (refreshAfter < 0) {
// Token already expired
RealmLog.debug("Expires time already reached for the access token, refresh as soon as possible");
// we avoid refreshing directly to avoid an edge case where the client clock is ahead
// of the server, causing all access_token received from the server to be always
// expired, we will flood the server with refresh token requests then, so adding
// a bit of delay is the best effort in this case.
refreshAfter = REFRESH_MARGIN_DELAY;
}
RealmLog.debug("Scheduling an access_token refresh in " + (refreshAfter) + " milliseconds");
if (refreshTokenTask != null) {
refreshTokenTask.cancel();
}
ScheduledFuture<?> task = REFRESH_TOKENS_EXECUTOR.schedule(new Runnable() {
@Override
public void run() {
if (!isClosed && !Thread.currentThread().isInterrupted()) {
refreshAccessToken(authServer);
}
}
}, refreshAfter, TimeUnit.MILLISECONDS);
refreshTokenTask = new RealmAsyncTaskImpl(task, REFRESH_TOKENS_EXECUTOR);
}
use of io.realm.internal.async.RealmAsyncTaskImpl in project realm-java by realm.
the class SyncSession method authenticateRealm.
// Authenticate by getting access tokens for the specific Realm
private void authenticateRealm(final AuthenticationServer authServer) {
if (networkRequest != null) {
networkRequest.cancel();
}
clearScheduledAccessTokenRefresh();
// Authenticate in a background thread. This allows incremental backoff and retries in a safe manner.
Future<?> task = SyncManager.NETWORK_POOL_EXECUTOR.submit(new ExponentialBackoffTask<AuthenticateResponse>() {
@Override
protected AuthenticateResponse execute() {
if (!isClosed && !Thread.currentThread().isInterrupted()) {
return authServer.loginToRealm(//refresh token in fact
getUser().getAccessToken(), configuration.getServerUrl(), getUser().getSyncUser().getAuthenticationUrl());
}
return null;
}
@Override
protected void onSuccess(AuthenticateResponse response) {
RealmLog.debug("Session[%s]: Access token acquired", configuration.getPath());
if (!isClosed && !Thread.currentThread().isInterrupted()) {
ObjectServerUser.AccessDescription desc = new ObjectServerUser.AccessDescription(response.getAccessToken(), configuration.getPath(), configuration.shouldDeleteRealmOnLogout());
getUser().getSyncUser().addRealm(configuration.getServerUrl(), desc);
// schedule a token refresh before it expires
if (nativeRefreshAccessToken(configuration.getPath(), getUser().getSyncUser().getAccessToken(configuration.getServerUrl()).value(), configuration.getServerUrl().toString())) {
scheduleRefreshAccessToken(authServer, response.getAccessToken().expiresMs());
} else {
// token not applied, no refresh will be scheduled
onGoingAccessTokenQuery.set(false);
}
}
}
@Override
protected void onError(AuthenticateResponse response) {
onGoingAccessTokenQuery.set(false);
RealmLog.debug("Session[%s]: Failed to get access token (%d)", configuration.getPath(), response.getError().getErrorCode());
if (!isClosed && !Thread.currentThread().isInterrupted()) {
errorHandler.onError(SyncSession.this, response.getError());
}
}
});
networkRequest = new RealmAsyncTaskImpl(task, SyncManager.NETWORK_POOL_EXECUTOR);
}
Aggregations