Substituting Java API classes for enhanced functionality (e.g. java.io.File)

  • Thread starter Christian Schlichtherle
  • Start date
C

Christian Schlichtherle

Hi,

I have written a substitute for some java.io.* classes. My classes treat ZIP
or JAR files exactly like directories, so that you can e.g. list ZIP files
and arbitrarily read or write or delete their entries. My substitutes have
the same name as in the java.io package, but live in their own package (e.g.
de.schlichtherle.io.File instead of java.io.File). This provides 98% source
code compatibility - most of the time a simple import statement at the top
of a using class is enough to use the new functionality.

To achieve 100% compatibility I would like to provide a modified classloader
which loads my classes instead of the java.io.* classes. This is not a
problem, as their is enough documentation on how to do it (basically you
only need to override the loadClass(...) method). However, my classes
delegate to the original java.io.* classes when e.g. encountering ordinary
files.

So how can I make an application using my classes as java.io.* when at the
same time my classes need to delegate to the original java.io.* classes???

Any hints would be helpful.

With best regards,
Christian
 
D

Dale King

Christian said:
Hi,

I have written a substitute for some java.io.* classes. My classes treat ZIP
or JAR files exactly like directories, so that you can e.g. list ZIP files
and arbitrarily read or write or delete their entries. My substitutes have
the same name as in the java.io package, but live in their own package (e.g.
de.schlichtherle.io.File instead of java.io.File). This provides 98% source
code compatibility - most of the time a simple import statement at the top
of a using class is enough to use the new functionality.

To achieve 100% compatibility I would like to provide a modified classloader
which loads my classes instead of the java.io.* classes. This is not a
problem, as their is enough documentation on how to do it (basically you
only need to override the loadClass(...) method). However, my classes
delegate to the original java.io.* classes when e.g. encountering ordinary
files.

So how can I make an application using my classes as java.io.* when at the
same time my classes need to delegate to the original java.io.* classes???

Better reread the license for Java again. You'll find that such a thing
would violate the license agreement:

"Sun grants you a non-exclusive, non-transferable, limited license
without fees to reproduce and distribute the Software, provided that...
(iii) you do not distribute additional software intended to replace any
component(s) of the Software..."
 
P

Patricia Shanahan

Dale said:
Better reread the license for Java again. You'll find that such a thing
would violate the license agreement:

"Sun grants you a non-exclusive, non-transferable, limited license
without fees to reproduce and distribute the Software, provided that...
(iii) you do not distribute additional software intended to replace any
component(s) of the Software..."

In addition to the license, there is a strong program readability
objection to replacing java.lang classes.

A reasonable Java programmer, seeing e.g. "import java.io.File;" at the
top of a program, would expect references to type File in the code to
mean the one in the normal API libraries.

They would expect, for example, to be able to use features of a later
version of File by merely installing a new JDK/JRE, with a new rt.jar.
They would look at the Sun-distributed source code when trying to
understand a NullPointerException with a java.io.File method in the
stack trace.

Patricia
 
C

Christian Schlichtherle

Hi,

I'm not breaking any of these statements as I would not redistribute Sun's
JDK/JRE. My classes would simply be distributed as part of an application or
a JAR library which requires the separately provided JDKJRE to run. There is
actually no difference to any other application or library.

Regards,
Christian
 
C

Christian Schlichtherle

Hi,

please note that no application could use this code just by accident. There
needs to be some static method call in front to replace the genuine
java.io.File by installing a custom class loader for the running thread. It
would actually work a bit like installing pluggable UI classes for Swing.
Thus, whoever uses it does know that he is not dealing with a genuine
java.io.File.

Second, if someone wants to upgrade to a new JDK/JRE, fine: Just do it and
my replacement classes will delegate to the updated JDK/JRE when needed
(e.g. when working on ordinary files rather than ZIP files), so there is no
need to worry about this.

Third, thanks to the delegation, the whole API is tested to be 100% backward
compatible with JDK 1.4.2 and JDK 1.5.0. Whatever you do with ordinary files
and directories, my API will behave exactly the same as the genuine
java.io.* classes (including exception throwing).

