Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

What is the purpose of having a wrapper class for JarFile in Spring-boot-loader?

+2
−0

My question relates to Spring-boot-loader v2.6.15.

For the purposes of this question, I will refer to Spring's custom JarFile class as CustomJarFile to avoid confusion.


Context

I am reusing some of the jar package code in spring-boot-loader. I noticed that they define their own CustomJarFile implementation that (most relevant to this question) overrides the close() method like so:

@Override
public void close() throws IOException {
	if (this.closed) {
		return;
	}
	super.close();
	if (this.type == JarFileType.DIRECT) {
		this.rootFile.close();
	}
	this.closed = true;
}

They also define a JarFileWrapper class that does not override the close method.

The custom JarUrlConnection#get() method notably returns an instance with the wrapper rather than the jar itself like so:

static JarURLConnection get(URL url, JarFile jarFile) throws IOException {
    // some stuff here...
    return new JarURLConnection(url, jarFile.getWrapper(), jarEntryName); // JarFile here is the custom class
}

Testing the code without the wrapper class by returning a JarUrlConnection with the CustomJarFile itself rather than its wrapper results in a "zip file closed" exception.

This is reasonable given that when you call close() on the wrapper, it will call the close() method of the ZipFile class. However, if the JarFile itself is sent, it will call the close() method of the JarFile since it is overriden.

Notably, The JavaDoc of the wrapper class states:

A wrapper used to create a copy of a {@link CustomJarFile} so that it can be safely closed without closing the original.


Question

I do not quite understand the purpose of using a wrapper here. If the goal is to just avoid closing the CustomJarFile, wouldn't simply not overriding the close() method in CustomJarFile (allowing it to propogate to ZipFile) have the same effect as using a wrapper? It feels like we just added a redundant layer.

While debugging, I've noticed that the close() method of CustomJarFile is never called between startup and shutdown of the service which makes me further believe that the wrapper is pointless if you simply remove the overriden close method in CustomJarFile.

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+1
−0

Why does the wrapper exists and why doesn't it override close()?

Apparently, this wrapper was introduced to "Attempt to fix memory leak in JarFile class". The memory leak issue was caused by a change in java 11 that introduced FinalizableResource which made it so that any JarFile implementation that override close() will end up with finalizer based cleanup.

Ultimately, too many JarFile instances are being created, especially given they used to wrap the JarFile in a JarFile, without ever being cleaned by the garbage collector since FinalizableResource doesn't deference it.

The simplest way to tackle this was to create a wrapper class that doesn't override close() which would be created/returned instead of JarFile, essentially working around the problem.

Why was the JarFile wrapped in itself before the wrapper was added?

In the question I said the close() method in JarFile is never called which made sense since it was never returned. However, it seems JarFile is actually exposed through JarURLConnection.getJarFile() and therefore may get closed and run you into trouble. I assume the exposure is intentional since that's not what was changed. My testing missed it, but the point here is that situations definitely exist where it could be called.

Java got rid of problematic code?

Well, apparently Java didn't like the change any more than Spring did and FinalizableResource was removed in literally the first java 12 tag: jdk-12-ga. I currently use Java 17 and it still does not exist.

Is the wrapper still necessary?

Unclear... It may still be necessary for the same reason JarFile was wrapped in itself before. It may not since the language has changed a lot since. Either way, I've learned to be happy with the wrapper and coexist :)

History
Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »