1
1
/*
2
- * Copyright 2017-2023 ObjectBox Ltd. All rights reserved.
2
+ * Copyright 2017-2024 ObjectBox Ltd. All rights reserved.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package io .objectbox ;
18
18
19
+ import org .greenrobot .essentials .io .IoUtils ;
20
+
19
21
import java .io .BufferedInputStream ;
20
22
import java .io .BufferedOutputStream ;
21
23
import java .io .File ;
43
45
import io .objectbox .exception .DbMaxReadersExceededException ;
44
46
import io .objectbox .flatbuffers .FlatBufferBuilder ;
45
47
import io .objectbox .ideasonly .ModelUpdate ;
46
- import org .greenrobot .essentials .io .IoUtils ;
47
48
48
49
/**
49
50
* Configures and builds a {@link BoxStore} with reasonable defaults. To get an instance use {@code MyObjectBox.builder()}.
@@ -77,6 +78,9 @@ public class BoxStoreBuilder {
77
78
/** Ignored by BoxStore */
78
79
private String name ;
79
80
81
+ /** If non-null, using an in-memory database with this identifier. */
82
+ private String inMemory ;
83
+
80
84
/** Defaults to {@link #DEFAULT_MAX_DB_SIZE_KBYTE}. */
81
85
long maxSizeInKByte = DEFAULT_MAX_DB_SIZE_KBYTE ;
82
86
@@ -92,8 +96,6 @@ public class BoxStoreBuilder {
92
96
93
97
int debugFlags ;
94
98
95
- private boolean android ;
96
-
97
99
boolean debugRelations ;
98
100
99
101
int fileMode ;
@@ -134,6 +136,8 @@ private BoxStoreBuilder() {
134
136
/** Called internally from the generated class "MyObjectBox". Check MyObjectBox.builder() to get an instance. */
135
137
@ Internal
136
138
public BoxStoreBuilder (byte [] model ) {
139
+ // Note: annotations do not guarantee parameter is non-null.
140
+ //noinspection ConstantValue
137
141
if (model == null ) {
138
142
throw new IllegalArgumentException ("Model may not be null" );
139
143
}
@@ -142,16 +146,15 @@ public BoxStoreBuilder(byte[] model) {
142
146
}
143
147
144
148
/**
145
- * Name of the database, which will be used as a directory for DB files.
149
+ * Name of the database, which will be used as a directory for database files.
146
150
* You can also specify a base directory for this one using {@link #baseDirectory(File)}.
147
- * Cannot be used in combination with {@link #directory(File)}.
151
+ * Cannot be used in combination with {@link #directory(File)} and {@link #inMemory(String)} .
148
152
* <p>
149
153
* Default: "objectbox", {@link #DEFAULT_NAME} (unless {@link #directory(File)} is used)
150
154
*/
151
155
public BoxStoreBuilder name (String name ) {
152
- if (directory != null ) {
153
- throw new IllegalArgumentException ("Already has directory, cannot assign name" );
154
- }
156
+ checkIsNull (directory , "Already has directory, cannot assign name" );
157
+ checkIsNull (inMemory , "Already set to in-memory database, cannot assign name" );
155
158
if (name .contains ("/" ) || name .contains ("\\ " )) {
156
159
throw new IllegalArgumentException ("Name may not contain (back) slashes. " +
157
160
"Use baseDirectory() or directory() to configure alternative directories" );
@@ -161,65 +164,89 @@ public BoxStoreBuilder name(String name) {
161
164
}
162
165
163
166
/**
164
- * The directory where all DB files should be placed in.
165
- * Cannot be used in combination with {@link #name(String)}/{@link #baseDirectory(File)}.
167
+ * The directory where all database files should be placed in.
168
+ * <p>
169
+ * If the directory does not exist, it will be created. Make sure the process has permissions to write to this
170
+ * directory.
171
+ * <p>
172
+ * To switch to an in-memory database, use a file path with {@link BoxStore#IN_MEMORY_PREFIX} and an identifier
173
+ * instead:
174
+ * <p>
175
+ * <pre>{@code
176
+ * BoxStore inMemoryStore = MyObjectBox.builder()
177
+ * .directory(BoxStore.IN_MEMORY_PREFIX + "notes-db")
178
+ * .build();
179
+ * }</pre>
180
+ * Alternatively, use {@link #inMemory(String)}.
181
+ * <p>
182
+ * Can not be used in combination with {@link #name(String)}, {@link #baseDirectory(File)}
183
+ * or {@link #inMemory(String)}.
166
184
*/
167
185
public BoxStoreBuilder directory (File directory ) {
168
- if (name != null ) {
169
- throw new IllegalArgumentException ("Already has name, cannot assign directory" );
170
- }
171
- if (!android && baseDirectory != null ) {
172
- throw new IllegalArgumentException ("Already has base directory, cannot assign directory" );
173
- }
186
+ checkIsNull (name , "Already has name, cannot assign directory" );
187
+ checkIsNull (inMemory , "Already set to in-memory database, cannot assign directory" );
188
+ checkIsNull (baseDirectory , "Already has base directory, cannot assign directory" );
174
189
this .directory = directory ;
175
190
return this ;
176
191
}
177
192
178
193
/**
179
194
* In combination with {@link #name(String)}, this lets you specify the location of where the DB files should be
180
195
* stored.
181
- * Cannot be used in combination with {@link #directory(File)}.
196
+ * Cannot be used in combination with {@link #directory(File)} or {@link #inMemory(String)} .
182
197
*/
183
198
public BoxStoreBuilder baseDirectory (File baseDirectory ) {
184
- if (directory != null ) {
185
- throw new IllegalArgumentException ("Already has directory, cannot assign base directory" );
186
- }
199
+ checkIsNull (directory , "Already has directory, cannot assign base directory" );
200
+ checkIsNull (inMemory , "Already set to in-memory database, cannot assign base directory" );
187
201
this .baseDirectory = baseDirectory ;
188
202
return this ;
189
203
}
190
204
191
205
/**
192
- * On Android, you can pass a Context to set the base directory using this method.
193
- * This will conveniently configure the storage location to be in the files directory of your app.
206
+ * Switches to an in-memory database using the given name as its identifier.
207
+ * <p>
208
+ * Can not be used in combination with {@link #name(String)}, {@link #directory(File)}
209
+ * or {@link #baseDirectory(File)}.
210
+ */
211
+ public BoxStoreBuilder inMemory (String identifier ) {
212
+ checkIsNull (name , "Already has name, cannot switch to in-memory database" );
213
+ checkIsNull (directory , "Already has directory, cannot switch to in-memory database" );
214
+ checkIsNull (baseDirectory , "Already has base directory, cannot switch to in-memory database" );
215
+ inMemory = identifier ;
216
+ return this ;
217
+ }
218
+
219
+ /**
220
+ * Use to check conflicting properties are not set.
221
+ * If not null, throws {@link IllegalStateException} with the given message.
222
+ */
223
+ private static void checkIsNull (@ Nullable Object value , String errorMessage ) {
224
+ if (value != null ) {
225
+ throw new IllegalStateException (errorMessage );
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Use on Android to pass a <a href="https://developer.android.com/reference/android/content/Context">Context</a>
231
+ * for loading the native library and, if not an {@link #inMemory(String)} database, for creating the base
232
+ * directory for database files in the
233
+ * <a href="https://developer.android.com/reference/android/content/Context#getFilesDir()">files directory of the app</a>.
194
234
* <p>
195
- * In more detail, this assigns the base directory (see {@link #baseDirectory}) to
235
+ * In more detail, upon {@link #build()} assigns the base directory (see {@link #baseDirectory}) to
196
236
* {@code context.getFilesDir() + "/objectbox/"}.
197
- * Thus, when using the default name (also "objectbox" unless overwritten using {@link #name(String)}), the default
198
- * location of DB files will be "objectbox/objectbox/" inside the app files directory.
199
- * If you specify a custom name, for example with {@code name("foobar")}, it would become
200
- * "objectbox/foobar/".
237
+ * Thus, when using the default name (also "objectbox", unless overwritten using {@link #name(String)}), the default
238
+ * location of database files will be "objectbox/objectbox/" inside the app's files directory.
239
+ * If a custom name is specified, for example with {@code name("foobar")}, it would become "objectbox/foobar/".
201
240
* <p>
202
- * Alternatively, you can also use {@link #baseDirectory} or {@link #directory(File)} instead.
241
+ * Use {@link #baseDirectory(File)} or {@link #directory(File)} to specify a different directory for the database
242
+ * files.
203
243
*/
204
244
public BoxStoreBuilder androidContext (Object context ) {
205
245
//noinspection ConstantConditions Annotation does not enforce non-null.
206
246
if (context == null ) {
207
247
throw new NullPointerException ("Context may not be null" );
208
248
}
209
249
this .context = getApplicationContext (context );
210
-
211
- File baseDir = getAndroidBaseDir (context );
212
- if (!baseDir .exists ()) {
213
- baseDir .mkdir ();
214
- if (!baseDir .exists ()) { // check baseDir.exists() because of potential concurrent processes
215
- throw new RuntimeException ("Could not init Android base dir at " + baseDir .getAbsolutePath ());
216
- }
217
- }
218
- if (!baseDir .isDirectory ()) {
219
- throw new RuntimeException ("Android base dir is not a dir: " + baseDir .getAbsolutePath ());
220
- }
221
- baseDirectory = baseDir ;
222
- android = true ;
223
250
return this ;
224
251
}
225
252
@@ -504,7 +531,7 @@ public BoxStoreBuilder debugRelations() {
504
531
* {@link DbException} are thrown during query execution).
505
532
*
506
533
* @param queryAttempts number of attempts a query find operation will be executed before failing.
507
- * Recommended values are in the range of 2 to 5, e.g. a value of 3 as a starting point.
534
+ * Recommended values are in the range of 2 to 5, e.g. a value of 3 as a starting point.
508
535
*/
509
536
@ Experimental
510
537
public BoxStoreBuilder queryAttempts (int queryAttempts ) {
@@ -580,14 +607,36 @@ byte[] buildFlatStoreOptions(String canonicalPath) {
580
607
}
581
608
582
609
/**
583
- * Builds a {@link BoxStore} using any given configuration.
610
+ * Builds a {@link BoxStore} using the current configuration of this builder.
611
+ *
612
+ * <p>If {@link #androidContext(Object)} was called and no {@link #directory(File)} or {@link #baseDirectory(File)}
613
+ * is configured, creates and sets {@link #baseDirectory(File)} as explained in {@link #androidContext(Object)}.
584
614
*/
585
615
public BoxStore build () {
616
+ // If in-memory, use a special directory (it will never be created)
617
+ if (inMemory != null ) {
618
+ directory = new File (BoxStore .IN_MEMORY_PREFIX + inMemory );
619
+ }
620
+ // On Android, create and set base directory if no directory is explicitly configured
621
+ if (directory == null && baseDirectory == null && context != null ) {
622
+ File baseDir = getAndroidBaseDir (context );
623
+ if (!baseDir .exists ()) {
624
+ baseDir .mkdir ();
625
+ if (!baseDir .exists ()) { // check baseDir.exists() because of potential concurrent processes
626
+ throw new RuntimeException ("Could not init Android base dir at " + baseDir .getAbsolutePath ());
627
+ }
628
+ }
629
+ if (!baseDir .isDirectory ()) {
630
+ throw new RuntimeException ("Android base dir is not a dir: " + baseDir .getAbsolutePath ());
631
+ }
632
+ baseDirectory = baseDir ;
633
+ }
586
634
if (directory == null ) {
587
- name = dbName (name );
588
635
directory = getDbDir (baseDirectory , name );
589
636
}
590
- checkProvisionInitialDbFile ();
637
+ if (inMemory == null ) {
638
+ checkProvisionInitialDbFile ();
639
+ }
591
640
return new BoxStore (this );
592
641
}
593
642
0 commit comments