Skip to content

Commit f9adcd1

Browse files
committed
Merge branch 'sync' into dev
2 parents 03d8b81 + dbacf70 commit f9adcd1

30 files changed

+1922
-1
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.objectbox.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Enables sync for an {@link Entity} class.
10+
* <p>
11+
* Note that currently sync can not be enabled or disabled for existing entities.
12+
* Also synced entities can not have relations to non-synced entities.
13+
*/
14+
@Retention(RetentionPolicy.CLASS)
15+
@Target(ElementType.TYPE)
16+
public @interface Sync {
17+
}

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

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.io.Closeable;
2222
import java.io.File;
2323
import java.io.IOException;
24+
import java.net.MalformedURLException;
25+
import java.net.URL;
2426
import java.util.ArrayList;
2527
import java.util.Collection;
2628
import java.util.Collections;
@@ -51,6 +53,7 @@
5153
import io.objectbox.reactive.DataObserver;
5254
import io.objectbox.reactive.DataPublisher;
5355
import io.objectbox.reactive.SubscriptionBuilder;
56+
import io.objectbox.sync.SyncClient;
5457

5558
/**
5659
* An ObjectBox database that provides {@link Box Boxes} to put and get Objects of a specific Entity class
@@ -169,19 +172,45 @@ static native void nativeRegisterCustomType(long store, int entityId, int proper
169172

170173
static native void nativeSetDebugFlags(long store, int debugFlags);
171174

172-
static native String nativeStartObjectBrowser(long store, @Nullable String urlPath, int port);
175+
private native String nativeStartObjectBrowser(long store, @Nullable String urlPath, int port);
176+
177+
private native boolean nativeStopObjectBrowser(long store);
173178

174179
static native boolean nativeIsObjectBrowserAvailable();
175180

176181
native long nativeSizeOnDisk(long store);
177182

178183
native long nativeValidate(long store, long pageLimit, boolean checkLeafLevel);
179184

185+
static native int nativeGetSupportedSync();
186+
180187
public static boolean isObjectBrowserAvailable() {
181188
NativeLibraryLoader.ensureLoaded();
182189
return nativeIsObjectBrowserAvailable();
183190
}
184191

192+
private static int getSupportedSync() {
193+
NativeLibraryLoader.ensureLoaded();
194+
try {
195+
int supportedSync = nativeGetSupportedSync();
196+
if (supportedSync < 0 || supportedSync > 2) {
197+
throw new IllegalStateException("Unexpected sync support: " + supportedSync);
198+
}
199+
return supportedSync;
200+
} catch (UnsatisfiedLinkError e) {
201+
System.err.println("Old JNI lib? " + e); // No stack
202+
return 0;
203+
}
204+
}
205+
206+
public static boolean isSyncAvailable() {
207+
return getSupportedSync() != 0;
208+
}
209+
210+
public static boolean isSyncServerAvailable() {
211+
return getSupportedSync() == 2;
212+
}
213+
185214
native long nativePanicModeRemoveAllObjects(long store, int entityId);
186215

187216
private final File directory;
@@ -216,6 +245,12 @@ public static boolean isObjectBrowserAvailable() {
216245

217246
private final TxCallback<?> failedReadTxAttemptCallback;
218247

248+
/**
249+
* Keeps a reference so the library user does not have to.
250+
*/
251+
@Nullable
252+
private SyncClient syncClient;
253+
219254
BoxStore(BoxStoreBuilder builder) {
220255
context = builder.context;
221256
relinker = builder.relinker;
@@ -489,6 +524,14 @@ public void close() {
489524
synchronized (this) {
490525
oldClosedState = closed;
491526
if (!closed) {
527+
if(objectBrowserPort != 0) { // not linked natively (yet), so clean up here
528+
try {
529+
stopObjectBrowser();
530+
} catch (Throwable e) {
531+
e.printStackTrace();
532+
}
533+
}
534+
492535
// Closeable recommendation: mark as closed before any code that might throw.
493536
closed = true;
494537
List<Transaction> transactionsToClose;
@@ -1015,11 +1058,41 @@ public String startObjectBrowser(int port) {
10151058
return url;
10161059
}
10171060

1061+
@Experimental
1062+
@Nullable
1063+
public String startObjectBrowser(String urlToBindTo) {
1064+
verifyObjectBrowserNotRunning();
1065+
int port;
1066+
try {
1067+
port = new URL(urlToBindTo).getPort(); // Gives -1 if not available
1068+
} catch (MalformedURLException e) {
1069+
throw new RuntimeException("Can not start Object Browser at " + urlToBindTo, e);
1070+
}
1071+
String url = nativeStartObjectBrowser(handle, urlToBindTo, 0);
1072+
if (url != null) {
1073+
objectBrowserPort = port;
1074+
}
1075+
return url;
1076+
}
1077+
1078+
@Experimental
1079+
public synchronized boolean stopObjectBrowser() {
1080+
if(objectBrowserPort == 0) {
1081+
throw new IllegalStateException("ObjectBrowser has not been started before");
1082+
}
1083+
objectBrowserPort = 0;
1084+
return nativeStopObjectBrowser(handle);
1085+
}
1086+
10181087
@Experimental
10191088
public int getObjectBrowserPort() {
10201089
return objectBrowserPort;
10211090
}
10221091

1092+
public boolean isObjectBrowserRunning() {
1093+
return objectBrowserPort != 0;
1094+
}
1095+
10231096
private void verifyObjectBrowserNotRunning() {
10241097
if (objectBrowserPort != 0) {
10251098
throw new DbException("ObjectBrowser is already running at port " + objectBrowserPort);
@@ -1097,4 +1170,15 @@ public long getNativeStore() {
10971170
return handle;
10981171
}
10991172

1173+
/**
1174+
* Returns the {@link SyncClient} associated with this store. To create one see {@link io.objectbox.sync.Sync Sync}.
1175+
*/
1176+
@Nullable
1177+
public SyncClient getSyncClient() {
1178+
return syncClient;
1179+
}
1180+
1181+
void setSyncClient(@Nullable SyncClient syncClient) {
1182+
this.syncClient = syncClient;
1183+
}
11001184
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,21 @@
1616

1717
package io.objectbox;
1818

19+
import javax.annotation.Nullable;
20+
1921
import io.objectbox.annotation.apihint.Internal;
22+
import io.objectbox.sync.SyncClient;
2023

2124
@Internal
2225
public class InternalAccess {
2326
public static <T> Cursor<T> getReader(Box<T> box) {
2427
return box.getReader();
2528
}
2629

30+
public static long getHandle(BoxStore boxStore) {
31+
return boxStore.internalHandle();
32+
}
33+
2734
public static long getHandle(Cursor reader) {
2835
return reader.internalHandle();
2936
}
@@ -32,6 +39,10 @@ public static long getHandle(Transaction tx) {
3239
return tx.internalHandle();
3340
}
3441

42+
public static void setSyncClient(BoxStore boxStore, @Nullable SyncClient syncClient) {
43+
boxStore.setSyncClient(syncClient);
44+
}
45+
3546
public static <T> void releaseReader(Box<T> box, Cursor<T> reader) {
3647
box.releaseReader(reader);
3748
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.objectbox.sync;
2+
3+
import javax.annotation.Nullable;
4+
5+
/**
6+
* Used by {@link SyncClient} to observe connectivity changes.
7+
* <p>
8+
* Implementations are provided by a {@link io.objectbox.sync.internal.Platform platform}.
9+
*/
10+
public abstract class ConnectivityMonitor {
11+
12+
@Nullable
13+
private SyncClient syncClient;
14+
15+
void setObserver(SyncClient syncClient) {
16+
//noinspection ConstantConditions Annotations do not enforce non-null.
17+
if (syncClient == null) {
18+
throw new IllegalArgumentException("Sync client must not be null");
19+
}
20+
this.syncClient = syncClient;
21+
onObserverSet();
22+
}
23+
24+
void removeObserver() {
25+
this.syncClient = null;
26+
onObserverRemoved();
27+
}
28+
29+
/**
30+
* Called right after the observer was set.
31+
*/
32+
public void onObserverSet() {
33+
}
34+
35+
/**
36+
* Called right after the observer was removed.
37+
*/
38+
public void onObserverRemoved() {
39+
}
40+
41+
/**
42+
* Notifies the observer that a connection is available.
43+
* Implementers should call this once a working network connection is available.
44+
*/
45+
public final void notifyConnectionAvailable() {
46+
SyncClient syncClient = this.syncClient;
47+
if (syncClient != null) {
48+
syncClient.notifyConnectionAvailable();
49+
}
50+
}
51+
52+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.objectbox.sync;
2+
3+
import io.objectbox.BoxStore;
4+
import io.objectbox.annotation.apihint.Experimental;
5+
import io.objectbox.sync.server.SyncServerBuilder;
6+
7+
/**
8+
* Start building a sync client using Sync.{@link #client(BoxStore, String, SyncCredentials)}
9+
* or a server using Sync.{@link #server(BoxStore, String, SyncCredentials)}.
10+
*/
11+
@SuppressWarnings({"unused", "WeakerAccess"})
12+
@Experimental
13+
public final class Sync {
14+
15+
/**
16+
* Returns true if the included native (JNI) ObjectBox library supports sync.
17+
*/
18+
public static boolean isAvailable() {
19+
return BoxStore.isSyncAvailable();
20+
}
21+
22+
/**
23+
* Start building a sync client. Requires the BoxStore that should be synced with the server,
24+
* the URL and port of the server to connect to and credentials to authenticate against the server.
25+
*/
26+
public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials credentials) {
27+
return new SyncBuilder(boxStore, url, credentials);
28+
}
29+
30+
/**
31+
* Start building a sync server. Requires the BoxStore the server should use,
32+
* the URL and port the server should bind to and authenticator credentials to authenticate clients.
33+
* Additional authenticator credentials can be supplied using the builder.
34+
*/
35+
public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCredentials authenticatorCredentials) {
36+
return new SyncServerBuilder(boxStore, url, authenticatorCredentials);
37+
}
38+
39+
private Sync() {
40+
}
41+
}

0 commit comments

Comments
 (0)