I have setup Hazelcast 4.0.1 with Spring Boot v2.2.6.RELEASE application. I am trying to connect to Hazelcast cluster as a client using "hazelcast-client.xml" using following in application-dev.yaml:
cache:
type: jcache
jcache:
provider: com.hazelcast.client.cache.impl.HazelcastClientCachingProvider
config: classpath:hazelcast/hazelcast-client.xml
When the application is run directly from Intellij IDEA, it runs fine. But when packaged as a boot JAR & run then it fails to process the file with the following exception from Hazelcast provider:
Caused by: java.net.URISyntaxException: Unsupported protocol in configuration location URL: jar:file:/D:/project/build/libs/project-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/hazelcast/hazelcast-client.xml
at com.hazelcast.cache.impl.AbstractHazelcastCachingProvider.getConfigURL(AbstractHazelcastCachingProvider.java:271)
at com.hazelcast.client.cache.impl.HazelcastClientCachingProvider.getConfigFromLocation(HazelcastClientCachingProvider.java:119)
at com.hazelcast.client.cache.impl.HazelcastClientCachingProvider.getOrCreateFromUri(HazelcastClientCachingProvider.java:76)
at com.hazelcast.cache.impl.AbstractHazelcastCachingProvider.getOrCreateInstanceFromProperties(AbstractHazelcastCachingProvider.java:357)
at com.hazelcast.cache.impl.AbstractHazelcastCachingProvider.getOrCreateInstance(AbstractHazelcastCachingProvider.java:321)
at com.hazelcast.cache.impl.AbstractHazelcastCachingProvider.createHazelcastCacheManager(AbstractHazelcastCachingProvider.java:247)
... 161 common frames omitted
If the "config" key is removed, then Hazelcast provider is able to locate the XML itself using classpath approach internally. But the issue is that I want to provide Spring Boot profile based XML e.g. hazelcast-client-dev.xml. I tried by not providing "classpath" scheme but that didn't help either.
On Spring side, the issue is that if the "config" value is provided without any URI scheme e.g. classpath, then Spring loads the value as ServletContext resource & fails. If "classpath" is added then Spring loads the value as ClassPathResouce & sets the property "hazelcast.config.location" with the classpath URL which in case of JAR starts with "jar:file:/" as mentioned in exception message.
The Hazelcast provider itself has checks on the provide file URI e.g. if URI starts with classpath, file, http or https otherwise it throws exception. It doesn't handle "jar" scheme.
I couldn't find a cleaner way of providing profile based filename. For now, I have to add the filename to System properties on application start as follows. Let me know if there is a better way in Spring Boot.
String profile = System.getProperty("spring.profiles.active");
System.setProperty("hazelcast.client.config", "classpath:hazelcast/hazelcast-client-" + profile + ".xml");
Kindly let me know if more info is needed.
Comment From: snicoll
Hazelcast 4 is not supported with Spring Boot 2.2 although I don't think that this particular error is related to the problems we are aware of.
The exception you've shared does not match the location in your configuration so I'd like to be able to look at a concrete example to understand what the problem might be. Please attach a small sample that reproduces the problem you've described, either as a zip attached to this issue or a link to a GitHub repo. Thank you.
Comment From: mobasherswl
I have updated the path mismatch in my 1st comment as you highlighted. But that was like typo.
I have attached the sample project springboot-hazelcast.zip as you suggested. I have hard coded the Spring profile in "main" method. Also there is another method, that is currently I am using in my current project to load client XML based on Spring profile.
If this project is run in IDEA, then it loads the XML & attempts to connect to cache server. It won't be able to connect to the Hazelcast server but that's fine.
Now if we build a boot JAR e.g. by issuing command "gradle clean bootJar", and run the JAR "java -jar app-0.0.1-SNAPSHOT.jar" then it will fail with exception I mentioned in my 1st comment.
Let me know if further details are required.
Comment From: snicoll
Thanks for the sample. When you run the app from your IDE or via bootRun the hazelcast configuration file is a file on the filesystem, for instance
file:/Users/snicoll/Downloads/springboot-hazelcast/app/build/resources/main/hazelcast-client-dev.xml
When you run that from a fat jar, the configuration file is packaged in a jar that's embedded in the application jar Spring Boot generates for you. Here is an example URL:
jar:file:/Users/snicoll/Downloads/springboot-hazelcast/app/build/libs/app-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/hazelcast-client-dev.xml
It is unfortunately nothing we can fix ourselves. Hazelcast should be able to handle such an URL but it doesn't seem able to unfortunately. Can you please raise that problem on the Hazelcast issue tracker?
Comment From: mobasherswl
The logical & reasonable approach is that Spring shouldn't process or do anything with the property & pass the value as such to the 3rd party. As can be seen in the provider method impl, they are handling the URI schemes themselves.
Comment From: snicoll
That is exactly what we we do.
Comment From: mobasherswl
Okay, I'll say this last time in case you didn't understand.
Spring should pass the config value as such. e.g if the value is like this:
spring:
cache:
jcache:
config: classpath: hazelcast-client-dev.xml
then in this case, the Hazelcast provider should get the value "classpath: hazelcast-client-dev.xml". What Spring is doing is it converts the value into ClassPathResource object & passes the output of this object to the provider. The provider has its own logic to deal with XML locations.
Comment From: snicoll
I understood what you meant. We are passing a valid URL to Hazelcast. If you want this to be fixed, you need to create an issue on the Hazelcast issue tracker.