https://github.com/spring-projects/spring-framework/issues/27619#issuecomment-2333340105
Comment From: bclozel
It looks like this proposal is making WebJarsResourceResolver
depend on two different types: WebJarVersionLocator
(from org.webjars:webjars-locator-lite
) and WebJarAssetLocator
(from org.webjars:webjars-locator-core
). Because those two types are used by public constructors, I think this class now needs both dependencies to be on the classpath to be loaded. Doesn't this defeats the purpose? Isn't it going to make things harder for maintenance and deprecation in the future?
Please advise.
Comment From: vpavic
I think this class now needs both dependencies to be on the classpath to be loaded.
Can you clarify why do you believe this is the case?
Before submitting this PR, I built Framework snapshot with these changes locally and used it in a sample app to test both scenarios - with only org.webjars:webjars-locator-lite
available on classpath, and with only org.webjars:webjars-locator-core
available.
Comment From: bclozel
I have just tried to create a sample application with the following dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.webjars:webjars-locator-lite'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
Trying to instantiate manually the resolver like so:
@SpringBootApplication
public class WebjarsApplication {
public static void main(String[] args) {
WebJarsResourceResolver webJarsResourceResolver = new WebJarsResourceResolver(new WebJarVersionLocator());
SpringApplication.run(WebjarsApplication.class, args);
}
}
Yields the following:
$ ./gradlew assemble
> Task :compileJava FAILED
/Users/bclozel/workspace/tmp/webjars/src/main/java/com/example/webjars/WebjarsApplication.java:13: error: cannot access WebJarAssetLocator
WebJarsResourceResolver webJarsResourceResolver = new WebJarsResourceResolver(new WebJarVersionLocator());
^
class file for org.webjars.WebJarAssetLocator not found
1 error
FAILURE: Build failed with an exception.
Am I missing something?
Comment From: vpavic
Ah, I see - the problem is when instantiating resolver in the application code, which doesn't compile if both implementations are not present on the classpath at that time. Admittedly in my tests I was focused on scenarios where it's the Framework instantiating resolver based on what's available on the classpath.
But shouldn't it be possible to overcome this issue if constructor accepted simply Object
for the midterm while both options are supported? Such constructor could be immediately deprecated, in favor of a factory method - potentially even adding one that allows WebJarsResourceResolver
to be created using BiFunction<String, String, String>
which would increase its ability to be reused with different locator implementations.
Comment From: vpavic
This works for me:
public WebJarsResourceResolver(Object webJarLocator) {
this.webJarLocator = switch (webJarLocator.getClass().getName()) {
case "org.webjars.WebJarVersionLocator" -> ((org.webjars.WebJarVersionLocator) webJarLocator)::fullPath;
case "org.webjars.WebJarAssetLocator" -> ((org.webjars.WebJarAssetLocator) webJarLocator)::getFullPathExact;
default -> throw new IllegalArgumentException("Unsupported WebJar locator implementation");
};
}
While such constructor isn't pretty, it's only for the midterm and doesn't break anyone while providing flexibility for the future while avoiding the following (IMO) downsides of the current solution:
- keeping the same inflexibility that was present until now (what happens if there's another WebJar locator implementation few years down the line?)
- code duplication
- you end up with awkwardly named
LiteWebJarVersionLocator
as your only implementation - breaking the continuity (version control history wise) of
WebJarsResourceResolver
Comment From: bclozel
Thanks for the proposal, but I think we're fine with the current tradeoffs.