-
Notifications
You must be signed in to change notification settings - Fork 3
Home
#Java ACI Store Transient and transactional store for Java objects.
The name of the store is derived from the acronym ACID (Atomicity, Consistency, Isolation, Durability) describing the properties of transactions. The store is designed to fullfil the first three of these properties but not the Durability.
First we create a simple example class of objects that shall be stored in a transactional store. The class implements the JacisCloneable interface to enable the store to clone the object without using Furthermore the class extends the AbstractReadOnlyModeSupportingObject (implementing the JacisReadonlyModeSupport interface). This means the object provides a secure read only mode. It is the responsibility of an object implementing the JacisReadonlyModeSupport interface to prevent any modification after the method switchToReadOnlyMode is called. The AbstractReadOnlyModeSupportingObject provides a checkWritable method throwing an exception if the object is in read only mode.
public static class Account extends AbstractReadOnlyModeSupportingObject implements JacisCloneable<Account> {
private final String name;
private long balance;
public Account(String name) {
this.name = name;
}
@Override
public Account clone() {
try {
Account clone = (Account) super.clone();
return clone;
} catch (CloneNotSupportedException e) { // can never happen
throw new InternalError("Failed to clone " + this + "! " + e);
}
}
Account deposit(long amount) {
checkWritable();
balance += amount;
return this;
}
public Account withdraw(long amount) {
checkWritable();
balance -= amount;
return this;
}
public String getName() {
return name;
}
public long getBalance() {
return balance;
}
}
First we initialize a JACIS container:
JacisContainer container = new JacisContainer();
Now we create a store for our example object:
JacisObjectTypeSpec<String, Account> objectTypeSpec = new JacisObjectTypeSpec<>(String.class, Account.class);
JacisStore<String, Account> store = container.createStore(objectTypeSpec);
Now we start a local transaction on the JACIS container (that means on all stores of this container):
JacisLocalTransaction tx = container.beginLocalTransaction();
Now we can work with the store (inside a transaction) and create and update objects. First we create a new Account:
Account account1 = new Account("account1");
All modifications of objects have to be notified to the store explicitly (there is no automatic dirty checking). This is true for modifications as well as for creating new objects. In both cases the store is notified by calling the update method:
store.update(account1.getName(), account1);
Enough for our first transaction. Now we commit the transaction. Afterwards all other transactions can see our new Account
tx.commit();
For our next transaction we use a helper method executing the passed code (as lambda expression) inside a transaction. We use this to deposit some money on our account. Note that the call of the update is again necessary. If we omit this call the change will be lost after commit (try it).
container.withLocalTx(() -> {
Account acc = store.get("account1");
acc.deposit(100);
store.update("account1", acc);
});
We use another transaction to check the balance of the Account:
container.withLocalTx(() -> {
Account acc = store.get("account1");
System.out.println("balance of " + acc.getName() + ": " + acc.getBalance());
});
Finally we withdraw some money and simulate an exception causing the transaction to be rolled back:
try {
container.withLocalTx(() -> {
Account acc = store.get("account1");
acc.withdraw(10);
store.update("account1", acc);
throw new RuntimeException("Error in transaction!");
});
} catch (RuntimeException e) {
System.out.println("Expected exception " + e);
// expected
}
Again we check the balance of the Account to see that the transaction failed and nothing is withdrawn:
container.withLocalTx(() -> {
Account acc = store.get("account1");
System.out.println("balance of " + acc.getName() + ": " + acc.getBalance());
});