Merged context masking
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>.
isBindingDeleted(BasicContext ctx, String bindingName)
isAttributeDeleted(BasicContext ctx, String bindingNameOrNullForContext, String attrName)
isSubcontextDeleted(BasicContext ctx, String subcontextName);
isInternalAttribute(BasicContext ctx, String bindingNameOrNullForContext, String attrName)
2. External storage solution
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);