X Tutup
Skip to content

Commit 28a2ddd

Browse files
committed
Merge branch 'master' into nh/android_support_ssl
2 parents 0d0ade8 + a31f0a4 commit 28a2ddd

File tree

19 files changed

+570
-32
lines changed

19 files changed

+570
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
* [ObjectServer] Added support for `SyncUser.isAdmin()` (#4353).
88
* [ObjectServer] Added support for changing passwords through `SyncUser.changePassword()` (#4423).
9+
* [ObjectServer] Added support for `SyncConfigration.Builder.waitForInitialRemoteData()` (#4270).
910
* Transient fields are now allowed in model classes, but are implicitly treated as having the `@Ignore` annotation (#4279).
1011
* Added `Realm.refresh()` and `DynamicRealm.refresh()` (#3476).
1112
* Added `Realm.getInstanceAsync()` and `DynamicRealm.getInstanceAsync()` (#2299).

realm/realm-library/src/androidTest/java/io/realm/internal/RealmNotifierTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public boolean canDeliverNotification() {
5454
@Override
5555
public void checkCanDeliverNotification(String exceptionMessage) {
5656
}
57+
58+
@Override
59+
public boolean isMainThread() {
60+
return false;
61+
}
5762
};
5863

5964
@Before

realm/realm-library/src/main/cpp/io_realm_SyncSession.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@
2222
#include "object-store/src/sync/sync_session.hpp"
2323

2424
#include "util.hpp"
25+
#include "jni_util/java_global_ref.hpp"
26+
#include "jni_util/java_method.hpp"
27+
#include "jni_util/java_class.hpp"
28+
#include "jni_util/java_local_ref.hpp"
29+
#include "jni_util/jni_utils.hpp"
2530

2631
using namespace std;
2732
using namespace realm;
33+
using namespace jni_util;
2834
using namespace sync;
2935

3036
JNIEXPORT jboolean JNICALL Java_io_realm_SyncSession_nativeRefreshAccessToken(JNIEnv* env, jclass,
@@ -43,7 +49,42 @@ JNIEXPORT jboolean JNICALL Java_io_realm_SyncSession_nativeRefreshAccessToken(JN
4349
return JNI_TRUE;
4450
}
4551
else {
46-
realm::jni_util::Log::d("no active/inactive session found");
52+
Log::d("no active/inactive session found");
53+
}
54+
}
55+
CATCH_STD()
56+
return JNI_FALSE;
57+
}
58+
59+
JNIEXPORT jboolean JNICALL Java_io_realm_SyncSession_nativeWaitForDownloadCompletion(JNIEnv* env,
60+
jobject session_object,
61+
jstring localRealmPath)
62+
{
63+
TR_ENTER()
64+
try {
65+
JStringAccessor local_realm_path(env, localRealmPath);
66+
auto session = SyncManager::shared().get_existing_session(local_realm_path);
67+
68+
if (session) {
69+
static JavaClass java_sync_session_class(env, "io/realm/SyncSession");
70+
static JavaMethod java_notify_result_method(env, java_sync_session_class, "notifyAllChangesDownloaded",
71+
"(Ljava/lang/Long;Ljava/lang/String;)V");
72+
JavaGlobalRef java_session_object_ref(env, session_object);
73+
74+
bool listener_registered =
75+
session->wait_for_download_completion([java_session_object_ref](std::error_code error) {
76+
JNIEnv* env = JniUtils::get_env(true);
77+
jobject java_error_code = nullptr;
78+
jstring java_error_message = nullptr;
79+
if (error != std::error_code{}) {
80+
java_error_code = NewLong(env, error.value());
81+
java_error_message = env->NewStringUTF(error.message().c_str());
82+
}
83+
env->CallVoidMethod(java_session_object_ref.get(), java_notify_result_method, java_error_code,
84+
java_error_message);
85+
});
86+
87+
return to_jbool(listener_registered);
4788
}
4889
}
4990
CATCH_STD()

realm/realm-library/src/main/cpp/jni_util/java_class.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ JavaGlobalRef JavaClass::get_jclass(JNIEnv* env, const char* class_name)
3636
jclass cls = env->FindClass(class_name);
3737
REALM_ASSERT_DEBUG(cls);
3838

39-
JavaGlobalRef cls_ref(env, cls);
40-
env->DeleteLocalRef(cls);
39+
JavaGlobalRef cls_ref(env, cls, true);
4140
return cls_ref;
4241
}

realm/realm-library/src/main/cpp/jni_util/java_global_ref.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@ JavaGlobalRef& JavaGlobalRef::operator=(JavaGlobalRef&& rhs)
3434
new (this) JavaGlobalRef(std::move(rhs));
3535
return *this;
3636
}
37+
38+
JavaGlobalRef::JavaGlobalRef(JavaGlobalRef& rhs)
39+
: m_ref(rhs.m_ref ? jni_util::JniUtils::get_env(true)->NewGlobalRef(rhs.m_ref) : nullptr)
40+
{
41+
}

realm/realm-library/src/main/cpp/jni_util/java_global_ref.hpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,18 @@ class JavaGlobalRef {
4545
~JavaGlobalRef();
4646

4747
JavaGlobalRef& operator=(JavaGlobalRef&& rhs);
48+
JavaGlobalRef(JavaGlobalRef&);
4849

4950
inline operator bool() const noexcept
5051
{
5152
return m_ref != nullptr;
5253
}
5354

54-
inline jobject get() noexcept
55+
inline jobject get() const noexcept
5556
{
5657
return m_ref;
5758
}
5859

59-
// Not implemented for now.
60-
JavaGlobalRef(JavaGlobalRef&) = delete;
61-
6260
private:
6361
jobject m_ref;
6462
};

realm/realm-library/src/main/cpp/jni_util/java_local_ref.hpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,24 @@ class JavaLocalRef {
4141
inline JavaLocalRef(JNIEnv* env, T obj, NeedToCreateLocalRef) noexcept
4242
: m_jobject(env->NewLocalRef(obj))
4343
, m_env(env){};
44+
4445
inline ~JavaLocalRef()
4546
{
4647
m_env->DeleteLocalRef(m_jobject);
4748
}
4849

49-
JavaLocalRef(const JavaLocalRef&) = delete;
50-
JavaLocalRef& operator=(const JavaLocalRef&) = delete;
51-
JavaLocalRef(JavaLocalRef&& rhs) = delete;
52-
JavaLocalRef& operator=(JavaLocalRef&& rhs) = delete;
50+
JavaLocalRef& operator=(JavaLocalRef&& rhs)
51+
{
52+
this->~JavaLocalRef();
53+
new (this) JavaLocalRef(std::move(rhs));
54+
return *this;
55+
}
56+
57+
inline JavaLocalRef(JavaLocalRef&& rhs)
58+
: m_env(rhs.m_env), m_jobject(rhs.m_jobject)
59+
{
60+
rhs.m_jobject = nullptr;
61+
}
5362

5463
inline operator bool() const noexcept
5564
{
@@ -59,6 +68,13 @@ class JavaLocalRef {
5968
{
6069
return m_jobject;
6170
}
71+
inline T get() const noexcept
72+
{
73+
return m_jobject;
74+
};
75+
76+
JavaLocalRef(const JavaLocalRef&) = delete;
77+
JavaLocalRef& operator=(const JavaLocalRef&) = delete;
6278

6379
private:
6480
T m_jobject;

realm/realm-library/src/main/java/io/realm/Realm.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ private static void checkFilesDirAvailable(Context context) {
260260
* @throws RealmMigrationNeededException if no migration has been provided by the default configuration and the
261261
* RealmObject classes or version has has changed so a migration is required.
262262
* @throws RealmFileException if an error happened when accessing the underlying Realm file.
263+
* @throws io.realm.exceptions.DownloadingRealmInterruptedException if {@link SyncConfiguration.Builder#waitForInitialRemoteData()}
264+
* was set and the thread opening the Realm was interrupted while the download was in progress.
263265
*/
264266
public static Realm getDefaultInstance() {
265267
if (defaultConfiguration == null) {
@@ -277,6 +279,8 @@ public static Realm getDefaultInstance() {
277279
* classes or version has has changed so a migration is required.
278280
* @throws RealmFileException if an error happened when accessing the underlying Realm file.
279281
* @throws IllegalArgumentException if a null {@link RealmConfiguration} is provided.
282+
* @throws io.realm.exceptions.DownloadingRealmInterruptedException if {@link SyncConfiguration.Builder#waitForInitialRemoteData()}
283+
* was set and the thread opening the Realm was interrupted while the download was in progress.
280284
* @see RealmConfiguration for details on how to configure a Realm.
281285
*/
282286
public static Realm getInstance(RealmConfiguration configuration) {

realm/realm-library/src/main/java/io/realm/RealmCache.java

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@
2626
import java.util.Collection;
2727
import java.util.EnumMap;
2828
import java.util.Iterator;
29-
import java.util.concurrent.CountDownLatch;
30-
import java.util.concurrent.Future;
31-
import java.util.concurrent.TimeUnit;
3229
import java.util.LinkedList;
3330
import java.util.List;
3431
import java.util.concurrent.ConcurrentLinkedQueue;
32+
import java.util.concurrent.CountDownLatch;
33+
import java.util.concurrent.Future;
34+
import java.util.concurrent.TimeUnit;
3535
import java.util.concurrent.atomic.AtomicBoolean;
3636

3737
import io.realm.exceptions.RealmFileException;
@@ -113,6 +113,7 @@ public void setFuture(Future future) {
113113
public void run() {
114114
T instance = null;
115115
try {
116+
// First call that will run all schema validation, migrations or initial transactions.
116117
instance = createRealmOrGetFromCache(configuration, realmClass);
117118
boolean results = notifier.post(new Runnable() {
118119
@Override
@@ -130,6 +131,9 @@ public void run() {
130131
T instanceToReturn = null;
131132
Throwable throwable = null;
132133
try {
134+
// This will run on the caller thread, but since the first `createRealmOrGetFromCache`
135+
// should have completed at this point, all expensive initializer functions have already
136+
// run.
133137
instanceToReturn = createRealmOrGetFromCache(configuration, realmClass);
134138
} catch (Throwable e) {
135139
throwable = e;
@@ -154,13 +158,18 @@ public void run() {
154158
} catch (InterruptedException e) {
155159
RealmLog.warn(e, "`CreateRealmRunnable` has been interrupted.");
156160
} catch (final Throwable e) {
157-
RealmLog.error(e, "`CreateRealmRunnable` failed.");
158-
notifier.post(new Runnable() {
159-
@Override
160-
public void run() {
161-
callback.onError(e);
162-
}
163-
});
161+
// DownloadingRealmInterruptedException is treated specially.
162+
// It async open is canceled, this could interrupt the download, but the user should
163+
// not care in this case, so just ignore it.
164+
if (!ObjectServerFacade.getSyncFacadeIfPossible().wasDownloadInterrupted(e)) {
165+
RealmLog.error(e, "`CreateRealmRunnable` failed.");
166+
notifier.post(new Runnable() {
167+
@Override
168+
public void run() {
169+
callback.onError(e);
170+
}
171+
});
172+
}
164173
} finally {
165174
if (instance != null) {
166175
instance.close();
@@ -283,10 +292,30 @@ private synchronized <E extends BaseRealm> E doCreateRealmOrGetFromCache(RealmCo
283292

284293
if (getTotalGlobalRefCount() == 0) {
285294
copyAssetFileIfNeeded(configuration);
295+
boolean fileExists = configuration.realmExists();
286296

287297
SharedRealm sharedRealm = null;
288298
try {
289299
sharedRealm = SharedRealm.getInstance(configuration);
300+
301+
// If waitForInitialRemoteData() was enabled, we need to make sure that all data is downloaded
302+
// before proceeding. We need to open the Realm instance first to start any potential underlying
303+
// SyncSession so this will work. TODO: This needs to be decoupled.
304+
if (!fileExists) {
305+
try {
306+
ObjectServerFacade.getSyncFacadeIfPossible().downloadRemoteChanges(configuration);
307+
} catch (Throwable t) {
308+
// If an error happened while downloading initial data, we need to reset the file so we can
309+
// download it again on the next attempt.
310+
// Realm.deleteRealm() is under the same lock as this method and globalCount is still 0, so
311+
// this should be safe.
312+
sharedRealm.close();
313+
sharedRealm = null;
314+
Realm.deleteRealm(configuration);
315+
throw t;
316+
}
317+
}
318+
290319
if (Table.primaryKeyTableNeedsMigration(sharedRealm)) {
291320
sharedRealm.beginTransaction();
292321
if (Table.migratePrimaryKeyTableIfNeeded(sharedRealm)) {
@@ -494,6 +523,8 @@ synchronized void invokeWithLock(Callback0 callback) {
494523
* Copies Realm database file from Android asset directory to the directory given in the {@link RealmConfiguration}.
495524
* Copy is performed only at the first time when there is no Realm database file.
496525
*
526+
* WARNING: This method is not thread-safe so external synchronization is required before using it.
527+
*
497528
* @param configuration configuration object for Realm instance.
498529
* @throws RealmFileException if copying the file fails.
499530
*/

realm/realm-library/src/main/java/io/realm/RealmConfiguration.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,27 @@ public Set<Class<? extends RealmModel>> getRealmObjectClasses() {
198198
return schemaMediator.getModelClasses();
199199
}
200200

201+
/**
202+
* Returns the absolute path to where the Realm file will be saved.
203+
*
204+
* @return the absolute path to the Realm file defined by this configuration.
205+
*/
201206
public String getPath() {
202207
return canonicalPath;
203208
}
204209

210+
/**
211+
* Checks if the Realm file defined by this configuration already exists.
212+
*
213+
* WARNING: This method is just a point-in-time check. Unless protected by external synchronization another
214+
* thread or process might have created or deleted the Realm file right after this method has returned.
215+
*
216+
* @return {@code true} if the Realm file exists, {@code false} otherwise.
217+
*/
218+
boolean realmExists() {
219+
return new File(canonicalPath).exists();
220+
}
221+
205222
/**
206223
* Returns the {@link RxObservableFactory} that is used to create Rx Observables from Realm objects.
207224
*

0 commit comments

Comments
 (0)
X Tutup