Livespace Development Tips & Gotchas
None of the methods that change Livespaces databeans are thread-safe: this includes setValue ()
, getValue ()
and entity registry add and remove methods. This means you may experience variable behaviour if you don't synchronize access to beans/entities that are in a Livespace container due to the fact that Livespace updates are coming in on a separate thread. Variable behaviour most often happens when you have rapidly changing property values.
In general, read operations (getValue ()
) don't need to be synchronized, but if you modify a published object, you should hold the lock on the object's container's entity registry first. e.g.
EntityClient client = new EntityClient (room, elvin, "room"); client.waitForEntity (1); RoomEntity room = (RoomEntity)client.singleton (); synchronized (room.mutex ()) { room.setValue ("Description", "A room"); room.setValue ("Web page", "http://www.livespace.org"); } ...
For user interface applications (i.e. dashboard panels etc), you can reduce or eliminate the need to worry about threading issues by making entity updates occur within the UI thread. This happens automatically for any entity containers created inside the UI thread, and can be enabled for containers in general by using the updateContainerFromUI ()
method on IDashboard
or SWTRuntime
. For example:
// from livespace.ui.clipboard IDashboard dashboard = (IDashboard)dependencies.value ("dashboard"); ClipboardEntity clipboard = (ClipboardEntity)dependencies.value ("clipboard"); // clipboard container was created by // SingleEntityDependency class and won't be updated // in UI thread unless we do this dashboard.updateContainerFromUI (clipboard.container); clipboardPanel = new ClipboardPanel (dashboard, clipboard);
waitForEntity()
on the SWT threadAnother gotcha is when creating an EntityClient
on the SWT thread. Although EntityContainers
on the SWT thread are updated on the SWT thread, if you call .waitForEntity()
on your client container, it will block the SWT thread, preventing any updates from Elvin from being executed (as they're pushed onto the end of the SWT queue, which isn't being processed because you're waiting). Attach a listener to the container's EntityRegistry
and react to it asynchronously or inject the reference using the Dependency
system instead.
For various reasons lists (e.g. ListDataObject
) do not work in distributed data object land. This is due to changes not being idempotent and the fact that list indexes change when you insert or delete items.
For collections of complex objects, you should use EntityRegistry
's of Entity-derived objects. This allows the objects in registry set to be identified and addressed by a global ID. For collections of simple objects (strings, numbers, date's, etc, anything that has a defined equals ()
and hashCode ()
) you should use SetDataObject.
For collections where ordering is required, consider using an EntityList
. Or, even better, consider whether you really need ordering at the data level or whether the ordering would be better done at the presentation layer.
When you want to react to changes to an EntityRegistry
including additions and removals of Entities
as soon as an EntityContainer
is created, the easiest way to do this is to create the EntityRegistry
the EntityContainer
will use and add your listener to it before the EntityContainer
is created. You might want to do this when a GUI is showing the available Entities
.
EntityRegistry registry = new EntityRegistry (); registry.addPropertyListener (myListener); EntityClient client = new EntityClient (elvin, room, "computer", registry);
When you change an Entity
property, try to make sure you don't make multiple changes to the same property in quick succession. Because the Livespace protocol is optimistic in that when a client makes a change to a property, it expects the change to be made. If two changes are made in quick succession, the server may miss the second change and revert the property to the first change unexpectedly.
foo
property to "bar".foo
property to "boo".foo
is now "bar".foo
from "bar" to "boo" but now it's "bar" again.