Add parent origin support
Update the `Origin` interface to include a default `getParent()` method which can be used to get the parent origin. The `TextResourceOrigin` has been updated to implement the method against the source `Resource`. A new `OriginTrackedResource` implementation allows any `Resource` to be decorated and carry an optional `Origin`. Ultimately this will allow us to include parent `Origin` information on properties loaded via a `PropertySourceLoader` without needing any changes to that interface. See gh-23018pull/23127/head
parent
bc5958c398
commit
960651c15a
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.origin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.WritableResource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorator that can be used to add {@link Origin} information to a {@link Resource} or
|
||||||
|
* {@link WritableResource}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.4.0
|
||||||
|
* @see #of(Resource, Origin)
|
||||||
|
* @see #of(WritableResource, Origin)
|
||||||
|
* @see OriginProvider
|
||||||
|
*/
|
||||||
|
public class OriginTrackedResource implements Resource, OriginProvider {
|
||||||
|
|
||||||
|
private final Resource resource;
|
||||||
|
|
||||||
|
private final Origin origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link OriginTrackedResource} instance.
|
||||||
|
* @param resource the resource to track
|
||||||
|
* @param origin the origin of the resource
|
||||||
|
*/
|
||||||
|
OriginTrackedResource(Resource resource, Origin origin) {
|
||||||
|
Assert.notNull(resource, "Resource must not be null");
|
||||||
|
this.resource = resource;
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return getResource().getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists() {
|
||||||
|
return getResource().exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadable() {
|
||||||
|
return getResource().isReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return getResource().isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFile() {
|
||||||
|
return getResource().isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getURL() throws IOException {
|
||||||
|
return getResource().getURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI getURI() throws IOException {
|
||||||
|
return getResource().getURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getFile() throws IOException {
|
||||||
|
return getResource().getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadableByteChannel readableChannel() throws IOException {
|
||||||
|
return getResource().readableChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long contentLength() throws IOException {
|
||||||
|
return getResource().contentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lastModified() throws IOException {
|
||||||
|
return getResource().lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource createRelative(String relativePath) throws IOException {
|
||||||
|
return getResource().createRelative(relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return getResource().getFilename();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return getResource().getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource getResource() {
|
||||||
|
return this.resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Origin getOrigin() {
|
||||||
|
return this.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OriginTrackedResource other = (OriginTrackedResource) obj;
|
||||||
|
return this.resource.equals(other) && ObjectUtils.nullSafeEquals(this.origin, other.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = this.resource.hashCode();
|
||||||
|
result = prime * result + ObjectUtils.nullSafeHashCode(this.origin);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.resource.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link OriginProvider origin tracked} version the given
|
||||||
|
* {@link WritableResource}.
|
||||||
|
* @param resource the tracked resource
|
||||||
|
* @param origin the origin of the resource
|
||||||
|
* @return a {@link OriginTrackedWritableResource} instance
|
||||||
|
*/
|
||||||
|
public static OriginTrackedWritableResource of(WritableResource resource, Origin origin) {
|
||||||
|
return (OriginTrackedWritableResource) of((Resource) resource, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link OriginProvider origin tracked} version the given
|
||||||
|
* {@link Resource}.
|
||||||
|
* @param resource the tracked resource
|
||||||
|
* @param origin the origin of the resource
|
||||||
|
* @return a {@link OriginTrackedResource} instance
|
||||||
|
*/
|
||||||
|
public static OriginTrackedResource of(Resource resource, Origin origin) {
|
||||||
|
if (resource instanceof WritableResource) {
|
||||||
|
return new OriginTrackedWritableResource((WritableResource) resource, origin);
|
||||||
|
}
|
||||||
|
return new OriginTrackedResource(resource, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of {@link OriginTrackedResource} for {@link WritableResource} instances.
|
||||||
|
*/
|
||||||
|
public static class OriginTrackedWritableResource extends OriginTrackedResource implements WritableResource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link OriginTrackedWritableResource} instance.
|
||||||
|
* @param resource the resource to track
|
||||||
|
* @param origin the origin of the resource
|
||||||
|
*/
|
||||||
|
OriginTrackedWritableResource(WritableResource resource, Origin origin) {
|
||||||
|
super(resource, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WritableResource getResource() {
|
||||||
|
return (WritableResource) super.getResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream getOutputStream() throws IOException {
|
||||||
|
return getResource().getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.origin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.origin.OriginTrackedResource.OriginTrackedWritableResource;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.WritableResource;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link OriginTrackedResource}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class OriginTrackedResourceTests {
|
||||||
|
|
||||||
|
private Origin origin;
|
||||||
|
|
||||||
|
private WritableResource resource;
|
||||||
|
|
||||||
|
private OriginTrackedWritableResource tracked;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
this.origin = MockOrigin.of("test");
|
||||||
|
this.resource = mock(WritableResource.class);
|
||||||
|
this.tracked = OriginTrackedResource.of(this.resource, this.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getInputStreamDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.getInputStream();
|
||||||
|
verify(this.resource).getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void existsDelegatesToResource() {
|
||||||
|
this.tracked.exists();
|
||||||
|
verify(this.resource).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isReadableDelegatesToResource() {
|
||||||
|
this.tracked.isReadable();
|
||||||
|
verify(this.resource).isReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isOpenDelegatesToResource() {
|
||||||
|
this.tracked.isOpen();
|
||||||
|
verify(this.resource).isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isFileDelegatesToResource() {
|
||||||
|
this.tracked.isFile();
|
||||||
|
verify(this.resource).isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getURLDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.getURL();
|
||||||
|
verify(this.resource).getURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getURIDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.getURI();
|
||||||
|
verify(this.resource).getURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFileDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.getFile();
|
||||||
|
verify(this.resource).getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readableChannelDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.readableChannel();
|
||||||
|
verify(this.resource).readableChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contentLengthDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.contentLength();
|
||||||
|
verify(this.resource).contentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lastModifiedDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.lastModified();
|
||||||
|
verify(this.resource).lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRelativeDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.createRelative("path");
|
||||||
|
verify(this.resource).createRelative("path");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilenameDelegatesToResource() {
|
||||||
|
this.tracked.getFilename();
|
||||||
|
verify(this.resource).getFilename();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getDescriptionDelegatesToResource() {
|
||||||
|
this.tracked.getDescription();
|
||||||
|
verify(this.resource).getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getOutputStreamDelegatesToResource() throws IOException {
|
||||||
|
this.tracked.getOutputStream();
|
||||||
|
verify(this.resource).getOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toStringDelegatesToResource() {
|
||||||
|
Resource resource = new ClassPathResource("test");
|
||||||
|
Resource tracked = OriginTrackedResource.of(resource, this.origin);
|
||||||
|
assertThat(tracked).hasToString(resource.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getOriginReturnsOrigin() {
|
||||||
|
assertThat(this.tracked.getOrigin()).isEqualTo(this.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getResourceReturnsResource() {
|
||||||
|
assertThat(this.tracked.getResource()).isEqualTo(this.resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void equalsAndHashCode() {
|
||||||
|
Origin o1 = MockOrigin.of("o1");
|
||||||
|
Origin o2 = MockOrigin.of("o2");
|
||||||
|
Resource r1 = mock(Resource.class);
|
||||||
|
Resource r2 = mock(Resource.class);
|
||||||
|
OriginTrackedResource r1o1a = OriginTrackedResource.of(r1, o1);
|
||||||
|
OriginTrackedResource r1o1b = OriginTrackedResource.of(r1, o1);
|
||||||
|
OriginTrackedResource r1o2 = OriginTrackedResource.of(r1, o2);
|
||||||
|
OriginTrackedResource r2o1 = OriginTrackedResource.of(r2, o1);
|
||||||
|
OriginTrackedResource r2o2 = OriginTrackedResource.of(r2, o2);
|
||||||
|
assertThat(r1o1a).isEqualTo(r1o1a).isEqualTo(r1o1a).isNotEqualTo(r1o2).isNotEqualTo(r2o1).isNotEqualTo(r2o2);
|
||||||
|
assertThat(r1o1a.hashCode()).isEqualTo(r1o1b.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofReturnsOriginTrackedResource() {
|
||||||
|
Resource resource = mock(Resource.class);
|
||||||
|
Resource tracked = OriginTrackedResource.of(resource, this.origin);
|
||||||
|
assertThat(tracked).isExactlyInstanceOf(OriginTrackedResource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ofWhenWritableReturnsOriginTrackedWrtiableResource() {
|
||||||
|
Resource resource = mock(WritableResource.class);
|
||||||
|
Resource tracked = OriginTrackedResource.of(resource, this.origin);
|
||||||
|
assertThat(tracked).isInstanceOf(WritableResource.class)
|
||||||
|
.isExactlyInstanceOf(OriginTrackedWritableResource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue