Consider the underling file in equals/hashcode

Update `StandardConfigDataResource` so that the underlying file is
considered in equals/hashcode. Prior to this commit, if the classpath
included `.` then the same resource could be loaded twice. Once via
a `ClassPathResource` and once via a `FileSystemResource`.

Fixes gh-34212
pull/34619/head
Phillip Webb 2 years ago
parent 837ac85f3b
commit aea57fbd41

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +28,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.log.LogMessage;
/**
* Imports {@link ConfigData} by {@link ConfigDataLocationResolver resolving} and
@ -117,16 +118,20 @@ class ConfigDataImporter {
ConfigDataResolutionResult candidate = candidates.get(i);
ConfigDataLocation location = candidate.getLocation();
ConfigDataResource resource = candidate.getResource();
this.logger.trace(LogMessage.format("Considering resource %s from location %s", resource, location));
if (resource.isOptional()) {
this.optionalLocations.add(location);
}
if (this.loaded.contains(resource)) {
this.logger
.trace(LogMessage.format("Already loaded resource %s ignoring location %s", resource, location));
this.loadedLocations.add(location);
}
else {
try {
ConfigData loaded = this.loaders.load(loaderContext, resource);
if (loaded != null) {
this.logger.trace(LogMessage.format("Loaded resource %s from location %s", resource, location));
this.loaded.add(resource);
this.loadedLocations.add(location);
result.put(candidate, loaded);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,10 @@
package org.springframework.boot.context.config;
import java.io.File;
import java.io.IOException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
@ -96,12 +98,21 @@ public class StandardConfigDataResource extends ConfigDataResource {
return false;
}
StandardConfigDataResource other = (StandardConfigDataResource) obj;
return this.resource.equals(other.resource) && this.emptyDirectory == other.emptyDirectory;
return (this.emptyDirectory == other.emptyDirectory) && isSameUnderlyingResource(this.resource, other.resource);
}
private boolean isSameUnderlyingResource(Resource ours, Resource other) {
return ours.equals(other) || isSameFile(getUnderlyingFile(ours), getUnderlyingFile(other));
}
private boolean isSameFile(File ours, File other) {
return (ours != null) && (other != null) && ours.equals(other);
}
@Override
public int hashCode() {
return this.resource.hashCode();
File underlyingFile = getUnderlyingFile(this.resource);
return (underlyingFile != null) ? underlyingFile.hashCode() : this.resource.hashCode();
}
@Override
@ -116,4 +127,17 @@ public class StandardConfigDataResource extends ConfigDataResource {
return this.resource.toString();
}
private File getUnderlyingFile(Resource resource) {
try {
if (resource instanceof ClassPathResource || resource instanceof FileSystemResource
|| resource instanceof FileUrlResource) {
File file = resource.getFile();
return (file != null) ? file.getAbsoluteFile() : null;
}
}
catch (IOException ex) {
}
return null;
}
}

@ -16,9 +16,12 @@
package org.springframework.boot.context.config;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import static org.assertj.core.api.Assertions.assertThat;
@ -66,4 +69,15 @@ class StandardConfigDataResourceTests {
assertThat(location).isNotEqualTo(other);
}
@Test // gh-34212
void equalsAndHashCodeWhenSameUnderlyingResource() throws IOException {
ClassPathResource classPathResource = new ClassPathResource("log4j2.springboot");
FileUrlResource fileUrlResource = new FileUrlResource(classPathResource.getURL());
ConfigDataResource classPathConfigDataResource = new StandardConfigDataResource(this.reference,
classPathResource);
ConfigDataResource fileUrlConfigDataResource = new StandardConfigDataResource(this.reference, fileUrlResource);
assertThat(classPathConfigDataResource.hashCode()).isEqualTo(fileUrlConfigDataResource.hashCode());
assertThat(classPathConfigDataResource).isEqualTo(fileUrlConfigDataResource);
}
}

Loading…
Cancel
Save