| # 9. Client/Server Now that we have seen how transactions work in db4o conceptually, we are prepared to tackle concurrently executing transactions. We start by preparing our database in the familiar way.
`[setFirstCar] Pilot pilot = new Pilot("Rubens Barrichello", 99); Car car = new Car("BMW"); car.Pilot = pilot; db.set(car);`
`[setSecondCar] Pilot pilot = new Pilot("Michael Schumacher", 100); Car car = new Car("Ferrari"); car.Pilot = pilot; db.set(car);`
## 9.1. Embedded server From the API side, there's no real difference between transactions executing concurrently within the same VM and transactions executed against a remote server. To use concurrent transactions within a single VM, we just open a db4o server on our database file, directing it to run on port 0, thereby declaring that no networking will take place.
`[accessLocalServer] ObjectServer server = Db4o.openServer(Util.YapFileName, 0); try { ObjectContainer client = server.openClient(); // Do something with this client, or open more clients client.close(); } finally { server.close(); }`
Again, we will delegate opening and closing the server to our environment to focus on client interactions.
`[queryLocalServer] ObjectContainer client = server.openClient(); listResult(client.get(new Car(null))); client.close();`
OUTPUT: 2 BMW[Rubens Barrichello/99]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:16] Connection closed by client %. [db4o 5.0.018 2006-01-25 22:22:16] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:16] '/tmp/formula149558.yap' closed

The transaction level in db4o is read committed . However, each client container maintains its own weak reference cache of already known objects. To make all changes committed by other clients immediately, we have to explicitly refresh known objects from the server. We will delegate this task to a specialized version of our listResult() method.

`public static void listRefreshedResult(ObjectContainer container, ObjectSet items, int depth) { Console.WriteLine(items.size()); while (items.hasNext()) { object item = items.next(); container.ext().refresh(item, depth); Console.WriteLine(item); } }`

`[demonstrateLocalReadCommitted] ObjectContainer client1 =server.openClient(); ObjectContainer client2 =server.openClient(); Pilot pilot = new Pilot("David Coulthard", 98); ObjectSet result = client1.get(new Car("BMW")); Car car = (Car)result.next(); car.Pilot = pilot; client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.commit(); listResult(client1.get(typeof(Car))); listRefreshedResult(client2, client2.get(typeof(Car)), 2); client1.close(); client2.close();`

OUTPUT: 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Rubens Barrichello/99]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:17] Connection closed by client %. [db4o 5.0.018 2006-01-25 22:22:17] Connection closed by client %. [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' closed

Simple rollbacks just work as you might expect now.

`[demonstrateLocalRollback] ObjectContainer client1 = server.openClient(); ObjectContainer client2 = server.openClient(); ObjectSet result = client1.get(new Car("BMW")); Car car = (Car)result.next(); car.Pilot = new Pilot("Someone else", 0); client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.rollback(); client1.ext().refresh(car, 2); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close();`

OUTPUT: 2 BMW[Someone else/0]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:17] Connection closed by client %. [db4o 5.0.018 2006-01-25 22:22:17] Connection closed by client %. [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' closed

## 9.2. Networking

From here it's only a small step towards operating db4o over a TCP/IP network. We just specify a port number greater than zero and set up one or more accounts for our client(s).

`[accessRemoteServer] ObjectServer server = Db4o.openServer(Util.YapFileName, ServerPort); server.grantAccess(ServerUser, ServerPassword); try { ObjectContainer client = Db4o.openClient("localhost", ServerPort, ServerUser, ServerPassword); // Do something with this client, or open more clients client.close(); } finally { server.close(); }`

The client connects providing host, port, user name and password.

`[queryRemoteServer] ObjectContainer client = Db4o.openClient("localhost", port, user, password); listResult(client.get(new Car(null))); client.close();`

OUTPUT: [db4o 5.0.018 2006-01-25 22:22:17] Server listening on port: '56128' [db4o 5.0.018 2006-01-25 22:22:17] Client 'user' connected. 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:17] Connection closed by client 'user'. [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:17] '/tmp/formula149558.yap' closed

Everything else is absolutely identical to the local server examples above.

`[demonstrateRemoteReadCommitted] ObjectContainer client1 = Db4o.openClient("localhost", port, user, password); ObjectContainer client2 = Db4o.openClient("localhost", port, user, password); Pilot pilot = new Pilot("Jenson Button", 97); ObjectSet result = client1.get(new Car(null)); Car car = (Car)result.next(); car.Pilot = pilot; client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.commit(); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close();`

OUTPUT: [db4o 5.0.018 2006-01-25 22:22:17] Server listening on port: '56128' [db4o 5.0.018 2006-01-25 22:22:17] Client 'user' connected. [db4o 5.0.018 2006-01-25 22:22:17] Client 'user' connected. 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[David Coulthard/98]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:18] Connection closed by client 'user'. [db4o 5.0.018 2006-01-25 22:22:18] Connection closed by client 'user'. [db4o 5.0.018 2006-01-25 22:22:18] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:18] '/tmp/formula149558.yap' closed

`[demonstrateRemoteRollback] ObjectContainer client1 = Db4o.openClient("localhost", port, user, password); ObjectContainer client2 = Db4o.openClient("localhost", port, user, password); ObjectSet result = client1.get(new Car(null)); Car car = (Car)result.next(); car.Pilot = new Pilot("Someone else", 0); client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.rollback(); client1.ext().refresh(car,2); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close();`

OUTPUT: [db4o 5.0.018 2006-01-25 22:22:18] Server listening on port: '56128' [db4o 5.0.018 2006-01-25 22:22:18] Client 'user' connected. [db4o 5.0.018 2006-01-25 22:22:18] Client 'user' connected. 2 BMW[Someone else/0]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 2 BMW[Jenson Button/97]/0 Ferrari[Michael Schumacher/100]/0 [db4o 5.0.018 2006-01-25 22:22:18] Connection closed by client 'user'. [db4o 5.0.018 2006-01-25 22:22:18] Connection closed by client 'user'. [db4o 5.0.018 2006-01-25 22:22:18] '/tmp/formula149558.yap' close request [db4o 5.0.018 2006-01-25 22:22:18] '/tmp/formula149558.yap' closed

## 9.3. Out-of-band signalling

Sometimes a client needs to send a special message to a server in order to tell the server to do something. The server may need to be signalled to perform a defragment or it may need to be signalled to shut itself down gracefully.
This is configured by calling setMessageRecipient(), passing the object that will process client-initiated messages.

`public void runServer() { lock(this) { ObjectServer db4oServer = Db4o.openServer(FILE, PORT); db4oServer.grantAccess(USER, PASS); // Using the messaging functionality to redirect all // messages to this.processMessage db4oServer.ext().configure().setMessageRecipient(this); try { if (! stop) { // wait forever for notify() from close() Monitor.Wait(this); } } catch (Exception e) { Console.WriteLine(e.ToString()); } db4oServer.close(); } }`

The message is received and processed by a processMessage() method:

`public void processMessage(ObjectContainer con, object message) { if (message is StopServer) { close(); } }`

Db4o allows a client to send an arbitrary signal or message to a server by sending a plain Java object to the server. The server will receive a callback message, including the object that came from the client. The server can interpret this message however it wants.

`public static void Main(string[] args) { ObjectContainer objectContainer = null; try { // connect to the server objectContainer = Db4o.openClient(HOST, PORT, USER, PASS); } catch (Exception e) { Console.WriteLine(e.ToString()); } if (objectContainer != null) { // get the messageSender for the ObjectContainer MessageSender messageSender = objectContainer.ext() .configure().getMessageSender(); // send an instance of a StopServer object messageSender.send(new StopServer()); // close the ObjectContainer objectContainer.close(); } }`

## 9.4. Putting it all together: a simple but complete db4o server

Let's put all of this information together now to implement a simple standalone db4o server with a special client that can tell the server to shut itself down gracefully on demand.
First, both the client and the server need some shared configuration information. We will provide this using an interface:

`namespace com.db4o.f1.chapter5 { /// <summary> /// Configuration used for StartServer and StopServer. /// </summary> public class ServerConfiguration { /// <summary> /// the host to be used. /// If you want to run the client server examples on two computers, /// enter the computer name of the one that you want to use as server. /// </summary> public const string HOST = "localhost"; /// <summary> /// the database file to be used by the server. /// </summary> public const string FILE = "formula1.yap"; /// <summary> /// the port to be used by the server. /// </summary> public const int PORT = 4488; /// <summary> /// the user name for access control. /// </summary> public const string USER = "db4o"; /// <summary> /// the pasword for access control. /// </summary> public const string PASS = "db4o"; } }`

