PDA

View Full Version : JAR Getting Locked On Windows When Config File Read From UrlResource


wondergeek
Nov 7th, 2005, 03:09 PM
Hello,

I'm running into an issue with my container when Spring tries to read a resource from a JAR file using UrlResource. I believe the root issue is whether or not the resource is read using ClassLoader.getResource().openStream() or ClassLoader.getResourceAsStream().

I have tested this scenario on our container (SAP Web AS) without Spring in the picture at all. If I read a resource (an XML file for instance) out of a jar using ClassLoader.getResource().openStream(), the JAR file gets locked and I cannot delete it (and consequently cannot redeploy the app) without shutting down the container. If I read the same resource using ClassLoader.getResourceAsStream(), then everything works fine.

I'm on Windows XP. I know there are some open bugs in the JDK regarding reading resources from JAR files on Windows and those JAR files getting locked.

If I were writing my own code, I could simply always use ClassLoader.getResourceAsStream(). Unfortunatly, we're trying to use Spring along with the ContextSingletonBeanFactoryLocator, which in turn delegates loading classpath*: style resources to a PathMatchingResourcePatternResolver. That creates UrlResource objects which read the XML config files using URL.openStream() which is causing the JAR files to be locked.

Are other people having this problem with their containers running on Windows? I couldn't find any fourm topics on it which would lead me to believe many people are not experiencing it.

What would be the best way around this problem? Is there someway to make Spring always use ClassLoader.getResrouceAsStream() without hacking the source?

Thanks!

Cowboy Bob
Nov 7th, 2005, 05:12 PM
It's not a Java problem, it's a Windows problem in the way that it locks files that are in use (ever wonder why you need to reboot after installing a patch?).

I'm not sure there's a lot that can be done about it other than move over to some kind of *nix box for development since this particular problem doesn't happen there.

Bob

wondergeek
Nov 7th, 2005, 05:38 PM
Part of my frusteration with this whole scenario is trying to figure out what is at fault. It sure seems like it's Windows to me as well. However, I wanted to rule out my container as the culprit, which is why I wondered if anyone else was seeing this type of behaviour on Tomcat or anything.

The lack of previous discussion on this topic leads me to two possible conclusions:

1) Everyone runs Unix
2) Lots of people run Windows, but their containers some how get around this issue or they are not using ContextSingletonBeanFactoryLocator (or reading resources from jar files)

Which conclusion is correct? :)

Cowboy Bob
Nov 7th, 2005, 05:40 PM
Personally I'm typing this right now on a Linux machine.

Bob

Costin Leau
Nov 8th, 2005, 03:51 AM
If you are using Tomcat you can get around the issue by using the antiLocking property.

wondergeek
Nov 8th, 2005, 08:54 AM
We are not using Tomcat unfortunatly. How do containers get around this problem on Windows?

Costin Leau
Nov 8th, 2005, 09:02 AM
Do a simple profiling or look through your container sources if you have them. If the classloaders do caching (through getResourceAsStream()) then you can write your own getResourceAsStream - I have done this and it's pretty simple. Don't take my word for it and look inside the JDK sources ;)

wondergeek
Nov 8th, 2005, 09:04 AM
getResourceAsStream is not my problem. That method actually works fine. It's getResource().openStream() that causes the jar file to get locked.

Unfortunatly again, I don't have source to my container... :(

Costin Leau
Nov 8th, 2005, 09:58 AM
Is the resource by any chance inside a jar? if yes then the only workaround I see is to implement the antiJar locking from tomcat. You can take a look at their sources but in short it checks if the file in inside a jar and if it is, it copies the file somewhere else (in a temporary directory) and uses it from there.
It's a relatively ugly hack - try unpacking the war when doing deploy.

wondergeek
Nov 8th, 2005, 10:06 AM
Yes, the file in question (beanRefContext.xml) is in a jar. So that means Tomcat has their own custom class loader that impliments this functionality correct?

Now, I just need to get my container vendor to do that...

Seems like this would be a common problem for all different kinds of contexts and useage scenarios on Windows. What would be involved in including this type of functionality (a ClassLoader decorator that preforms this anti locking behaviour?) in Spring. I've never written a class loader, so I don't know what's involved or even if it's possible. But it seems like it might be a useful hack to have in Spring (at least for the Windows platform).

wondergeek
Nov 10th, 2005, 11:39 AM
I have solved this problem in our environment with the following changes to the org.springframework.core.io.UrlResource class:


...
/**
* Create a new UrlResource.
* @param url a URL
*/
public UrlResource(URL url) {
Assert.notNull(url, "url is required");
this.url = url;
disableUrlCaching();
}

/**
* Create a new UrlResource.
* @param path a URL path
*/
public UrlResource(String path) throws MalformedURLException {
Assert.notNull(path, "path is required");
this.url = new URL(path);
disableUrlCaching();
}

/**
* Disable caching on the URL object to avoid JAR file locking on
* Windows.
*/
private void disableUrlCaching() {
try {
url.openConnection().setDefaultUseCaches(false);
} catch(IOException e) {
// Can't modify caching, ignore
}
}
...

In our container, the JAR file is no longer getting locked after Spring reads the beanRefContext.xml file out of it.

This fix is really only needed for the Windows platform at UNIX does not suffer from the JAR locking problem.

Ideally, this fix could be made part of the core Spring framework as a configurable option or only enabled if Windows was detected as the runtime platform.

Finally, although this patch fixes our immediate problem, are there tradeoffs or potential issues to disabling caching on URLConnection objects as I've done?

Costin Leau
Nov 10th, 2005, 12:42 PM
I assume you lose some small performance but I think this is not a problem as you can always turn back the flag/cache on if you really need it.
I suggest you make an issue on JIRA and link this thread. Nice work, btw.

Juergen Hoeller
Nov 12th, 2005, 08:00 PM
FYI, Spring 1.2.6 has a refined PathMatchingResourcePatternResolver implementation: using relative resources within jar locations, instead of URL building. This means that classpath pattern matching within jar files will now result in ClassPathResource objects rather than UrlResource objects, using ClassLoader.getResourceAsStream() rather than ClassLoader.getResource().openStream().

The original motivation for that change was the following issue:
http://opensource2.atlassian.com/projects/spring/browse/SPR-1435

Please give a recent nightly 1.2.6 snapshot a try and check whether this change also solves the present jar locking issue!

However, note that this only applies to "classpath:" patterns, not to "classpath*:" patterns. Due to the ClassLoader API, we can only use ClassLoader.getResourceAsStream() for the former but not for the latter, where we have to use ClassLoader.getResources().

Juergen

wondergeek
Nov 14th, 2005, 09:02 AM
Jeurgen,

Thanks, I'm sure that change will be helpful in many areas. The reason we ran into this problem was because we were using ContextSingletonBeanFactoryLocator which uses the classpath*: pattern and thus wouldn't be affected by the change you mentioned. However, we're still gonna be ok due to the patch you made in UrlResource.

Thanks!