Version 13 (modified by 13 years ago) ( diff ) | ,
---|
SqSync
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
Environment | |
4.1, 4.2 Trunk | |
4.0.2 (Win) | |
not supported | |
Sources | |
ConfigurationOfSqSync | |
SqSync |
SqSync | |
SY-AddressBook (optional) | |
SY-ProjectAnalyzation (optional) |
Method Wrappers | |
Signals | |
Widgets (optional) | |
ProcessLocalStorage | |
Misc | |
SqSync@SqueakSource |
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.
The package Widgets
is needed only for morphic controls and icons for the example application SyAddressBook2
which can be found in SY-AddressBook
package.
Signals
are used to provide a synchronization mechanism across process borders, i.e., while using multiple clients and servers in one Squeak image.
The SqSync
package contains several sub-packages:
SqSync-Shared
... shared command objects and exceptions as well as the network protocol processing classesSqSync-Client
... client-specific classesSqSync-Server
... server-specific classesSqSync-Repository
... shared model repository stuffSqSync-Repository-Inception
... everything you need to intercept method callsSqSync-Tools
... some customized versions of present classesSqSync-Tests
... tests =)SqSync-Tests-Model
... example model used in the testsSqSync-Tests-Support
... mocks
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.
Model Restrictions
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.
Client Connection
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 #disconnect
.
Creating Objects
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.
Weak References
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.
Acknowledgments
To date, the following people contributed to this project:
- Stefan Richter
- Christopher Schuster
- Bastian Steinert
- Marcel Taeumel