Now we'll create the server:

`using System; using System.Threading; using com.db4o; using com.db4o.messaging; using j4o.lang; namespace com.db4o.f1.chapter5 { /// <summary> /// starts a db4o server with the settings from ServerConfiguration. /// This is a typical setup for a long running server. /// The Server may be stopped from a remote location by running /// StopServer. The StartServer instance is used as a MessageRecipient /// and reacts to receiving an instance of a StopServer object. /// Note that all user classes need to be present on the server side /// and that all possible Db4o.configure() calls to alter the db4o /// configuration need to be executed on the client and on the server. /// </summary> public class StartServer : ServerConfiguration, MessageRecipient { /// <summary> /// setting the value to true denotes that the server should be closed /// </summary> private bool stop = false; /// <summary> /// starts a db4o server using the configuration from /// ServerConfiguration. /// </summary> public static void Main(string[] arguments) { new StartServer().runServer(); } /// <summary> /// opens the ObjectServer, and waits forever until close() is called /// or a StopServer message is being received. /// </summary> public void runServer() { lock(this) { ObjectServer db4oServer = Db4o.openServer(FILE, PORT); db4oServer.grantAccess(USER, PASS); // Using the messaging functionality to redirect all // messages to this.processMessage db4oServer.ext().configure().setMessageRecipient(this); try { if (! stop) { // wait forever for notify() from close() Monitor.Wait(this); } } catch (Exception e) { Console.WriteLine(e.ToString()); } db4oServer.close(); } } /// <summary> /// messaging callback /// see com.db4o.messaging.MessageRecipient#processMessage() /// </summary> public void processMessage(ObjectContainer con, object message) { if (message is StopServer) { close(); } } /// <summary> /// closes this server. /// </summary> public void close() { lock(this) { stop = true; Monitor.PulseAll(this); } } } }`

And last but not least, the client that stops the server.

`using System; using com.db4o; using com.db4o.messaging; namespace com.db4o.f1.chapter5 { /// <summary> /// stops the db4o Server started with StartServer. /// This is done by opening a client connection /// to the server and by sending a StopServer object as /// a message. StartServer will react in it's /// processMessage method. /// </summary> public class StopServer : ServerConfiguration { /// <summary> /// stops a db4o Server started with StartServer. /// </summary> /// <exception cref="Exception" /> public static void Main(string[] args) { ObjectContainer objectContainer = null; try { // connect to the server objectContainer = Db4o.openClient(HOST, PORT, USER, PASS); } catch (Exception e) { Console.WriteLine(e.ToString()); } if (objectContainer != null) { // get the messageSender for the ObjectContainer MessageSender messageSender = objectContainer.ext() .configure().getMessageSender(); // send an instance of a StopServer object messageSender.send(new StopServer()); // close the ObjectContainer objectContainer.close(); } } } }`

## 9.5. Conclusion

That's it, folks. No, of course it isn't. There's much more to db4o we haven't covered yet: schema evolution, custom persistence for your classes, writing your own query objects, etc. The following, more loosely coupled chapters will look into more advanced db4o features.
This tutorial is work in progress. We will successively add chapters and incorporate feedback from the community into the existing chapters.
We hope that this tutorial has helped to get you started with db4o. How should you continue now?
- Browse the remaining chapters.
-(Interactive version only)While this tutorial is basically sequential in nature, try to switch back and forth between the chapters and execute the sample snippets in arbitrary order. You will be working with the same database throughout; sometimes you may just get stuck or even induce exceptions, but you can always reset the database via the console window.
- The examples we've worked through are included in your db4o distribution in full source code. Feel free to experiment with it.
- I you're stuck, see if the FAQ can solve your problem, browse the information on our web site, check if your problem is submitted to Bugzilla or visit our forums at http://forums.db4o.com/forums/.

## 9.6. Full source

