This paper discusses one concrete issue related to merged contexts: masking of deleted items. Merged context is discussed
in issue 34057.
It is expected that reader read comments in the issue and is familiar
with MultiFilesystems and its implementation of masking of files.
Two solutions are presented here:
multiFS like solution
external storage
1. MultiFS like solution
MultiFS uses for masking special files with names which ends with
_hidden and with attributes with special value VoidValue. The
merged context can use the same approch. For the clients of merged
context the merging is transparent. Situation changes if client access
directly the writable layer - it contains bunch of internal stuff which
is used to implement masking.
Below is list of all possible usage scenarios of merged context and
their impact on writable layer and how the masking will be implemented.
Following contexts are used in the examples: R, W and M. M is merged
context of [W, R]. W is writtable layer of merged context. R is
readonly layer of merged context. All context are SPI contexts, ie.
BasicContext descendants.
It is also expected that clients can later access directly the writable
layer and they need to distinguish internal masking staff from real
context content.
A.) Delete binding
M.bindObject("B", null);
results in
if (notExistBinding(W,"B")) {
W.bindObject("B", whateverValue);
}
W.setAttribute("B", "DELETED_BINDING", whateverValue);
Comments:
the deleted binding is always masked without regard to its
existence on the R. It makes the rules for masking simpler and
guarantees that once the B was deleted it stays deleted.
M ignores and does not report bindings which has "DELETED_BINDING" attribute set to non-null value
M ignores and does not report binding attribute with name "DELETED_BINDING"
W can use boolean MergedContextUtils.isBindingDeleted(W, "B") to identify and/or ignore deleted bindings
W can use boolean MergedContextUtils.isInternalAttribute(W, "B", "DELETED_BINDING") to ignore internal attributes
B.) Modify binding
M.bindObject("B", someValue);
results in
if (notExistBinding(W,"B")) {
copyAttributes = true;
}
W.bindObject("B", someValue);
W.setAttribute("DELETED_BINDING", null);
if (copyAttributes) {
// copy all attrs of R.B to W.B here
}
Comments:
because binding attributes are not merged they have to be copied from layer R when object is being created on layer W.
C.) Set binding attribute
M.setAttribute("B", "A", someValue);
results in
if (notExistBinding(W,"B")) {
W.bindObject("B", R.lookupObject("B")); // copy all attrs of R.B to W.B here
}
W.setAttribute("B", "A", someValue);
Comments:
binding attributes are not merged
because binding attributes are not merged they have to be copied from layer R when object is being created on layer W.
D.) Delete binding attribute
M.setAttribute("B", "A", null);
results in
if (notExistBinding(W,"B")) {
W.bindObject("B", R.lookupObject("B")); // copy all attrs of R.B to W.B here
}
W.setAttribute("B", "A", null);
E.) Set context attribute
M.setAttribute(null, "A", someValue);
results in
W.setAttribute(null, "A", someValue);
Comments:
context attributes are merged. That's opposite to binding attributes.
F.) Delete context attribute
M.setAttribute(null, "A", null);
results in
W.setAttribute(null, "A", "DELETED_ATTRIBUTE");
Comments:
M ignores and does not report context attributes with value "DELETED_ATTRIBUTE"
W can use boolean MergedContextUtils.isAttributeDeleted(W, null, "A") to identify and/or ignore deleted attributes
G.) Destroy subcontext
M.destroySubcontext("S");
results in
W.setAttribute(null, "DELETED_SUBCONTEXT_"+"S", whateverValue);
Comments:
subcontext is not physically deleted on writable layer. It is just masked out.
M ignores and does not report context attributes with name starting with "DELETED_SUBCONTEXT_"
M
ignores and does not report subcontexts which are according to
existence of "DELETED_SUBCONTEXT_<subcontext_name>" attribute
deleted
W can use boolean MergedContextUtils.isSubcontextDeleted(W, "S") to identify and/or ignore deleted subcontexts
W
can use boolean MergedContextUtils.isInternalAttribute(W, null,
"DELETED_SUBCONTEXT_<subcontext_name>") to ignore internal
attributes
H.) Create subcontext
M.createSubcontext("S");
results in
W.createSubcontext("S");
W.setAttribute(null, "DELETED_SUBCONTEXT_"+"S", null);
Summary of all used masks
Following masks are used:
"DELETED_BINDING" - name of binding attribute. Binding having this attribute was deleted.
"DELETED_ATTRIBUTE" - value of binding attribute. Attribute with this value was deleted.
"DELETED_SUBCONTEXT_<subcontext_name>"
- name of context attribute. Context with this attribute contains deleted subcontext with name <subcontext_name>.
Following methods
would be needed in API for clients to identify and/or ignore masking
implementation. It is better to have explicit API rather then leting
clients compare the Strings directly:
This is modification of previous
solution with only difference that masks are not stored in W layer, but
in separate storage. The storage could be for example Properties file
and it would be up to clients of merged context whether and how they
persist the properties file. For example default context registry (aka SFS) could
store the properties file somewhere in the userdir.
A.) Delete binding
M.bindObject("B", null);
results in
properties.setProperty(M.getAbsoluteContextName()+":DELETED_BINDING:"+"B", "yes");
B.) Modify binding
M.bindObject("B", someValue);
results in
if (notExistBinding(W,"B")) {
copyAttributes = true;
}
W.bindObject("B", someValue);
if (copyAttributes) {
// copy all attrs of R.B to W.B here
}
properties.setProperty(M.getAbsoluteContextName()+":DELETED_BINDING:"+"B", null);
C.) Set binding attribute
M.setAttribute("B", "A", someValue);
results in
if (notExistBinding(W,"B")) {
W.bindObject("B", R.lookupObject("B"));
// copy all attrs of R.B to W.B here
}
W.setAttribute("B", "A", someValue);
D.) Delete binding attribute
M.setAttribute("B", "A", null);
results in
if (notExistBinding(W,"B")) {
W.bindObject("B", R.lookupObject("B"));
// copy all attrs of R.B to W.B here
}
W.setAttribute("B", "A", null);
E.) Set context attribute
M.setAttribute(null, "A", someValue);
results in
W.setAttribute(null, "A", someValue);
properties.setProperty(M.getAbsoluteContextName()+":DELETED_CONTEXT_ATTRIBUTE:"+"A", null);
F.) Delete context attribute
M.setAttribute(null, "A", null);
results in
properties.setProperty(M.getAbsoluteContextName()+":DELETED_CONTEXT_ATTRIBUTE:"+"A", "yes");
G.) Destroy subcontext
M.destroySubcontext("S");
results in properties.setProperty(M.getAbsoluteContextName()+":DELETED_SUBCONTEXT:"+"S", "yes");
H.) Create subcontext
M.createSubcontext("S");
results in
W.createSubcontext("S");
properties.setProperty(M.getAbsoluteContextName()+":DELETED_SUBCONTEXT:"+"S", null);