Committed Event Example

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.

PushedUpdatesExample.cs: OpenClient
01private IObjectContainer OpenClient() 02 { 03 try 04 { 05 IObjectContainer client = Db4oFactory.OpenClient("localhost", Port, User, 06 Password); 07 CommitEventHandler committedEventHandler = CreateCommittedEventHandler(client); 08 IEventRegistry eventRegistry = EventRegistryFactory.ForObjectContainer(client); 09 eventRegistry.Committed += committedEventHandler; 10 // save the client-listener pair in a map, so that we can 11 // remove the listener later 12 clientListeners.Add(client, committedEventHandler); 13 return client; 14 } 15 catch (Exception ex) 16 { 17 System.Console.WriteLine(ex.ToString()); 18 } 19 return null; 20 }
PushedUpdatesExample.cs: CreateCommittedEventHandler
01private CommitEventHandler CreateCommittedEventHandler(IObjectContainer objectContainer) 02 { 03 return new CommitEventHandler(delegate(object sender, CommitEventArgs args) 04 { 05 // get all the updated objects 06 IObjectInfoCollection updated = args.Updated; 07 08 foreach (IObjectInfo info in updated) 09 { 10 Object obj = info.GetObject(); 11 // refresh object on the client 12 objectContainer.Ext().Refresh(obj, 2); 13 } 14 }); 15 }
PushedUpdatesExample.vb: OpenClient
01Private Function OpenClient() As IObjectContainer 02 Try 03 Dim client As IObjectContainer = Db4oFactory.OpenClient("localhost", Port, User, Password) 04 Dim committedEventHandler As CommittedEventHandler = New CommittedEventHandler(client) 05 Dim eventRegistry As IEventRegistry = EventRegistryFactory.ForObjectContainer(client) 06 AddHandler eventRegistry.Committed, AddressOf committedEventHandler.OnCommitted 07 ' save the client-listener pair in a map, so that we can 08 ' remove the listener later 09 clientListeners.Add(client, committedEventHandler) 10 Return client 11 Catch ex As Exception 12 System.Console.WriteLine(ex.ToString) 13 End Try 14 Return Nothing 15 End Function
CommittedEventHandler.vb
01' Copyright (C) 2007 db4objects Inc. http://www.db4o.com 02Imports Db4objects.Db4o 03Imports Db4objects.Db4o.Events 04Imports Db4objects.Db4o.Ext 05Imports Db4objects.Db4o.Foundation 06 07 08Namespace Db4objects.Db4odoc.CommitCallbacks 09 Public Class CommittedEventHandler 10 11 Private _objectContainer As IObjectContainer 12 13 Private Delegate Sub OnCommittedHandler(ByVal sender As Object, ByVal args As CommitEventArgs) 14 15 Public Sub New(ByVal objectContainer As IObjectContainer) 16 _objectContainer = objectContainer 17 End Sub 18 19 Private Function CreateCommittedEventHandler(ByVal objectContainer As IObjectContainer) As OnCommittedHandler 20 Return AddressOf OnCommitted 21 End Function 22 23 Public Sub OnCommitted(ByVal sender As Object, ByVal args As CommitEventArgs) 24 ' get all the updated objects 25 Dim updated As IObjectInfoCollection = args.Updated 26 For Each info As IObjectInfo In updated 27 Dim obj As Object = info.GetObject() 28 ' refresh object on the client 29 _objectContainer.Ext().Refresh(obj, 2) 30 Next 31 End Sub 32 33 End Class 34End Namespace

Run the following method to see how the 2 clients work concurrently on the same object:

PushedUpdatesExample.cs: Run
01public void Run() 02 { 03 File.Delete(Db4oFileName); 04 IObjectServer server = Db4oFactory.OpenServer(Db4oFileName, Port); 05 try 06 { 07 server.GrantAccess(User, Password); 08 09 IObjectContainer client1 = OpenClient(); 10 IObjectContainer client2 = OpenClient(); 11 12 if (client1 != null && client2 != null) 13 { 14 try 15 { 16 // wait for the operations to finish 17 WaitForCompletion(); 18 19 // save pilot with client1 20 Car client1Car = new Car("Ferrari", 2006, new Pilot("Schumacher")); 21 client1.Set(client1Car); 22 client1.Commit(); 23 24 WaitForCompletion(); 25 26 // retrieve the same pilot with client2 27 Car client2Car = (Car)client2.Query(typeof(Car)).Next(); 28 System.Console.WriteLine(client2Car); 29 30 // modify the pilot with client1 31 client1Car.Model = 2007; 32 client1Car.Pilot = new Pilot("Hakkinnen"); 33 client1.Set(client1Car); 34 client1.Commit(); 35 36 WaitForCompletion(); 37 38 // client2Car has been automatically updated in 39 // the committed event handler because of the 40 // modification and the commit by client1 41 System.Console.WriteLine(client2Car); 42 43 WaitForCompletion(); 44 } 45 catch (Exception ex) 46 { 47 System.Console.WriteLine(ex.ToString()); 48 } 49 finally 50 { 51 CloseClient(client1); 52 CloseClient(client2); 53 } 54 } 55 } 56 catch (Exception ex) 57 { 58 System.Console.WriteLine(ex.ToString()); 59 } 60 finally 61 { 62 server.Close(); 63 } 64 }
PushedUpdatesExample.vb: Run
01Public Sub Run() 02 File.Delete(Db4oFileName) 03 Dim server As IObjectServer = Db4oFactory.OpenServer(Db4oFileName, Port) 04 Try 05 server.GrantAccess(User, Password) 06 Dim client1 As IObjectContainer = OpenClient 07 Dim client2 As IObjectContainer = OpenClient 08 If Not (client1 Is Nothing) AndAlso Not (client2 Is Nothing) Then 09 Try 10 ' wait for the operations to finish 11 WaitForCompletion() 12 13 'save pilot with client1 14 Dim client1Car As Car = New Car("Ferrari", 2006, New Pilot("Schumacher")) 15 client1.Set(client1Car) 16 client1.Commit() 17 WaitForCompletion() 18 19 ' retrieve the same pilot with client2 20 Dim client2Car As Car = CType(client2.Query(GetType(Car)).Next, Car) 21 System.Console.WriteLine(client2Car) 22 23 ' modify the pilot with client1 24 client1Car.Model = 2007 25 client1Car.Pilot = New Pilot("Hakkinnen") 26 client1.Set(client1Car) 27 client1.Commit() 28 WaitForCompletion() 29 30 ' client2Car has been automatically updated in 31 ' the committed event handler because of the 32 ' modification and the commit by client1 33 System.Console.WriteLine(client2Car) 34 WaitForCompletion() 35 Catch ex As Exception 36 System.Console.WriteLine(ex.ToString) 37 Finally 38 CloseClient(client1) 39 CloseClient(client2) 40 End Try 41 End If 42 Catch ex As Exception 43 System.Console.WriteLine(ex.ToString) 44 Finally 45 server.Close() 46 End Try 47 End Sub

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:

PushedUpdatesExample.cs: CloseClient
01private void CloseClient(IObjectContainer client) 02 { 03 // remove listeners before shutting down 04 if (clientListeners[client] != null) 05 { 06 IEventRegistry eventRegistry = EventRegistryFactory.ForObjectContainer(client); 07 eventRegistry.Committed -= (CommitEventHandler)clientListeners[client]; 08 clientListeners.Remove(client); 09 } 10 client.Close(); 11 }

PushedUpdatesExample.vb: CloseClient
1Private Sub CloseClient(ByVal client As IObjectContainer) 2 ' remove listeners before shutting down 3 If Not (clientListeners(client) Is Nothing) Then 4 Dim eventRegistry As IEventRegistry = EventRegistryFactory.ForObjectContainer(client) 5 RemoveHandler eventRegistry.Committed, AddressOf CType(clientListeners(client), CommittedEventHandler).OnCommitted 6 clientListeners.Remove(client) 7 End If 8 client.Close() 9 End Sub