Template Examples - Locker
Robert Wenzel, Engineer, Potix Corporation
January XX, 2018
ZK 8.5
Introduction
What we want
Our goal is to avoid multiple users from editing the same resource simultaneously we need the following functionalities.
- lock / unlock a resource
- e.g. obtain and release exclusive access to edit a specific objects properties
- observe a resource for owner changes (LockEvents)
- it should be possible to observe the same resource with multiple concurrent users
- react to LockEvents and update the UI
- immediately indicate availability/ownership/unavailability of a resource to each observing user
First we need a way to indicate our intention that we want to edit a resource and obtain a lock and vice versa return the lock (unlock) when we are done editing. This is only useful if other users are aware of that condition so they don't attempt concurrent editing (and potentially losing data or producing inconsistent merged results) - means we need to observe the current lock status and finally react in the UI. e.g. by changing labels, styles, disabling/hiding/removing/adding appropriate controls.
Simple example
Before going into details here a small example illustrating what we are trying to achieve.
Here 2 users in 2 separate browsers are seeing (observing) the same resource with the initial information that it's currently not locked by anyone. As soon as User-1 locks the resource it becomes editable for him. At the same the lock button disappears for the User-2 - replaced by a message showing the lock status and owner. When unlocking the resource it becomes available for both users again. Then User-2 repeats the cycle. As simple as that.
How does it work
The example above is implemented in simpleLockable.zul and SimpleLockableViewModel.java shouldn't contain too many surprises - Which is the intention of this article -> to show a simple way to achieve that.
<zk>
<style>
.lockIndicator { padding: 12px; display: inline-block; border-radius: 15px; }
.lockIndicator .z-label { color: white; font-weight: bold; }
.lockIndicator.OWNED { background-color: LimeGreen; }
.lockIndicator.AVAILABLE { background-color: LightSteelBlue; }
.lockIndicator.UNAVAILABLE { background-color: Crimson; }
</style>
<div viewModel="@id('vm') @init('zk.example.template.locker.SimpleLockableViewModel')">
I am '${vm.username}'.
<separator/>
This is
<div sclass="@load(('lockIndicator ' += vm.lockable.status))">
<choose>
<when test="@load(vm.lockable.status eq 'OWNED')">
<textbox value="@bind(vm.lockable.resource.value)"/>
</when>
<otherwise>
<label value="@load(vm.lockable.resource.value)"/>
</otherwise>
</choose>
</div>
<apply template="@load(vm.lockable.status)">
<template name="OWNED">
I am the owner. No one except me can edit until I
<button iconSclass="z-icon-unlock" label="unlock" onClick="@command('unlock')"/> it.
<separator/>
If I refresh or close the browser tab, the lock will be automatically released.
<separator/>
If the browser crashes, gets terminated or the network disconnects, a server side alive check will release the lock
within configurable intervals.
</template>
<template name="AVAILABLE">
No one has currently locked it. I can
<button iconSclass="z-icon-lock" label="lock" onClick="@command('lock')"/> it.
<separator/>
If another user (e.g. in another browser) locks the resource it will be displayed as locked.
</template>
<template name="UNAVAILABLE">
currently locked by '${vm.lockable.owner}'.
<separator/>
I have to wait for the lock to be released. e.g. manually or after a timeout (in case the user is disconnected)
</template>
</apply>
</div>
</zk>
public class SimpleLockableViewModel {
private static final AtomicInteger userCounter = new AtomicInteger(0);
private final String username = "User-" + userCounter.incrementAndGet();
private final UiLockTracker<SimpleResource> lockTracker = new UiLockTracker<>(10, 5);
private static SimpleResource sharedResource = new SimpleResource("the Resource Value");
@Init
public void init(@ContextParam(ContextType.DESKTOP) Desktop desktop) {
desktop.enableServerPush(true);
lockTracker.observe(new MvvmLockable<SimpleResource>(getUsername(), sharedResource) {
@Override
public void onLockEvent(LockEvent event) {
super.onLockEvent(event);
BindUtils.postNotifyChange(null, null, sharedResource, "value");
}
});
}
@Command
public void lock() { lockTracker.lock(); }
@Command
public void unlock() { lockTracker.unlock(); }
public UiLockable<SimpleResource> getLockable() { return lockTracker.getLockable(); }
public String getUsername() { return username; }
}
Summary
Example Sources
The code examples are available on github in the zk-template-examples repository
- zul files: https://github.com/zkoss-demo/zk-template-examples/tree/master/src/main/webapp/locker
- java classes: https://github.com/zkoss-demo/zk-template-examples/tree/master/src/main/java/zk/example/template/locker
Running the Example
Clone the repo
git clone git@github.com:zkoss-demo/zk-template-examples.git
The example war file can be built using the gradle-wrapper (on windows simply omit the prefix './'):
./gradlew war
Execute using gretty:
./gradlew appRun
Execute using jetty-runner (faster startup):
./gradlew startJettyRunner
Then access the example http://localhost:8080/zk-template-examples/locker/
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |