Holger Stenzhorn opened SPR-15256 and commented
Following the advice from from Juergen Hoeller in #19117, I have the code below "living" in a class of a webapp:
@Value("x") Path a;
@Value("y") Path b;
@Value("#{servletContext.getRealPath('x')}") Path c;
@Value("#{servletContext.getRealPath('y')}") Path d;
It is important to note that the directory x
actually exists but y
does not exist.
Now I have the following test method:
private void test() {
Stream.of(a, b, c, d).forEach(p -> System.out.format("%s\n%s\n\n", p, p.toAbsolutePath()));
}
This method returns:
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/x
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/x
y
/usr/local/Cellar/tomcat/8.5.11/libexec/bin/y
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/x
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/x
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/y
/Users/holger/Developer/test/out/artifacts/test_Web_exploded/y
So for existing paths there is no difference as can be seen from the output for a
and c
.
But for non-existing paths there is a difference as the output for b
and d
shows.
I can see that the documentation for PathEditor
says this:
As a fallback, a path will be resolved in the file system via Paths#get(String) if no existing context-relative resource could be found.
So it seems to me that the above difference might be intentional...
(Note: My specific use case is that I want to specify with the above "method" a path where Lucene creates its index and that directory does not necessarily have to exist beforehand.)
Affects: 4.3.6
Issue Links: - #19117 PathEditor returns wrong result for absolute path in servletContext.getRealPath(...)
Comment From: kascaks
Hi,
the situation just changed with 5.3.5.
In servlet context, PathEditor now always prepends context path to the path string even if path does not exist in context itself. The reason seems to be this commit https://github.com/spring-projects/spring-framework/commit/ebf6fff31294f4a90de776da05fb9d4adbf6eab1. If PathEditor runs inside servlet context, resource.isFile()
always returns true, even if path does not exist.
Following test passes in 5.3.4, but fails with 5.3.5.
public class PathConverterTests {
static final String PATH_STRING = "/var/tmp/";
@Configuration
public static class Foo {
@Value(PATH_STRING)
private Path path;
}
@Test
public void testPathConverter() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ServletContext servletContextMock = Mockito.mock(ServletContext.class);
Mockito.when(servletContextMock.getRealPath(PATH_STRING)).thenReturn("/dummy-context-path/" + PATH_STRING);
Enumeration<?> enumerationMock = Mockito.mock(Enumeration.class);
Mockito.when(enumerationMock.hasMoreElements()).thenReturn(false);
Mockito.when(servletContextMock.getAttributeNames()).thenReturn(enumerationMock);
Mockito.when(servletContextMock.getInitParameterNames()).thenReturn(enumerationMock);
ctx.setServletContext(servletContextMock);
ctx.register(Foo.class);
ctx.refresh();
Foo x = ctx.getBean(Foo.class);
ctx.close();
/* passes in 5.4.3, fails in 5.3.5 */
Assertions.assertEquals(Paths.get(PATH_STRING), x.path);
}
}
For me this broke backwards compatibility. I have a spring boot single bundled jar that runs web application and in application.properties
I have something resembling path=/var/tmp
which is a path to a folder where some data are written by the application. With 5.3.5 this syntax is no longer possible as it always prepends /tmp/tomcat-docbase....
to that path. Of course, changing property to path=file:///var/tmp
works, but PathEditor javadoc about fallback is no longer valid and file:
is kind of ugly/hard to maintain (path relative/absolute, os windows/linux)
Comment From: jhoeller
@kascaks This has been reported in #26702 already, let's use that ticket for tracking the regression. We're trying to fix it in 5.3.6 (although we still don't quite know where things go wrong there, it might also be in the ServletContextResource
itself).
Comment From: snicoll
The handling looks consistent to me now. That above outputs the following for me:
x
/Users/snicoll/workspace/temp/demo-web/x
y
/Users/snicoll/workspace/temp/demo-web/y
/private/var/folders/hn/7bzmb6w56w3550705qmk6m4m0000gn/T/tomcat-docbase.8080.3737663102758843393/x
/private/var/folders/hn/7bzmb6w56w3550705qmk6m4m0000gn/T/tomcat-docbase.8080.3737663102758843393/x
/private/var/folders/hn/7bzmb6w56w3550705qmk6m4m0000gn/T/tomcat-docbase.8080.3737663102758843393/y
/private/var/folders/hn/7bzmb6w56w3550705qmk6m4m0000gn/T/tomcat-docbase.8080.3737663102758843393/y
Where /Users/snicoll/workspace/temp/demo-web/x
exists while /Users/snicoll/workspace/temp/demo-web/y
does not.