For a project that requires markup reloading and a custom markup directory structure we have implemented a custom IResourceStreamLocator.
This implementation creates instance of its own IResourceStream implementation which does NOT at the same time implement IFixedLocationResourceStream.
This means there is no "locationAsString()" method implemented on the resource stream instances.
Now the MarkupCache comes into the picture. It is using a cacheKey and the locationAsString as cache keys using a 2 step mechanism.
cacheKey -> locationAsString
locationAsString -> Markup
Additionally MergedMarkup comes into play. MergedMarkup is a markup instance that merges a base markup with another markup.
This is typically used for markup inheritance.
BasePage.html implemented by BasePage
PageA.html extends BasePage
PageB.html extends BasePage
MergedMarkup#locationAsString() concatenates the locationAsString values of its source markup/resource objects putting a ":" inbetween.
This means it will NEVER be NULL, even if both source objects return NULL as their locationAsString values.
Now, in case the original resource streams do not implement IFixedLocationResourceStream, this result in a locationAsString of "null:null".
Unfortunately this is the case for ALL possible combinations of the source objects of MergedMarkup
So the locationAsString is "null:null" for (BasePage,PageA) and also for (BasePage,PageB)
Back to the MarkupCache#loadMarkup method.
There is a check to use the cacheKey as a replacement for locationAsString in case its NULL.
Now, if the Markup loaded is a MergedMarkup instance, it doesnt return NULL, but "null:null".
So, putIntoCache() gets called with "null:null" as the key for ALL possible combinations resulting in a huge chaos of templates being merged together, when those cache entries are used later on.
A workaround is to implement IFixedLocationResourceStream on our custom IResourceStream implementation.