if (!(error instanceof PathNotFoundException)) return fromCache;
// The path was not found in the cache, so since we don't know whether the ancestors are federated
// from multiple source nodes, we need to populate the cache starting with the lowest ancestor
// that already exists in the cache.
PathNotFoundException notFound = (PathNotFoundException)fromCache.getError();
Path lowestExistingAncestor = notFound.getLowestAncestorThatDoesExist();
if (location.hasPath()) {
Path path = location.getPath();
Path ancestor = path.getParent();
if (!ancestor.equals(lowestExistingAncestor)) {
// Load the nodes along the path below the existing ancestor, down to (but excluding) the desired path
Path pathToLoad = ancestor;
while (!pathToLoad.equals(lowestExistingAncestor)) {
Location locationToLoad = new Location(pathToLoad);
loadContributionsFromSources(locationToLoad, null, contributions); // sourceNames may be null or empty
FederatedNode mergedNode = createFederatedNode(locationToLoad, contributions, true);
if (mergedNode == null) {
// No source had a contribution ...
I18n msg = FederationI18n.nodeDoesNotExistAtPath;
fromCache.setError(new PathNotFoundException(location, ancestor, msg.text(path, ancestor)));
return fromCache;
}
contributions.clear();
// Move to the next child along the path ...
pathToLoad = pathToLoad.getParent();
}
}
}
// At this point, all ancestors exist ...
} else {
// There is no error, so look for the merge plan ...
MergePlan mergePlan = getMergePlan(fromCache);
if (mergePlan != null) {
// We found the merge plan, so check whether it's still valid ...
final DateTime now = getCurrentTimeInUtc();
if (mergePlan.isExpired(now)) {
// It is still valid, so check whether any contribution is from a non-existant projection ...
for (Contribution contribution : mergePlan) {
if (!this.sourceNames.contains(contribution.getSourceName())) {
// TODO: Record that the cached contribution is from a source that is no longer in this repository
}
}
return fromCache;
}
// At least one of the contributions is expired, so go through the contributions and place
// the valid contributions in the 'contributions' list; any expired contribution
// needs to be loaded by adding the name to the 'sourceNames'
if (mergePlan.getContributionCount() > 0) {
sourceNames = new HashSet<String>(sourceNames);
for (Contribution contribution : mergePlan) {
if (!contribution.isExpired(now)) {
sourceNames.remove(contribution.getSourceName());
contributions.add(contribution);
}
}
}
}
}
// Get the contributions from the sources given their names ...
location = fromCache.getActualLocationOfNode();
if (location == null) location = fromCache.at(); // not yet in the cache
loadContributionsFromSources(location, sourceNames, contributions); // sourceNames may be null or empty
FederatedNode mergedNode = createFederatedNode(location, contributions, true);
if (mergedNode == null) {
// No source had a contribution ...
if (location.hasPath()) {
Path ancestor = location.getPath().getParent();
I18n msg = FederationI18n.nodeDoesNotExistAtPath;
fromCache.setError(new PathNotFoundException(location, ancestor, msg.text(location, ancestor)));
return fromCache;
}
I18n msg = FederationI18n.nodeDoesNotExistAtLocation;
fromCache.setError(new PathNotFoundException(location, null, msg.text(location)));
return fromCache;
}
return mergedNode;
}