Committed callbacks can be used in various scenarios:
In our example we will create an implementation for the last case.
When several clients are working
on the same objects it is very possible that the data will be outdated on some
of the clients. Before the commit-callbacks feature was introduced the solution
was to call refresh
regularly to get object updates from the
server. With the commit-callback this process can be easily automated:
Let's open 2 clients, which will work with Car objects, and register committed event listeners for them.
01private ObjectContainer openClient() { 02
try { 03
ObjectContainer client = Db4o.openClient("localhost", PORT, USER, 04
PASSWORD); 05
EventListener4 committedEventListener = createCommittedEventListener(client); 06
EventRegistry eventRegistry = EventRegistryFactory 07
.forObjectContainer(client); 08
eventRegistry.committed().addListener(committedEventListener); 09
// save the client-listener pair in a map, so that we can 10
// remove the listener later 11
clientListeners.put(client, committedEventListener); 12
return client; 13
} catch (Exception ex) { 14
ex.printStackTrace(); 15
} 16
return null; 17
}
01private EventListener4 createCommittedEventListener( 02
final ObjectContainer objectContainer) { 03
return new EventListener4() { 04
public void onEvent(Event4 e, EventArgs args) { 05
// get all the updated objects 06
ObjectInfoCollection updated = ((CommitEventArgs) args) 07
.updated(); 08
Iterator4 infos = updated.iterator(); 09
while (infos.moveNext()) { 10
ObjectInfo info = (ObjectInfo) infos.current(); 11
Object obj = info.getObject(); 12
// refresh object on the client 13
objectContainer.ext().refresh(obj, 2); 14
} 15
} 16
}; 17
}
Run the following method to see how the 2 clients work concurrently on the same object:
01public void run() throws IOException, DatabaseFileLockedException { 02
new File(DB4O_FILE_NAME).delete(); 03
ObjectServer server = Db4o.openServer(DB4O_FILE_NAME, PORT); 04
try { 05
server.grantAccess(USER, PASSWORD); 06
07
ObjectContainer client1 = openClient(); 08
ObjectContainer client2 = openClient(); 09
10
if (client1 != null && client2 != null) { 11
try { 12
// wait for the operations to finish 13
waitForCompletion(); 14
15
// save pilot with client1 16
Car client1Car = new Car("Ferrari", 2006, new Pilot( 17
"Schumacher")); 18
client1.set(client1Car); 19
client1.commit(); 20
21
waitForCompletion(); 22
23
// retrieve the same pilot with client2 24
Car client2Car = (Car) client2.query(Car.class).next(); 25
System.out.println(client2Car); 26
27
// modify the pilot with client1 28
client1Car.setModel(2007); 29
client1Car.setPilot(new Pilot("Hakkinnen")); 30
client1.set(client1Car); 31
client1.commit(); 32
33
waitForCompletion(); 34
35
// client2Car has been automatically updated in 36
// the committed event handler because of the 37
// modification and the commit by client1 38
System.out.println(client2Car); 39
40
waitForCompletion(); 41
} catch (Exception ex) { 42
ex.printStackTrace(); 43
} finally { 44
closeClient(client1); 45
closeClient(client2); 46
} 47
} 48
} catch (Exception ex) { 49
ex.printStackTrace(); 50
} finally { 51
server.close(); 52
} 53
}
You should see that client2 picked up the changes committed from the client1 automatically due to the committed event handler.
Working with the committed event you should remember that
the listener is called in a separate thread, which needs to be synchronized
with the main application thread. This functionality is not implemented in the presented example, instead a simple thread Sleep(1000) method is used (WaitForCompletion method), which is not realiable at all. For a reliable execution use events and notifications from the committed callbacks.
It is a good practice to remove the committed event handlers from the registry before shutting down the clients:
01private void closeClient(ObjectContainer client) { 02
// remove listeners before shutting down 03
if (clientListeners.get(client) != null) { 04
EventRegistry eventRegistry = EventRegistryFactory 05
.forObjectContainer(client); 06
eventRegistry.committed().removeListener( 07
(EventListener4) clientListeners.get(client)); 08
clientListeners.remove(client); 09
} 10
client.close(); 11
}