`namespace com.db4o.f1.chapter5 { using System; using System.IO; using com.db4o; using com.db4o.f1; public class ClientServerExample : Util { public static void Main(string[] args) { File.Delete(Util.YapFileName); accessLocalServer(); File.Delete(Util.YapFileName); ObjectContainer db = Db4o.openFile(Util.YapFileName); try { setFirstCar(db); setSecondCar(db); } finally { db.close(); } configureDb4o(); ObjectServer server = Db4o.openServer(Util.YapFileName, 0); try { queryLocalServer(server); demonstrateLocalReadCommitted(server); demonstrateLocalRollback(server); } finally { server.close(); } accessRemoteServer(); server = Db4o.openServer(Util.YapFileName, ServerPort); server.grantAccess(ServerUser, ServerPassword); try { queryRemoteServer(ServerPort, ServerUser, ServerPassword); demonstrateRemoteReadCommitted(ServerPort, ServerUser, ServerPassword); demonstrateRemoteRollback(ServerPort, ServerUser, ServerPassword); } finally { server.close(); } } public static void setFirstCar(ObjectContainer db) { Pilot pilot = new Pilot("Rubens Barrichello", 99); Car car = new Car("BMW"); car.Pilot = pilot; db.set(car); } public static void setSecondCar(ObjectContainer db) { Pilot pilot = new Pilot("Michael Schumacher", 100); Car car = new Car("Ferrari"); car.Pilot = pilot; db.set(car); } public static void accessLocalServer() { ObjectServer server = Db4o.openServer(Util.YapFileName, 0); try { ObjectContainer client = server.openClient(); // Do something with this client, or open more clients client.close(); } finally { server.close(); } } public static void queryLocalServer(ObjectServer server) { ObjectContainer client = server.openClient(); listResult(client.get(new Car(null))); client.close(); } public static void configureDb4o() { Db4o.configure().objectClass(typeof(Car)).updateDepth(3); } public static void demonstrateLocalReadCommitted(ObjectServer server) { ObjectContainer client1 =server.openClient(); ObjectContainer client2 =server.openClient(); Pilot pilot = new Pilot("David Coulthard", 98); ObjectSet result = client1.get(new Car("BMW")); Car car = (Car)result.next(); car.Pilot = pilot; client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.commit(); listResult(client1.get(typeof(Car))); listRefreshedResult(client2, client2.get(typeof(Car)), 2); client1.close(); client2.close(); } public static void demonstrateLocalRollback(ObjectServer server) { ObjectContainer client1 = server.openClient(); ObjectContainer client2 = server.openClient(); ObjectSet result = client1.get(new Car("BMW")); Car car = (Car)result.next(); car.Pilot = new Pilot("Someone else", 0); client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.rollback(); client1.ext().refresh(car, 2); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close(); } public static void accessRemoteServer() { ObjectServer server = Db4o.openServer(Util.YapFileName, ServerPort); server.grantAccess(ServerUser, ServerPassword); try { ObjectContainer client = Db4o.openClient("localhost", ServerPort, ServerUser, ServerPassword); // Do something with this client, or open more clients client.close(); } finally { server.close(); } } public static void queryRemoteServer(int port, string user, string password) { ObjectContainer client = Db4o.openClient("localhost", port, user, password); listResult(client.get(new Car(null))); client.close(); } public static void demonstrateRemoteReadCommitted(int port, string user, string password) { ObjectContainer client1 = Db4o.openClient("localhost", port, user, password); ObjectContainer client2 = Db4o.openClient("localhost", port, user, password); Pilot pilot = new Pilot("Jenson Button", 97); ObjectSet result = client1.get(new Car(null)); Car car = (Car)result.next(); car.Pilot = pilot; client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.commit(); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close(); } public static void demonstrateRemoteRollback(int port, string user, string password) { ObjectContainer client1 = Db4o.openClient("localhost", port, user, password); ObjectContainer client2 = Db4o.openClient("localhost", port, user, password); ObjectSet result = client1.get(new Car(null)); Car car = (Car)result.next(); car.Pilot = new Pilot("Someone else", 0); client1.set(car); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.rollback(); client1.ext().refresh(car,2); listResult(client1.get(new Car(null))); listResult(client2.get(new Car(null))); client1.close(); client2.close(); } } }`

--
generated by Doctor courtesy of db4objects Inc.