Thinking in layers, here is how it works in terms of program flow:

Application code -> substituted java.io.* -> genuine java.io.* (residing in
rt.jar)

As you can see, the rt.jar is not touched (reengineered or whatever) at all.
My substitute is just an add-on. It needs to be installed once by any
application who wants to use its enhanced features.

Regards,
Christian
 
P

Patricia Shanahan

Christian said:
Hi,

please note that no application could use this code just by accident. There
needs to be some static method call in front to replace the genuine
java.io.File by installing a custom class loader for the running thread. It
would actually work a bit like installing pluggable UI classes for Swing.
Thus, whoever uses it does know that he is not dealing with a genuine
java.io.File.

I've done a lot of maintenance work, so I tend to think about
programming in terms of picking up a large, unfamiliar program and
attempting a bug fix or feature extension, rather than writing a program
from scratch. In my model of programming, some other programmer, who I
may never have met, did the work to use your code before I even knew the
application existed. Even when I am writing from scratch, that model
influences my programming.

Most interesting programs are far too large for me to completely read
and understand them before starting productive work. The better a
programming style supports selective reading the better I like it.

I would not expect to need to read the application initialization flow
to understand what a java.io class does, for a Zip file or for any other
sort of file. I find that out by reading the java.io javadocs.

The requirement to put a distinguishing import statement in each file
that depends on your package is a valuable feature, not a bug. The
frustrating thing is that, with the import, your package looks very
useful. Without it, it opens up fresh fields for code obfuscation in java.
Second, if someone wants to upgrade to a new JDK/JRE, fine: Just do it and
my replacement classes will delegate to the updated JDK/JRE when needed
(e.g. when working on ordinary files rather than ZIP files), so there is no
need to worry about this.

How do you make sure the JVM will not object when I call a method that
was added to a java.io class, say around JDK 1.8.2, but does not exist
in the corresponding class in your package?

On the licensing issue, how to do you interpret item C in "SUPPLEMENTAL
LICENSE TERMS", in the JDK 1.5 license?

Patricia
 
C

Christian Schlichtherle

Patricia Shanahan said:
I would not expect to need to read the application initialization flow
to understand what a java.io class does, for a Zip file or for any other
sort of file. I find that out by reading the java.io javadocs.

Although I understand your point (cause I do have the same experience), I'ld
like to add something: Your criticism applies the same to the pluggable L&F
of Swing and any other pluggable code based on Reflection. This feature
really does make code harder to debug und understand, but looking at all the
plugin code and all sorts of *Beans stuff, I also think that this kind of
"dynamic linking" is one of the core advantages of Java compared to e.g.
C++. Without this ability, we wouldn't have (Enterprise) JavaBeans or SPI
code (like in the java.security package) at all. In essence, Java wouldn't
be Java.
How do you make sure the JVM will not object when I call a method that
was added to a java.io class, say around JDK 1.8.2, but does not exist
in the corresponding class in your package?

That's a fair point: If the classloader approach would work, all that is
required for a normal application is to compile the code against the java.io
package. If that changes, my plugin code would need to change as well. I
might be able to provide this through the use of the Proxy class, but still
this would leave the opportunity of my code to yield new methods which do
not behave correctly when used within ZIP files.
On the licensing issue, how to do you interpret item C in "SUPPLEMENTAL
LICENSE TERMS", in the JDK 1.5 license?

I would not (re)distribute JREs or JDKs, so that term doesn't apply at all.

After all, the point about maintainability makes me rethink the strategy
again: The existing de.schlichtherle.io package works fine for about nine
months in daily use. But as it needs to inherit from java.io.File (in order
for a JFileChooser to browse ZIP files), there are some subtle traps which I
hoped to escape by turning the package into a pluggabe replacement for the
java.io package...

With best regards,
Christian
 
J

Joan

Christian Schlichtherle said:
Hi,

I have written a substitute for some java.io.* classes. My classes treat ZIP
or JAR files exactly like directories, so that you can e.g. list ZIP files
and arbitrarily read or write or delete their entries.

Why bother, microsoft "explorer" does this already.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top