Skip to content

Commit a2a834a

Browse files
Merge branch '818-android-jni-crash-query-find' into 'dev'
Throw helpful error if query is closed See merge request objectbox/objectbox-java!112
2 parents 875fcde + 9d5e605 commit a2a834a

File tree

7 files changed

+301
-34
lines changed

7 files changed

+301
-34
lines changed

objectbox-java/src/main/java/io/objectbox/BoxStore.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ public static boolean isSyncServerAvailable() {
232232
/** Set when running inside TX */
233233
final ThreadLocal<Transaction> activeTx = new ThreadLocal<>();
234234

235-
private boolean closed;
235+
// volatile so checkOpen() is more up-to-date (no need for synchronized; it's a race anyway)
236+
volatile private boolean closed;
236237

237238
final Object txCommitCountLock = new Object();
238239

@@ -1067,6 +1068,7 @@ public long validate(long pageLimit, boolean checkLeafLevel) {
10671068
}
10681069

10691070
public int cleanStaleReadTransactions() {
1071+
checkOpen();
10701072
return nativeCleanStaleReadTransactions(handle);
10711073
}
10721074

@@ -1100,6 +1102,7 @@ long internalHandle() {
11001102
* Note that failed or aborted transaction do not trigger observers.
11011103
*/
11021104
public SubscriptionBuilder<Class> subscribe() {
1105+
checkOpen();
11031106
return new SubscriptionBuilder<>(objectClassPublisher, null);
11041107
}
11051108

@@ -1109,6 +1112,7 @@ public SubscriptionBuilder<Class> subscribe() {
11091112
*/
11101113
@SuppressWarnings("unchecked")
11111114
public <T> SubscriptionBuilder<Class<T>> subscribe(Class<T> forClass) {
1115+
checkOpen();
11121116
return new SubscriptionBuilder<>((DataPublisher) objectClassPublisher, forClass);
11131117
}
11141118

@@ -1246,9 +1250,7 @@ long panicModeRemoveAllObjects(int entityId) {
12461250
* Note: Once you {@link #close()} this BoxStore, do not use it from the C API.
12471251
*/
12481252
public long getNativeStore() {
1249-
if (closed) {
1250-
throw new IllegalStateException("Store must still be open");
1251-
}
1253+
checkOpen();
12521254
return handle;
12531255
}
12541256

objectbox-java/src/main/java/io/objectbox/query/Query.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ native void nativeSetParameter(long handle, int entityId, int propertyId, @Nulla
110110
private final int queryAttempts;
111111
private static final int INITIAL_RETRY_BACK_OFF_IN_MS = 10;
112112

113-
long handle;
113+
// volatile so checkOpen() is more up-to-date (no need for synchronized; it's a race anyway)
114+
volatile long handle;
114115

115116
Query(Box<T> box, long queryHandle, @Nullable List<EagerRelation<T, ?>> eagerRelations, @Nullable QueryFilter<T> filter,
116117
@Nullable Comparator<T> comparator) {
@@ -135,7 +136,12 @@ protected void finalize() throws Throwable {
135136
}
136137

137138
/**
138-
* If possible, try to close the query once you are done with it to reclaim resources immediately.
139+
* Closes this query and frees used resources.
140+
* <p>
141+
* If possible, call this always once done with this. Otherwise, will be called once this is finalized (e.g. garbage
142+
* collected).
143+
* <p>
144+
* Calling any other methods of this afterwards will throw an {@link IllegalStateException}.
139145
*/
140146
public synchronized void close() {
141147
if (handle != 0) {
@@ -256,6 +262,7 @@ public long[] findIds() {
256262
*/
257263
@Nonnull
258264
public long[] findIds(final long offset, final long limit) {
265+
checkOpen();
259266
return box.internalCallWithReaderHandle(cursorHandle -> nativeFindIds(handle, cursorHandle, offset, limit));
260267
}
261268

@@ -294,6 +301,7 @@ public PropertyQuery property(Property<T> property) {
294301
}
295302

296303
<R> R callInReadTx(Callable<R> callable) {
304+
checkOpen();
297305
return store.callInReadTxWithRetry(callable, queryAttempts, INITIAL_RETRY_BACK_OFF_IN_MS, true);
298306
}
299307

@@ -308,6 +316,7 @@ <R> R callInReadTx(Callable<R> callable) {
308316
*/
309317
public void forEach(final QueryConsumer<T> consumer) {
310318
ensureNoComparator();
319+
checkOpen(); // findIds also checks, but throw early outside of transaction.
311320
box.getStore().runInReadTx(() -> {
312321
LazyList<T> lazyList = new LazyList<>(box, findIds(), false);
313322
int size = lazyList.size();
@@ -384,6 +393,7 @@ void resolveEagerRelation(@Nonnull T entity, EagerRelation<T, ?> eagerRelation)
384393

385394
/** Returns the count of Objects matching the query. */
386395
public long count() {
396+
checkOpen();
387397
ensureNoFilter();
388398
return box.internalCallWithReaderHandle(cursorHandle -> nativeCount(handle, cursorHandle));
389399
}
@@ -392,6 +402,7 @@ public long count() {
392402
* Sets a parameter previously given to the {@link QueryBuilder} to a new value.
393403
*/
394404
public Query<T> setParameter(Property<?> property, String value) {
405+
checkOpen();
395406
nativeSetParameter(handle, property.getEntityId(), property.getId(), null, value);
396407
return this;
397408
}
@@ -402,6 +413,7 @@ public Query<T> setParameter(Property<?> property, String value) {
402413
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
403414
*/
404415
public Query<T> setParameter(String alias, String value) {
416+
checkOpen();
405417
nativeSetParameter(handle, 0, 0, alias, value);
406418
return this;
407419
}
@@ -410,6 +422,7 @@ public Query<T> setParameter(String alias, String value) {
410422
* Sets a parameter previously given to the {@link QueryBuilder} to a new value.
411423
*/
412424
public Query<T> setParameter(Property<?> property, long value) {
425+
checkOpen();
413426
nativeSetParameter(handle, property.getEntityId(), property.getId(), null, value);
414427
return this;
415428
}
@@ -420,6 +433,7 @@ public Query<T> setParameter(Property<?> property, long value) {
420433
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
421434
*/
422435
public Query<T> setParameter(String alias, long value) {
436+
checkOpen();
423437
nativeSetParameter(handle, 0, 0, alias, value);
424438
return this;
425439
}
@@ -428,6 +442,7 @@ public Query<T> setParameter(String alias, long value) {
428442
* Sets a parameter previously given to the {@link QueryBuilder} to a new value.
429443
*/
430444
public Query<T> setParameter(Property<?> property, double value) {
445+
checkOpen();
431446
nativeSetParameter(handle, property.getEntityId(), property.getId(), null, value);
432447
return this;
433448
}
@@ -438,6 +453,7 @@ public Query<T> setParameter(Property<?> property, double value) {
438453
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
439454
*/
440455
public Query<T> setParameter(String alias, double value) {
456+
checkOpen();
441457
nativeSetParameter(handle, 0, 0, alias, value);
442458
return this;
443459
}
@@ -481,6 +497,7 @@ public Query<T> setParameter(String alias, boolean value) {
481497
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
482498
*/
483499
public Query<T> setParameters(Property<?> property, long value1, long value2) {
500+
checkOpen();
484501
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, value1, value2);
485502
return this;
486503
}
@@ -491,6 +508,7 @@ public Query<T> setParameters(Property<?> property, long value1, long value2) {
491508
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
492509
*/
493510
public Query<T> setParameters(String alias, long value1, long value2) {
511+
checkOpen();
494512
nativeSetParameters(handle, 0, 0, alias, value1, value2);
495513
return this;
496514
}
@@ -499,6 +517,7 @@ public Query<T> setParameters(String alias, long value1, long value2) {
499517
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
500518
*/
501519
public Query<T> setParameters(Property<?> property, int[] values) {
520+
checkOpen();
502521
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, values);
503522
return this;
504523
}
@@ -509,6 +528,7 @@ public Query<T> setParameters(Property<?> property, int[] values) {
509528
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
510529
*/
511530
public Query<T> setParameters(String alias, int[] values) {
531+
checkOpen();
512532
nativeSetParameters(handle, 0, 0, alias, values);
513533
return this;
514534
}
@@ -517,6 +537,7 @@ public Query<T> setParameters(String alias, int[] values) {
517537
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
518538
*/
519539
public Query<T> setParameters(Property<?> property, long[] values) {
540+
checkOpen();
520541
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, values);
521542
return this;
522543
}
@@ -527,6 +548,7 @@ public Query<T> setParameters(Property<?> property, long[] values) {
527548
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
528549
*/
529550
public Query<T> setParameters(String alias, long[] values) {
551+
checkOpen();
530552
nativeSetParameters(handle, 0, 0, alias, values);
531553
return this;
532554
}
@@ -535,6 +557,7 @@ public Query<T> setParameters(String alias, long[] values) {
535557
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
536558
*/
537559
public Query<T> setParameters(Property<?> property, double value1, double value2) {
560+
checkOpen();
538561
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, value1, value2);
539562
return this;
540563
}
@@ -545,6 +568,7 @@ public Query<T> setParameters(Property<?> property, double value1, double value2
545568
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
546569
*/
547570
public Query<T> setParameters(String alias, double value1, double value2) {
571+
checkOpen();
548572
nativeSetParameters(handle, 0, 0, alias, value1, value2);
549573
return this;
550574
}
@@ -553,6 +577,7 @@ public Query<T> setParameters(String alias, double value1, double value2) {
553577
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
554578
*/
555579
public Query<T> setParameters(Property<?> property, String[] values) {
580+
checkOpen();
556581
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, values);
557582
return this;
558583
}
@@ -563,6 +588,7 @@ public Query<T> setParameters(Property<?> property, String[] values) {
563588
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
564589
*/
565590
public Query<T> setParameters(String alias, String[] values) {
591+
checkOpen();
566592
nativeSetParameters(handle, 0, 0, alias, values);
567593
return this;
568594
}
@@ -571,6 +597,7 @@ public Query<T> setParameters(String alias, String[] values) {
571597
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
572598
*/
573599
public Query<T> setParameters(Property<?> property, String key, String value) {
600+
checkOpen();
574601
nativeSetParameters(handle, property.getEntityId(), property.getId(), null, key, value);
575602
return this;
576603
}
@@ -581,6 +608,7 @@ public Query<T> setParameters(Property<?> property, String key, String value) {
581608
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
582609
*/
583610
public Query<T> setParameters(String alias, String key, String value) {
611+
checkOpen();
584612
nativeSetParameters(handle, 0, 0, alias, key, value);
585613
return this;
586614
}
@@ -589,6 +617,7 @@ public Query<T> setParameters(String alias, String key, String value) {
589617
* Sets a parameter previously given to the {@link QueryBuilder} to new values.
590618
*/
591619
public Query<T> setParameter(Property<?> property, byte[] value) {
620+
checkOpen();
592621
nativeSetParameter(handle, property.getEntityId(), property.getId(), null, value);
593622
return this;
594623
}
@@ -599,6 +628,7 @@ public Query<T> setParameter(Property<?> property, byte[] value) {
599628
* @param alias as defined using {@link QueryBuilder#parameterAlias(String)}.
600629
*/
601630
public Query<T> setParameter(String alias, byte[] value) {
631+
checkOpen();
602632
nativeSetParameter(handle, 0, 0, alias, value);
603633
return this;
604634
}
@@ -609,6 +639,7 @@ public Query<T> setParameter(String alias, byte[] value) {
609639
* @return count of removed Objects
610640
*/
611641
public long remove() {
642+
checkOpen();
612643
ensureNoFilter();
613644
return box.internalCallWithWriterHandle(cursorHandle -> nativeRemove(handle, cursorHandle));
614645
}
@@ -632,6 +663,7 @@ public long remove() {
632663
* it may be GCed and observers may become stale (won't receive anymore data).
633664
*/
634665
public SubscriptionBuilder<List<T>> subscribe() {
666+
checkOpen();
635667
return new SubscriptionBuilder<>(publisher, null);
636668
}
637669

@@ -663,6 +695,7 @@ public void publish() {
663695
* Note: the format of the returned string may change without notice.
664696
*/
665697
public String describe() {
698+
checkOpen();
666699
return nativeToString(handle);
667700
}
668701

@@ -673,7 +706,17 @@ public String describe() {
673706
* Note: the format of the returned string may change without notice.
674707
*/
675708
public String describeParameters() {
709+
checkOpen();
676710
return nativeDescribeParameters(handle);
677711
}
678712

713+
/**
714+
* Throws if {@link #close()} has been called for this.
715+
*/
716+
private void checkOpen() {
717+
if (handle == 0) {
718+
throw new IllegalStateException("This query is closed. Build and use a new one.");
719+
}
720+
}
721+
679722
}

objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ public QueryBuilder<T> and() {
532532
}
533533

534534
private void combineOperator(Operator operator) {
535+
verifyHandle(); // Not using handle, but throw for consistency with other methods.
535536
if (lastCondition == 0) {
536537
throw new IllegalStateException("No previous condition. Use operators like and() and or() only between two conditions.");
537538
}

0 commit comments

Comments
 (0)