db4o Developer Community

db4o open source object database, native to Java and .NET
Welcome to db4o Developer Community Sign in | Join
in Search
More Search Options

Product News from the Core Team

This blog features product news right from the core developer team, once new features and functions get checked into Subversion, available as Continuous Build every 2 hours.

Committed Callbacks and Pushed Updates

Committed callbacks have just been completed and checked in to SVN. You can now register a listener for the committed event and use it to keep all clients informed about changes committed by other clients.

The syntax to register a committed listener looks like this:

// Java 

EventRegistry eventRegistry = EventRegistryFactory.forObjectContainer(objectContainer);
eventRegistry.committed().addListener(new EventListener4() {
   public void onEvent(Event4 e, EventArgs args) {

   }
});


// .NET

IEventRegistry registry = EventRegistryFactory.ForObjectContainer(Db());
registry.Committed += new CommitEventHandler(OnEvent);

public void OnEvent(object sender, Db4objects.Db4o.Events.CommitEventArgs args)
{

}

Now where and why is this new functionality helpful?

The really cool usecase is that you can keep objects on all clients in sync with committed changes, if you refresh objects from within the listener. That way you can make sure that the local cache always is updated automatically and you never ever again have to call #refresh() upon retrieval of objects in a query.

When working with the new functionality in a Client/Server setup you should be aware of the fact that the listener will be called from a different thread than your normal application so you will have to use synchronisation to guard your application from objects being modified behind it's back.

It also is good practice to remove listeners before shutting down your clients.

 

Below is a complete runnable sample application to demonstrate the new functionality. Notice the changed output when printing the same 'client2Car' object twice without any obvious intermediate modification:

Car: Ferrari 2006 Driver: Schumacher
Car: Ferrari 2007 Driver: Hakkinnen

 

And here is the code sample:

package com.db4o.db4ounit.common.assorted;

import java.io.*;
import com.db4o.*;
import com.db4o.events.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;


public class PushedUpdatesSample {
	
   private static final String FILE = "pushedUpdates.db4o";
	
   private static final int PORT = 4440;
	
   private static final String USER = "db4o";
	
   private static final String PASSWORD = "db4o";
	
   public static void main(String[] args) throws IOException {
      new PushedUpdatesSample().run();
   }
	
   public void run() throws IOException{
      new File(FILE).delete();
      ObjectServer server = Db4o.openServer(FILE, PORT);
      server.grantAccess(USER, PASSWORD);
		
      ObjectContainer client1 = openClient();
      ObjectContainer client2 = openClient();
		
      waitForCompletion();
		
      Car client1Car = new Car("Ferrari", 2006, new Driver("Schumacher"));
      client1.set(client1Car);
      client1.commit();
		
      waitForCompletion();
		
      Car client2Car = (Car) client2.query(Car.class).next();
      System.out.println(client2Car);
		
      client1Car.model = 2007;
      client1Car.driver = new Driver("Hakkinnen");
      client1.set(client1Car);
      client1.commit();
		
      waitForCompletion();
		
		
      // This is the beef of this example.
      // client2Car has been automatically updated because of the
      // modification and the commit by client1 
      System.out.println(client2Car);
		
		
      waitForCompletion();
		
      client1.close();
      client2.close();
      server.close();
   }
	
   private ObjectContainer openClient() throws IOException {
      ObjectContainer client = Db4o.openClient("localhost", PORT, USER, PASSWORD);
      EventListener4 committedEventListener = createCommittedEventListener(client);
      EventRegistry eventRegistry = EventRegistryFactory.forObjectContainer(client);
      eventRegistry.committed().addListener(committedEventListener);
      return client; 
   }
	
   private EventListener4 createCommittedEventListener(final ObjectContainer objectContainer){
      return new EventListener4() {
         public void onEvent(Event4 e, EventArgs args) {
            ObjectInfoCollection updated = ((CommitEventArgs)args).updated();
            Iterator4 infos = updated.iterator();
            while(infos.moveNext()){
               ObjectInfo info = (ObjectInfo) infos.current();
               Object obj = info.getObject();
               objectContainer.ext().refresh(obj, 2);
            }
         }
      };
   }
	
   private void waitForCompletion(){
      try {
         Thread.sleep(1000);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   public static class Car {
		
      public String name;
		
      public int model;
		
      public Driver driver;
		
      public Car(String name, int model, Driver driver) {
         this.name = name;
         this.model = model;
         this.driver = driver;
      }
		
      public String toString() {
         return "Car: " + name + " " + model + " Driver: " + driver.name; 
      }
		
   }
	
   public static class Driver {
		
      public String name;

      public Driver(String name) {
         this.name = name;
      }
		
   }

}
Published Monday, April 23, 2007 11:02 PM by Carl Rosenberger
Filed under: ,


Comments

 

db4o Newsletter said:

Welcome to the May Newsletter! 6.2.501 adds Committed Callbacks and Pushed Updates, improves C# readability

April 26, 2007 12:27 AM
 

duke@resolve-systems.com said:

It would be nice if this lateral pushed updates is hidden within the db4o library and users dont have to implement it themselves and can simply configure that they want pushed updates.

Duke

April 26, 2007 4:02 PM
 

Carl Rosenberger said:

Thank you for the suggestion, Duke!

The problem with hiding the functionality from the application:

Since the update necessarily happens from a different thread, you would be guaranteed to get unexpected results from synchronisation issues between your application thread and the update thread, if you were not aware of "things going on behind your back". That's why we think it is better if you integrate the update code into your application, because you know much better when the update thread is OK to change your objects. This way we make sure that you know that there is an issue you have to solve to get this functionality to work nicely.

May 2, 2007 12:00 PM
 

llucifer said:

The implementation you show in the example contains objectContainer.ext().refresh(obj, 2). The refresh  depth is hard wired to 2. Will there be a way to determine the needed refresh depth? Can there be a way to actually only refresh the objects in the objects graph referenced by the obj committed which were relly committed to the database?

May 5, 2007 9:23 AM
 

Carl Rosenberger said:

The hardwired depth of '2' for refresh tries to make sure that objects of classes with internally installed translators get refreshed completely, especially collections. It may well be possible that '1' already is fully sufficient for all collection types that you are using but I wanted to make sure that this first blog entry is usable for the most widest range of usecases.

May 5, 2007 9:55 AM
 

db4o Newsletter said:

The efficiency of Transparent Persistence is added to .NET LINQ provider New “Stable” Release 6.4 is

May 22, 2008 10:17 PM
 

db4o Newsletter said:

db4o 7.4 Development Release is available for immediate download ! A R T I C L E S db4o Announces its

June 28, 2008 11:39 PM
Anonymous comments are disabled