|
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]
Dim pilot As Pilot = New Pilot("Rubens Barrichello", 99)
Dim car As Car = New Car("BMW")
car.Pilot = pilot
db.[Set](car) |
[setSecondCar]
Dim pilot As Pilot = New Pilot("Michael Schumacher", 100)
Dim car As 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]
Dim server As ObjectServer = Db4oFactory.OpenServer(Util.YapFileName, 0)
Try
Dim client As ObjectContainer = server.OpenClient()
' Do something with this client, or open more clients
client.Close()
Finally
server.Close()
End Try |
Again, we will delegate opening and closing the server to our environment to focus on client interactions.
[queryLocalServer]
Dim client As ObjectContainer = server.OpenClient()
ListResult(client.[Get](New Car(Nothing)))
client.Close() |
OUTPUT: 2
Ferrari[Michael Schumacher/100]/0
BMW[Rubens Barrichello/99]/0
[db4o 5.5.1 2006-07-05 10:33:50]
Connection closed by client %.
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.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 Shared Sub ListRefreshedResult(ByVal container As ObjectContainer, ByVal items As ObjectSet, ByVal depth As Integer)
Console.WriteLine(items.Count)
For Each item As Object In items
container.Ext().Refresh(item, depth)
Console.WriteLine(item)
Next
End Sub |
[demonstrateLocalReadCommitted]
Dim client1 As ObjectContainer = server.OpenClient()
Dim client2 As ObjectContainer = server.OpenClient()
Dim pilot As Pilot = New Pilot("David Coulthard", 98)
Dim result As ObjectSet = client1.[Get](New Car("BMW"))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = pilot
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Commit()
ListResult(client1.[Get](GetType(Car)))
ListRefreshedResult(client2, client2.[Get](GetType(Car)), 2)
client1.Close()
client2.Close() |
OUTPUT: 2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[Rubens Barrichello/99]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
[db4o 5.5.1 2006-07-05 10:33:51]
Connection closed by client %.
[db4o 5.5.1 2006-07-05 10:33:51]
Connection closed by client %.
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.yap' closed
|
Simple rollbacks just work as you might expect now.
[demonstrateLocalRollback]
Dim client1 As ObjectContainer = server.OpenClient()
Dim client2 As ObjectContainer = server.OpenClient()
Dim result As ObjectSet = client1.[Get](New Car("BMW"))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = New Pilot("Someone else", 0)
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Rollback()
client1.Ext().Refresh(car, 2)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close() |
OUTPUT: 2
Ferrari[Michael Schumacher/100]/0
BMW[Someone else/0]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
[db4o 5.5.1 2006-07-05 10:33:51]
[db4o 5.5.1 2006-07-05 10:33:51]
Connection closed by client %.
Connection closed by client %.
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:51]
'/tmp/formula158157.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]
Dim server As ObjectServer = Db4oFactory.OpenServer(Util.YapFileName, ServerPort)
server.GrantAccess(ServerUser, ServerPassword)
Try
Dim client As ObjectContainer = Db4oFactory.OpenClient("localhost", ServerPort, ServerUser, ServerPassword)
' Do something with this client, or open more clients
client.Close()
Finally
server.Close()
End Try |
The client connects providing host, port, user name and password.
[queryRemoteServer]
Dim client As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
ListResult(client.[Get](New Car(Nothing)))
client.Close() |
OUTPUT: [db4o 5.5.1 2006-07-05 10:33:52]
Server listening on port: '56128'
[db4o 5.5.1 2006-07-05 10:33:52]
Client 'user' connected.
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
[db4o 5.5.1 2006-07-05 10:33:52]
Connection closed by client 'user'.
[db4o 5.5.1 2006-07-05 10:33:52]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:52]
'/tmp/formula158157.yap' closed
|
Everything else is absolutely identical to the local server examples above.
[demonstrateRemoteReadCommitted]
Dim client1 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim client2 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim pilot As Pilot = New Pilot("Jenson Button", 97)
Dim result As ObjectSet = client1.[Get](New Car(Nothing))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = pilot
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Commit()
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close() |
OUTPUT: [db4o 5.5.1 2006-07-05 10:33:52]
Server listening on port: '56128'
[db4o 5.5.1 2006-07-05 10:33:52]
Client 'user' connected.
[db4o 5.5.1 2006-07-05 10:33:52]
Client 'user' connected.
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
2
Ferrari[Michael Schumacher/100]/0
BMW[David Coulthard/98]/0
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
[db4o 5.5.1 2006-07-05 10:33:53]
Connection closed by client 'user'.
[db4o 5.5.1 2006-07-05 10:33:53]
Connection closed by client 'user'.
[db4o 5.5.1 2006-07-05 10:33:53]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:53]
'/tmp/formula158157.yap' closed
|
[demonstrateRemoteRollback]
Dim client1 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim client2 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim result As ObjectSet = client1.[Get](New Car(Nothing))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = New Pilot("Someone else", 0)
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Rollback()
client1.Ext().Refresh(car, 2)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close() |
OUTPUT: [db4o 5.5.1 2006-07-05 10:33:54]
Server listening on port: '56128'
[db4o 5.5.1 2006-07-05 10:33:54]
Client 'user' connected.
[db4o 5.5.1 2006-07-05 10:33:54]
Client 'user' connected.
2
Ferrari[Someone else/0]/0
BMW[David Coulthard/98]/0
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
2
Ferrari[Jenson Button/97]/0
BMW[David Coulthard/98]/0
[db4o 5.5.1 2006-07-05 10:33:54]
Connection closed by client 'user'.
[db4o 5.5.1 2006-07-05 10:33:54]
Connection closed by client 'user'.
[db4o 5.5.1 2006-07-05 10:33:54]
'/tmp/formula158157.yap' close request
[db4o 5.5.1 2006-07-05 10:33:54]
'/tmp/formula158157.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 Sub RunServer()
SyncLock Me
Dim db4oServer As ObjectServer = Db4oFactory.OpenServer(FILE, PORT)
db4oServer.GrantAccess(USER, PASS)
' Using the messaging functionality to redirect all
' messages to this.processMessage
db4oServer.Ext().Configure().SetMessageRecipient(Me)
Try
If Not [stop] Then
' wait forever for Notify() from Close()
Monitor.Wait(Me)
End If
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
db4oServer.Close()
End SyncLock
End Sub |
The message is received and processed by a processMessage() method:
Public Sub ProcessMessage(ByVal con As ObjectContainer, ByVal message As Object) Implements MessageRecipient.ProcessMessage
If TypeOf message Is StopServer Then
Close()
End If
End Sub |
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 Shared Sub Main(ByVal args As String())
Dim objectContainer As ObjectContainer = Nothing
Try
' connect to the server
objectContainer = Db4oFactory.OpenClient(HOST, PORT, USER, PASS)
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
If Not objectContainer Is Nothing Then
' get the messageSender for the ObjectContainer
Dim messageSender As MessageSender = objectContainer.Ext().Configure().GetMessageSender()
' send an instance of a StopServer object
messageSender.Send(New StopServer())
' close the ObjectContainer
objectContainer.Close()
End If
End Sub |
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 HOST As String = "localhost"
''' <summary>
''' the database file to be used by the server.
''' </summary>
Public Const FILE As String = "formula1.yap"
''' <summary>
''' the port to be used by the server.
''' </summary>
Public Const PORT As Integer = 4488
''' <summary>
''' the user name for access control.
''' </summary>
Public Const USER As String = "db4o"
''' <summary>
''' the pasword for access control.
''' </summary>
Public Const PASS As String = "db4o"
End Class
End Namespace
|
Now we'll create the server:
Imports System
Imports System.Threading
Imports com.db4o
Imports com.db4o.messaging
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
Inherits ServerConfiguration
Implements MessageRecipient
''' <summary>
''' setting the value to true denotes that the server should be closed
''' </summary>
Private [stop] As Boolean = False
''' <summary>
''' starts a db4o server using the configuration from
''' ServerConfiguration.
''' </summary>
Public Shared Sub Main(ByVal arguments As String())
Dim server As New StartServer
server.RunServer()
End Sub
''' <summary>
''' opens the ObjectServer, and waits forever until Close() is called
''' or a StopServer message is being received.
''' </summary>
Public Sub RunServer()
SyncLock Me
Dim db4oServer As ObjectServer = Db4oFactory.OpenServer(FILE, PORT)
db4oServer.GrantAccess(USER, PASS)
' Using the messaging functionality to redirect all
' messages to this.processMessage
db4oServer.Ext().Configure().SetMessageRecipient(Me)
Try
If Not [stop] Then
' wait forever for Notify() from Close()
Monitor.Wait(Me)
End If
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
db4oServer.Close()
End SyncLock
End Sub
''' <summary>
''' messaging callback
''' see com.db4o.messaging.MessageRecipient#ProcessMessage()
''' </summary>
Public Sub ProcessMessage(ByVal con As ObjectContainer, ByVal message As Object) Implements MessageRecipient.ProcessMessage
If TypeOf message Is StopServer Then
Close()
End If
End Sub
''' <summary>
''' closes this server.
''' </summary>
Public Sub Close()
SyncLock Me
[stop] = True
Monitor.PulseAll(Me)
End SyncLock
End Sub
End Class
End Namespace
|
And last but not least, the client that stops the server.
Imports System
Imports com.db4o
Imports 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
Inherits ServerConfiguration
''' <summary>
''' stops a db4o Server started with StartServer.
''' </summary>
''' <exception cref="Exception" />
Public Shared Sub Main(ByVal args As String())
Dim objectContainer As ObjectContainer = Nothing
Try
' connect to the server
objectContainer = Db4oFactory.OpenClient(HOST, PORT, USER, PASS)
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
If Not objectContainer Is Nothing Then
' get the messageSender for the ObjectContainer
Dim messageSender As MessageSender = objectContainer.Ext().Configure().GetMessageSender()
' send an instance of a StopServer object
messageSender.Send(New StopServer())
' close the ObjectContainer
objectContainer.Close()
End If
End Sub
End Class
End Namespace
|
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
Imports System.IO
Imports com.db4o
Namespace com.db4o.f1.chapter5
Public Class ClientServerExample
Inherits Util
Public Shared Sub Main(ByVal args As String())
File.Delete(Util.YapFileName)
AccessLocalServer()
File.Delete(Util.YapFileName)
Dim db As ObjectContainer = Db4oFactory.OpenFile(Util.YapFileName)
Try
SetFirstCar(db)
SetSecondCar(db)
Finally
db.Close()
End Try
ConfigureDb4o()
Dim server As ObjectServer = Db4oFactory.OpenServer(Util.YapFileName, 0)
Try
QueryLocalServer(server)
DemonstrateLocalReadCommitted(server)
DemonstrateLocalRollback(server)
Finally
server.Close()
End Try
AccessRemoteServer()
server = Db4oFactory.OpenServer(Util.YapFileName, ServerPort)
server.GrantAccess(ServerUser, ServerPassword)
Try
QueryRemoteServer(ServerPort, ServerUser, ServerPassword)
DemonstrateRemoteReadCommitted(ServerPort, ServerUser, ServerPassword)
DemonstrateRemoteRollback(ServerPort, ServerUser, ServerPassword)
Finally
server.Close()
End Try
End Sub
Public Shared Sub SetFirstCar(ByVal db As ObjectContainer)
Dim pilot As Pilot = New Pilot("Rubens Barrichello", 99)
Dim car As Car = New Car("BMW")
car.Pilot = pilot
db.[Set](car)
End Sub
Public Shared Sub SetSecondCar(ByVal db As ObjectContainer)
Dim pilot As Pilot = New Pilot("Michael Schumacher", 100)
Dim car As Car = New Car("Ferrari")
car.Pilot = pilot
db.[Set](car)
End Sub
Public Shared Sub AccessLocalServer()
Dim server As ObjectServer = Db4oFactory.OpenServer(Util.YapFileName, 0)
Try
Dim client As ObjectContainer = server.OpenClient()
' Do something with this client, or open more clients
client.Close()
Finally
server.Close()
End Try
End Sub
Public Shared Sub QueryLocalServer(ByVal server As ObjectServer)
Dim client As ObjectContainer = server.OpenClient()
ListResult(client.[Get](New Car(Nothing)))
client.Close()
End Sub
Public Shared Sub ConfigureDb4o()
Db4oFactory.Configure().ObjectClass(GetType(Car)).UpdateDepth(3)
End Sub
Public Shared Sub DemonstrateLocalReadCommitted(ByVal server As ObjectServer)
Dim client1 As ObjectContainer = server.OpenClient()
Dim client2 As ObjectContainer = server.OpenClient()
Dim pilot As Pilot = New Pilot("David Coulthard", 98)
Dim result As ObjectSet = client1.[Get](New Car("BMW"))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = pilot
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Commit()
ListResult(client1.[Get](GetType(Car)))
ListRefreshedResult(client2, client2.[Get](GetType(Car)), 2)
client1.Close()
client2.Close()
End Sub
Public Shared Sub DemonstrateLocalRollback(ByVal server As ObjectServer)
Dim client1 As ObjectContainer = server.OpenClient()
Dim client2 As ObjectContainer = server.OpenClient()
Dim result As ObjectSet = client1.[Get](New Car("BMW"))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = New Pilot("Someone else", 0)
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Rollback()
client1.Ext().Refresh(car, 2)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close()
End Sub
Public Shared Sub AccessRemoteServer()
Dim server As ObjectServer = Db4oFactory.OpenServer(Util.YapFileName, ServerPort)
server.GrantAccess(ServerUser, ServerPassword)
Try
Dim client As ObjectContainer = Db4oFactory.OpenClient("localhost", ServerPort, ServerUser, ServerPassword)
' Do something with this client, or open more clients
client.Close()
Finally
server.Close()
End Try
End Sub
Public Shared Sub QueryRemoteServer(ByVal port As Integer, ByVal user As String, ByVal password As String)
Dim client As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
ListResult(client.[Get](New Car(Nothing)))
client.Close()
End Sub
Public Shared Sub DemonstrateRemoteReadCommitted(ByVal port As Integer, ByVal user As String, ByVal password As String)
Dim client1 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim client2 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim pilot As Pilot = New Pilot("Jenson Button", 97)
Dim result As ObjectSet = client1.[Get](New Car(Nothing))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = pilot
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Commit()
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close()
End Sub
Public Shared Sub DemonstrateRemoteRollback(ByVal port As Integer, ByVal user As String, ByVal password As String)
Dim client1 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim client2 As ObjectContainer = Db4oFactory.OpenClient("localhost", port, user, password)
Dim result As ObjectSet = client1.[Get](New Car(Nothing))
Dim car As Car = DirectCast(result.[Next](), Car)
car.Pilot = New Pilot("Someone else", 0)
client1.[Set](car)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Rollback()
client1.Ext().Refresh(car, 2)
ListResult(client1.[Get](New Car(Nothing)))
ListResult(client2.[Get](New Car(Nothing)))
client1.Close()
client2.Close()
End Sub
End Class
End Namespace
|
--
generated by Doctor courtesy of db4objects Inc.