Skip to content

Commit bfe580d

Browse files
committed
Merge branch '69-switch-boxstorebuilder-to-flatstoreoptions' into dev
# Conflicts: # tests/objectbox-java-test/src/test/java/io/objectbox/BoxStoreTest.java
2 parents 04c2e75 + d5caeba commit bfe580d

File tree

8 files changed

+482
-14
lines changed

8 files changed

+482
-14
lines changed

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

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2019 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2017-2020 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ public class BoxStore implements Closeable {
6666
/** Change so ReLinker will update native library when using workaround loading. */
6767
public static final String JNI_VERSION = "2.6.0";
6868

69-
private static final String VERSION = "2.6.1-2020-06-09";
69+
private static final String VERSION = "2.7.0-2020-07-30";
7070
private static BoxStore defaultStore;
7171

7272
/** Currently used DB dirs with values from {@link #getCanonicalPath(File)}. */
@@ -136,7 +136,13 @@ public static String getVersionNative() {
136136
*/
137137
public static native void testUnalignedMemoryAccess();
138138

139-
static native long nativeCreate(String directory, long maxDbSizeInKByte, int maxReaders, byte[] model);
139+
/**
140+
* Creates a native BoxStore instance with FlatBuffer {@link io.objectbox.model.FlatStoreOptions} {@code options}
141+
* and a {@link ModelBuilder} {@code model}. Returns the handle of the native store instance.
142+
*/
143+
static native long nativeCreateWithFlatOptions(byte[] options, byte[] model);
144+
145+
static native boolean nativeIsReadOnly(long store);
140146

141147
static native void nativeDelete(long store);
142148

@@ -166,6 +172,10 @@ static native void nativeRegisterCustomType(long store, int entityId, int proper
166172

167173
static native boolean nativeIsObjectBrowserAvailable();
168174

175+
native long nativeSizeOnDisk(long store);
176+
177+
native long nativeValidate(long store, long pageLimit, boolean checkLeafLevel);
178+
169179
public static boolean isObjectBrowserAvailable() {
170180
NativeLibraryLoader.ensureLoaded();
171181
return nativeIsObjectBrowserAvailable();
@@ -214,10 +224,9 @@ public static boolean isObjectBrowserAvailable() {
214224
canonicalPath = getCanonicalPath(directory);
215225
verifyNotAlreadyOpen(canonicalPath);
216226

217-
handle = nativeCreate(canonicalPath, builder.maxSizeInKByte, builder.maxReaders, builder.model);
227+
handle = nativeCreateWithFlatOptions(builder.buildFlatStoreOptions(canonicalPath), builder.model);
218228
int debugFlags = builder.debugFlags;
219229
if (debugFlags != 0) {
220-
nativeSetDebugFlags(handle, debugFlags);
221230
debugTxRead = (debugFlags & DebugFlags.LOG_TRANSACTIONS_READ) != 0;
222231
debugTxWrite = (debugFlags & DebugFlags.LOG_TRANSACTIONS_WRITE) != 0;
223232
} else {
@@ -446,6 +455,14 @@ public boolean isClosed() {
446455
return closed;
447456
}
448457

458+
/**
459+
* Whether the store was created using read-only mode.
460+
* If true the schema is not updated and write transactions are not possible.
461+
*/
462+
public boolean isReadOnly() {
463+
return nativeIsReadOnly(handle);
464+
}
465+
449466
/**
450467
* Closes the BoxStore and frees associated resources.
451468
* This method is useful for unit tests;
@@ -900,6 +917,19 @@ public String diagnose() {
900917
return nativeDiagnose(handle);
901918
}
902919

920+
/**
921+
* Validates up to {@code pageLimit} pages of the store. Set {@code checkLeafLevel} to check leafs, too.
922+
* Returns the number of pages validated.
923+
* Throws StorageException if validation fails.
924+
* Throws DbFileCorruptException or DbPagesCorruptException if the DB is actually inconsistent (corrupt).
925+
*/
926+
public long validate(long pageLimit, boolean checkLeafLevel) {
927+
if (pageLimit < 0) {
928+
throw new IllegalArgumentException("pageLimit must be zero or positive");
929+
}
930+
return nativeValidate(handle, pageLimit, checkLeafLevel);
931+
}
932+
903933
public int cleanStaleReadTransactions() {
904934
return nativeCleanStaleReadTransactions(handle);
905935
}

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

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package io.objectbox;
1818

19+
import com.google.flatbuffers.FlatBufferBuilder;
20+
import io.objectbox.model.FlatStoreOptions;
1921
import org.greenrobot.essentials.io.IoUtils;
2022

2123
import java.io.BufferedInputStream;
@@ -37,6 +39,7 @@
3739
import io.objectbox.annotation.apihint.Internal;
3840
import io.objectbox.exception.DbException;
3941
import io.objectbox.ideasonly.ModelUpdate;
42+
import io.objectbox.model.ValidateOnOpenMode;
4043

4144
/**
4245
* Builds a {@link BoxStore} with optional configurations. The class is not initiated directly; use
@@ -87,18 +90,32 @@ public class BoxStoreBuilder {
8790

8891
boolean debugRelations;
8992

93+
long fileMode;
94+
9095
int maxReaders;
9196

9297
int queryAttempts;
9398

99+
/** For DebugCursor. */
100+
boolean skipReadSchema;
101+
102+
boolean readOnly;
103+
boolean usePreviousCommit;
104+
105+
short validateOnOpenMode;
106+
long validateOnOpenPageLimit;
107+
94108
TxCallback<?> failedReadTxAttemptCallback;
95109

96110
final List<EntityInfo<?>> entityInfoList = new ArrayList<>();
97111
private Factory<InputStream> initialDbFileFactory;
98112

99-
/** Not for application use. */
113+
/** Not for application use, for DebugCursor. */
114+
@Internal
100115
public static BoxStoreBuilder createDebugWithoutModel() {
101-
return new BoxStoreBuilder();
116+
BoxStoreBuilder builder = new BoxStoreBuilder();
117+
builder.skipReadSchema = true;
118+
return builder;
102119
}
103120

104121
private BoxStoreBuilder() {
@@ -259,6 +276,17 @@ private static File getAndroidFilesDir(Object context) {
259276
return filesDir;
260277
}
261278

279+
/**
280+
* Specify
281+
* <a href="https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation">unix-style file permissions</a>
282+
* to use for the database directory and files.
283+
* E.g. for {@code rw-rw-rw-} (owner, group, other) pass the octal code {@code 666}.
284+
*/
285+
public BoxStoreBuilder fileMode(long mode) {
286+
this.fileMode = mode;
287+
return this;
288+
}
289+
262290
/**
263291
* Sets the maximum number of concurrent readers. For most applications, the default is fine (&gt; 100 readers).
264292
* <p>
@@ -269,7 +297,6 @@ private static File getAndroidFilesDir(Object context) {
269297
* For highly concurrent setups (e.g. you are using ObjectBox on the server side) it may make sense to increase the
270298
* number.
271299
*/
272-
273300
public BoxStoreBuilder maxReaders(int maxReaders) {
274301
this.maxReaders = maxReaders;
275302
return this;
@@ -299,6 +326,63 @@ public BoxStoreBuilder maxSizeInKByte(long maxSizeInKByte) {
299326
return this;
300327
}
301328

329+
/**
330+
* Open the store in read-only mode: no schema update, no write transactions.
331+
* <p>
332+
* It is recommended to use this with {@link #usePreviousCommit()} to ensure no data is lost.
333+
*/
334+
public BoxStoreBuilder readOnly() {
335+
this.readOnly = true;
336+
return this;
337+
}
338+
339+
/**
340+
* Ignores the latest data snapshot (committed transaction state) and uses the previous snapshot instead.
341+
* When used with care (e.g. backup the DB files first), this option may also recover data removed by the latest
342+
* transaction.
343+
* <p>
344+
* It is recommended to use this with {@link #readOnly()} to ensure no data is lost.
345+
*/
346+
public BoxStoreBuilder usePreviousCommit() {
347+
this.usePreviousCommit = true;
348+
return this;
349+
}
350+
351+
/**
352+
* When a database is opened, ObjectBox can perform additional consistency checks on its database structure.
353+
* Reliable file systems already guarantee consistency, so this is primarily meant to deal with unreliable
354+
* OSes, file systems, or hardware.
355+
* <p>
356+
* Note: ObjectBox builds upon ACID storage, which already has strong consistency mechanisms in place.
357+
*
358+
* @param validateOnOpenMode One of {@link ValidateOnOpenMode}.
359+
*/
360+
public BoxStoreBuilder validateOnOpen(short validateOnOpenMode) {
361+
if (validateOnOpenMode < ValidateOnOpenMode.None || validateOnOpenMode > ValidateOnOpenMode.Full) {
362+
throw new IllegalArgumentException("Must be one of ValidateOnOpenMode");
363+
}
364+
this.validateOnOpenMode = validateOnOpenMode;
365+
return this;
366+
}
367+
368+
/**
369+
* To fine-tune {@link #validateOnOpen(short)}, you can specify a limit on how much data is looked at.
370+
* This is measured in "pages" with a page typically holding 4000.
371+
* Usually a low number (e.g. 1-20) is sufficient and does not impact startup performance significantly.
372+
* <p>
373+
* This can only be used with {@link ValidateOnOpenMode#Regular} and {@link ValidateOnOpenMode#WithLeaves}.
374+
*/
375+
public BoxStoreBuilder validateOnOpenPageLimit(long limit) {
376+
if (validateOnOpenMode != ValidateOnOpenMode.Regular && validateOnOpenMode != ValidateOnOpenMode.WithLeaves) {
377+
throw new IllegalStateException("Must call validateOnOpen(mode) with mode Regular or WithLeaves first");
378+
}
379+
if (limit < 1) {
380+
throw new IllegalArgumentException("limit must be positive");
381+
}
382+
this.validateOnOpenPageLimit = limit;
383+
return this;
384+
}
385+
302386
/**
303387
* @deprecated Use {@link #debugFlags} instead.
304388
*/
@@ -370,6 +454,39 @@ public BoxStoreBuilder initialDbFile(Factory<InputStream> initialDbFileFactory)
370454
return this;
371455
}
372456

457+
byte[] buildFlatStoreOptions(String canonicalPath) {
458+
FlatBufferBuilder fbb = new FlatBufferBuilder();
459+
// FlatBuffer default values are set in generated code, e.g. may be different from here, so always store value.
460+
fbb.forceDefaults(true);
461+
462+
// Add non-integer values first...
463+
int directoryPathOffset = fbb.createString(canonicalPath);
464+
465+
FlatStoreOptions.startFlatStoreOptions(fbb);
466+
467+
// ...then build options.
468+
FlatStoreOptions.addDirectoryPath(fbb, directoryPathOffset);
469+
FlatStoreOptions.addMaxDbSizeInKByte(fbb, maxSizeInKByte);
470+
FlatStoreOptions.addFileMode(fbb, fileMode);
471+
FlatStoreOptions.addMaxReaders(fbb, maxReaders);
472+
if (validateOnOpenMode != 0) {
473+
FlatStoreOptions.addValidateOnOpen(fbb, validateOnOpenMode);
474+
if (validateOnOpenPageLimit != 0) {
475+
FlatStoreOptions.addValidateOnOpenPageLimit(fbb, validateOnOpenPageLimit);
476+
}
477+
}
478+
if(skipReadSchema) FlatStoreOptions.addSkipReadSchema(fbb, skipReadSchema);
479+
if(usePreviousCommit) FlatStoreOptions.addUsePreviousCommit(fbb, usePreviousCommit);
480+
if(readOnly) FlatStoreOptions.addReadOnly(fbb, readOnly);
481+
if (debugFlags != 0) {
482+
FlatStoreOptions.addDebugFlags(fbb, debugFlags);
483+
}
484+
485+
int offset = FlatStoreOptions.endFlatStoreOptions(fbb);
486+
fbb.finish(offset);
487+
return fbb.sizedByteArray();
488+
}
489+
373490
/**
374491
* Builds a {@link BoxStore} using any given configuration.
375492
*/
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020 ObjectBox Ltd. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.objectbox.exception;
17+
18+
/** Errors were detected in a file, e.g. illegal values or structural inconsistencies. */
19+
public class FileCorruptException extends DbException {
20+
public FileCorruptException(String message) {
21+
super(message);
22+
}
23+
24+
public FileCorruptException(String message, int errorCode) {
25+
super(message, errorCode);
26+
}
27+
}

0 commit comments

Comments
 (0)