[[PageOutline(1-3)]] = !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). {{{ #!div style="clear:both;" }}} = How to Install = {{{ #!div class="wiki_infotable" style="float:right;" ||'''Environment'''|| || || [[Image(media/icons/custom:squeak_16.png, title="Recommended Squeak Version", nolink, right)]] || 4.1, 4.2 Trunk || || [[Image(media/icons/silk:application_home.png, title="Recommended Squeak VM Version", nolink, right)]] || 4.0.2 (Win) || || [[Image(media/icons/silk:cog.png, title="Recommended Cog VM Version", nolink, right)]] || ''not supported'' || ||'''Sources'''|| || || [[Image(media/icons/silk:script_gear.png, title="Metacello Configuration", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/MetacelloRepository/ ConfigurationOfSqSync] || || [[Image(media/icons/silk:database.png, title="Repository", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SqSync/ SqSync] || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || !SqSync || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || SY-!AddressBook (optional) || || [[Image(media/icons/silk:package.png, title="Needed Packages from the Repository", nolink, right)]] || SY-!ProjectAnalyzation (optional) || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:methodwrappers Method Wrappers] || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:signals Signals] || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [wiki:widgets Widgets] (optional) || || [[Image(media/icons/silk:bullet_go.png, title="Dependents", nolink, right)]] || [http://www.squeaksource.com/ProcessLocalStorage.html ProcessLocalStorage] || ||'''Misc'''|| || || [[Image(media/icons/silk:world.png, title="Website", nolink, right)]] || [http://www.hpi.uni-potsdam.de/hirschfeld/squeaksource/SqSync.html 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 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 * `SqSync-Tests-Support` ... mocks {{{ #!div style="clear:both;" }}} = 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 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 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 [wiki:signals 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 = [[Image(media/icons/silk:user.png, nolink)]] To date, the following people contributed to this project: * Robert Krahn * Stefan Richter * Christopher Schuster * Bastian Steinert * Marcel Taeumel