Community
Participate
Working Groups
This problem was found using eclipse 2.1.3 but also exists in 3.0. This problem doesn't occur all the time, but below I will provide a way to reproduce this problem fairly consistently. I will also provide a fix (one line). Problem: I have java project "X" which contains a jar file (my jar file is named log4j.jar). I have another java project "Y" which has X's jar file on its build path. Occassionally, when I try to delete X (specifying to "delete contents..."), I will get a "Delete Problems" dialog that reads: title: Problems encountered while deleting resources. details: Could not delete: /X. Could not delete: D:\dev\workspace\X\log4j.jar If this error occurs I usually have to close/re-open my workspace in order to delete the project. How to reproduce: I come across this problem in more likely scenarios, but to reproduce this problem fairly consistently I just double click on the jar file (in Y) to expand it and then quickly double click various packages to expand/collapse them and double click various class files to open editors, then close them, build the project, right click the project and go to the properties page - go to the "Libraries" tab of the Java Build Path page and click OK (mix up these steps - open some editors, build, close editors, etc.). Doing this for less than a minute should get you in a state to reproduce the problem. For a more exact approach, you can track the number of times a java.util.zip.ZipFile object is created (with your jar file as the File parameter in the constructor) vs. the number of times ZipFile.close() is called (again, on a zip file representing your jar file) and wait for the number of creations to get well ahead (>5) of the number of times it is closed before you try to delete the project. If needed I can detail a fairly easy way to do this by modifying your rt.jar and replacing System.out with a custom PrintStream class. Cause: Zipfile.close() isn't being called explicitly - it appears that we're allowing garbage collection to take care of this. When we get to org.eclipse.ui.actions.DeleteResourceAction.delete(IResource[], IProgressMonitor) (after following the steps above and trying to delete the project in question) - garbage collection hasn't cleaned up these unreferenced ZipFile objects. Proposed fix: Making "System.gc();" the first line of the method: org.eclipse.ui.actions.DeleteResourceAction.delete(IResource[], IProgressMonitor) fixes this problem.
Relying on the behaviour of System.gc() isn't a good idea - it's implementation specific and is spec'd to only really be a suggestion to the VM. Calling close() on the zipfile seems to be a much better idea. Passing to JDT.
One known scenario for this to occur is when the Java indexing is busy processing the offending file. You may reveal its activity by toggling the progress view in verbose mode. Can you then tell us whether the indexer is at work when this occurs ?
When I tested this I made sure nothing was showing in the Progress status area (bottom right hand corner). I would normally try the deletion soon after following the steps I mentioned, but after any tasks (i.e. builds, etc.) were complete. After it failed once I tried waiting a long time (maybe 30 minutes) then attempted to delete again - and received the same error.
I looked at our code and indeed I found one occurence where we create a ZipFile without closing it. This happens when opening a .class file that doesn't have any source attached. This is the only occurence though. David, do you see this problem if you open .class files that have source attached? If you see the problem only when opening .class files that don't have source attached, then the fix is easy and I can provide you with a patch so that you can try out. Please let me know wich Eclipse version you would like this patch to be compatible with (2.1.3 or 3.0.1 ?).
Hi Jerome, I can't say if the scenario you described is the same as what David was able to reproduce (I was able to reproduce it once but I do not remember viewing the class file with no source attached - I could be wrong though), but the patch would need to be against the v2.1.3 release. Thanks!
Created attachment 15973 [details] Patch against 2.1.3
A full JDT Core patch can be found at http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout%7E/jdt-core-home/r2.1/main.html#updates
Created attachment 15974 [details] For convenience, this is the full source for ToolFactory.java after applying the patch on 2.1.3
Released fix to HEAD, 3.1 maintenance and 2.1 maintenance streams. Added regression test DeleteTests#testDeleteProjectAfterUsingJar() to HEAD.
I just had a quick question regarding the ToolFactory code that you posted... In the createDefaultClassFileReader(String zipFileName, String zipEntryName, int decodingFlag) method, shouldn't there be a call to do zipFile.close() before returning null in the if(!zipEntryName.toLowerCase().endsWith(".class")) {} statement as well as after the Util.getZipEntryByteContent(zipEntry, zipFile) statement? For example, public static IClassFileReader createDefaultClassFileReader(String zipFileName, String zipEntryName, int decodingFlag){ try { if (JavaModelManager.ZIP_ACCESS_VERBOSE) { System.out.println("(" + Thread.currentThread() + ") [ToolFactory.createDefaultClassFileReader()] Creating ZipFile on " + zipFileName); //$NON-NLS-1$ //$NON-NLS-2$ } ZipFile zipFile = new ZipFile(zipFileName); ZipEntry zipEntry = zipFile.getEntry(zipEntryName); if (zipEntry == null) { return null; } if (!zipEntryName.toLowerCase().endsWith(".class")) {//$NON-NLS-1$ zipFile.close(); // <-- close here ? return null; } byte classFileBytes[] = Util.getZipEntryByteContent(zipEntry, zipFile); zipFile.close(); // <-- close here ? return new ClassFileReader(classFileBytes, decodingFlag); } catch(ClassFormatException e) { return null; } catch(IOException e) { return null; } } } I don't know if this would affect this bug but I just wanted to question it first :) Thanks!
I tested the fix posted and it did not resolve the reported problem.
I would rather write it like this: public static IClassFileReader createDefaultClassFileReader( String zipFileName, String zipEntryName, int decodingFlag) { ZipFile zipFile = null; try { if (JavaModelManager.ZIP_ACCESS_VERBOSE) { System.out.println("(" + Thread.currentThread() + ") [ToolFactory.createDefaultClassFileReader()] Creating ZipFile on " + zipFileName); //$NON-NLS-1$ //$NON-NLS-2$ } zipFile = new ZipFile(zipFileName); ZipEntry zipEntry = zipFile.getEntry(zipEntryName); if (zipEntry == null) { return null; } if (!zipEntryName.toLowerCase().endsWith(".class")) { //$NON-NLS-1$ return null; } byte classFileBytes[] = Util.getZipEntryByteContent(zipEntry, zipFile); return new ClassFileReader(classFileBytes, decodingFlag); } catch (ClassFormatException e) { return null; } catch (IOException e) { return null; } finally { if (zipFile != null) { try { zipFile.close(); } catch (IOException e) { // ignore } } } } Using a finally block covers all returns statement within the method. This is indeed what we have done in the 3.0 stream.
Created attachment 15991 [details] Apply after the first patch has been applied
The code in comment 12 is much better than what I had posted in comment 10. I tested the latest jdt.core update produced by Olivier and I can no longer reproduce the problem where JAR files are locked so it looks like this problem is resolved. Thank you very much!
It looks like I missed one case in the 2.1 stream. So thanks Troy for identifying that, and thanks Olivier for the fix.
Created attachment 16011 [details] For convenience, this is the full source for ToolFactory.java after applying the 2 patches on 2.1.3
Comment 14 indicates that the problem is fixed with the 2 patches. Marking as FIXED.
Candidating fix for 3.0.2 release (not committed yet) Once this is ported to R3_0_maintenance stream, the Target Milestone should be set back to 2.1.3
Verified for 3.0.2 with build M200502161722