Excluding replicated state from updates
There are multiple options to exclude some of the state of a replicated object from being checked for modifications each time an update is requested. These options are divided into complile-time and runtime directives.
The follwing can be found on this page: TOC(inline,noheading,notitle,ExcludedState)?
Compile-time directives are annotated directly to classes and fields in the source code. These directives direct the transformation and code generation. A compile-time directive applies to all instances of an annotated class. There is no way to revoke the directive at runtime for a given subset of the instances.
Java introduces a final modifier that can be attached to classes, methods, and fields. Here, only final modifiers attached to fields is relevant. A final field must be initialized within the construtor of its class and must not be changed after the constructor returns.
Obviously, there is no need for the update mechanism to inspect the constents of final fields, because they cannot be modified. Therefore, they have not to be updated after the object's constructor returns. The property of being unmodifyable does only hold for the field itself, not for its contents (if the final field is a reference to another object).
Java's transient modifier may only be attached to instance variables. In Java, marking a field as transient does not affect its normal operations. It is only meaningful, if an object is marshaled using java.io.ObjectOutputStream. Transient fields are excluded from the "persistent" state of an object. Therefore, the state of such fields is not marshaled along with the object. The result is, that the corresponding field of the object is uninitialized, when being unmarshaled from the stream.
The uka.transport serialization also respects the transient modifier and does not encode the state of transient fields. Therefore, the transient modifier has also an impact on the object replication mechanism that heavily depends on uka.transport. A transient instance variable of an object that is part of a replicated graph is completely excluded from replication. After the object is constructed on one node, its state is neither transfered to other replicas during the initial update, nor is it kept consistent after the variable is changed on some replica.
Transient variables in replicated state can be used to store replica-local data. The application is completely responsible for initializing and managing the state of transient variables on all replicas separately.
Java does not introduce the concept of value classes. uka.tranport and CollectiveReplication introduce their own notion of value classes that is most advantageous for optimizing communication and updates. All state of a value class is "final as a whole" and instances of value classes do not have an identity like objects.
For value objects, references and copies are exchangeable. Among other properties (e.g. during serialization and remote method calls), value objects within replicated graphs are neither checked for updates (becaues their state does never change), nor are they included into the management structures of the replicated object at all.
When referencing a value object through a non-final reference, the whole state of the value is retransmitted, if the reference changes (because it is initialized to a new value). When referencing the value object through a final reference, neither the reference nor the object is ever checked for updates.
The state of value classes is immutable in a sense that it must not change after the object is constructed. Note: this is different from declaring the class final. A final class means that it is not allowed derive subclasses. In contrast, a value class means that its complete state is final. This property must hold for the complete state of the class (also for inherited state) and for the state of all of its subclasses (because these inherit the Value interface).
A value object has no identity in the sense that it neither must be tested for identity using the built-in == operator nor must is be used as monitor for synchronization.
Declaring value classes
A class is degraded to a values class by the implementation of the special marker interface uka.lang.Value. A value class must obey to the following rules:
- Instances of value classes must not change (in any of their instance variables) after the object is contructed. Other than declaring all of its instance variables final, this property is not enforced by the compiler. This makes value classes more widely usable.
- The application must not depend on the identity of two value objects, because a value object might be replaced by a copy of itself during marshaling or update.
Comparision to C# "value classes"
Things declared with the struct keyword in C# are bare blocks of memory. They share the property of having no identity with the value classes defined here. But since those memory regions are not immutable, references and copies are not exchangeable. This requires to eagerly copy them to exclude side effects.
A runtime directive applies to only some instances of a class. In contrast to compile-time directives, which are annotated to declarations, runtime directives are used like regular library calls. Those runtime directives always deal with previously constructed instances.
The distribution of objects that are part of replicated graphs can be explicitly restricted to some replicas of the replicated object.
The following call is used, to specify the distribution of object obj that is part of the replicated graph rooted at replica (the replicated object). Assume the variable ranks is an array of integer values specifying the ranks of replica, where obj should be available:
jp.lang.DistributedRuntime.distribute(replica, obj, ranks);
Unfortunately, at the moment the distribution of an object must be specified before the object becomes part of the replicated state and may not be changed afterwards. See: ticket:208
Objects can be marked as being immutable from some point in time (e.g. after completely initializing a replicated graph) by "freezing" them. A frozen object is excluded from updates like an object referenced through a transient reference, or a value object. The difference is that the the exclusion does not depend on static properties of its class, and that it can be excluded from updates at some point in time, when the application guarantees not to modify it any longer.
The following call freezes the object obj, which is already part of the replica replica:
Note: Freezing an object is itself a modification of the replicated state and must be done within a (collectively or exclusively) synchronized section on the replicated object.
Note: There is currently no way to defrost a frozen object. See ticket:223