This framework enables applications to make use of shared objects in a distributed system with optimistic updates. It is well-suited for graphical user interfaces that rely on fast updates (< 200 ms).
How to Install
|4.1, 4.2 Trunk|
If you are on a recent 4.2 or Trunk image, you can use the Metacello configuration:
((Installer mc http: 'http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/') project: 'MetacelloRepository') install: 'ConfigurationOfSqSync'. ConfigurationOfSqSync load.
If you cannot use Metacello, or prefer to install manually, just load the
SqSync package from the Monticello HTTP repository.
Widgets is needed only for morphic controls and icons for the example application
SyAddressBook2 which can be found in
Signals are used to provide a synchronization mechanism across process borders, i.e., while using multiple clients and servers in one Squeak image.
SqSync package contains several sub-packages:
SqSync-Shared... shared command objects and exceptions as well as the network protocol processing classes
SqSync-Client... client-specific classes
SqSync-Server... server-specific classes
SqSync-Repository... shared model repository stuff
SqSync-Repository-Inception... everything you need to intercept method calls
SqSync-Tools... some customized versions of present classes
SqSync-Tests... tests =)
SqSync-Tests-Model... example model used in the tests
How to Use
Custom Domain Model
Create your own domain model via inheritance from
SqEntity. That domain model code has to be replicated to all clients and the server by yourself.
Do not use lazy initialization.
Make use of the
#initialize method in each model to set initial values. Failure to do so will result in each setter being recognized as changing the model and those changes will be replicated to other clients.
"Wrong." SyPerson>>friends ^ friends ifNil: [friends := OrderedCollection new] "Correct." SyPerson>>initialize super initialize. friends := OrderedCollection new. SyPerson>>friends ^ friends
Force method replication of
non-SqSync objects with <replicate> pragma.
If you use other objects apart from your domain models in your application, you will need to tell
SqSync explicitely which method call results in an object change and therefore needs to be replicated.
SyPerson>>addFriend: aPerson <replicate> self friends add: aPerson. "friends is OrderedCollection"
Do not initialize an object with
SqSync subtypes from within.
Call this a bug, but a client needs to set these objects from the outside so that proper method wrappers can be installed and the object's method calls will be replicated correctly.
"Wrong." SyPerson>>initialize super initialize. address := SyAddress new. "Correct." | myPerson | myPerson := SyPerson new. myPerson address: SyAddress new.
Creating the new
SyAddress instance during the initilization of
SyPerson does not register the address on the server as only the object creation (i.e.,
SyPerson new) is replicated to all clients.
Emitting signals in a model do not replicate resulting method calls.
This advice only applies if you work with Signals in your model. As model changes are processed in the Squeak UI process, a connection to a resulting model change is not replicated because everything is threated as one call.
SyPerson>>givenNameChanged: newName "Signal." self emit. SyPerson>>givenName: aString givenName: aString. self givenNameChanged: aString. ... self connect: myPerson signal: #givenNameChanged: toSelector: #updateTimeStamp. ... MyObject>>updateTimeStamp myPerson timestamp: TimeStamp now.
This is only a problem if used like in the example above. Every client sets his own timestamp in the model. There is not one client that sets the timestamp and every other clients gets this timestamp replicate. So pay attention to this.
Server Startup & Shutdown
To start a server that runs on port 22701 open a workspace and do:
myServer := SyServer new start.
To shutdown the server, just call
#stop. A server instance can and should be re-used as it holds the complete repository with all instance of the domain.
In any application you need an
SyClient object that connects to a server and so opens a TCP connection:
myClient := SyClient new connectToHostNamed: 'localhost' port: 22701. "or connectToHostNamed: '127.0.0.1' port: 22701."
You close the connection with
If your repository is empty, you will get now objects from the server. At first, you need to register and define a new root object:
myClient repository rootObject: PersonList new.
From this point on, you should work with your root object. If you already did register a root object, just get it after the client connected:
personList := myClient repository rootObject.
Further objects should only be added via the root object. Therefore the root object needs an interface to add other objects like this:
personList addPerson: Person new.
The new instance of
Person will be registered and is now part of the shared domain graph and visible for other clients.
However, it is possible to register and access objects in the global object list:
"Fetch the list of all registered objects." allDomainObjects := myClient repository all. "Register any object of type SqEntity." myServer repository register: Person new.
To allow garbage collection of shared objects, clients need to refrain from keeping strong references to objects that are reachable from the root object. You should never do this:
"Forbidden." myClient repository register: Person new.
Only the server keeps strong references to all objects.
How to Extend
Internal documentation. Show models, explain the architecture. Do anything that may help a new developer to extend this project.
To date, the following people contributed to this project:
- Robert Krahn
- Stefan Richter
- Christopher Schuster
- Bastian Steinert
- Marcel Taeumel