I'm using AndroidStudio with maven. what i don't understand is how duplicate classes don't confuse classloaders nor the build process.
I made my own android.util.Log class (like, i made a android.util package inside my app, and inside it i made my own Log class), instead of using the standard android Log.d, i use my own. How and why does this work?
i've read somewhere that there are 3 classloaders - your application classloader, 3rd party libs classloader, and system classloaders. so is it that they go in some kind of priority, where if your application classloader finds the .class file then it gets "linked"? and any other duplicate .class files just don't matter?
and... why isn't this behaviour causing any kind of build errors/warnings?
an example is that ... say i want to use reflection to call a certain private class that both i and android will provide. i want guarantee that whether or not it calls mine or ANdroid's, there's not clash that causes a crash. but i'd like to know exactly why/how it works the way i'm seeing it work.
ClassLoaders are organized in a hierarchy:
http://docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html
This may point to what you're looking for:
http://yenliangl.blogspot.fr/2009/11/dynamic-loading-of-classes-in-your.html
Related
I'm doing a library. I have three packages:
Spreadsheet is the main package. The io package is an internal package for internal use. Unfortunately, the user can access to them since they are public classes.
I would like to keep this package, since it allows me to separate concepts while programming, but i would like to "hide" these classes to the end user.
What could i do?
It's good that you're asking yourself this question! I don't see much attention on this lately.
As OdsReader and OdsWriter are used only inside the Spreadsheet class, just move them inside the spreadsheet package, removing the public visibility keywork. They'll now be accessible only from the spreadsheed package's classes.
The solution proposed above, which is over-complicated for your use-case, and which is to use Java 9+ modules (or OSGi - please no!), is not really necessary here, but it's neverthless a step forward in maintaining definitions private and sealed, even to Reflection abusers.
As a side note, I see you've got an exceptions package.
I never recommend doing so, as you'll have to expose those exceptions' constructor to the users of your code, and they'll be able to instantiate them for no good reasons.
Move the exceptions inside the packages which uses them, and declare the constructor as package private.
Starting with java9, you can turn this library into a module. See this jigsaw tutorial.
Modules need to export a package in order for its public members to be accessible from other modules: Simply don't export your internal package, and it won't be visible.
You can also go with something like OSGi, a module system that predates java9. It too has this notion that there's a level beyond public (let's call it 'visible').
A final option is to use classloader shenanigans (where you for example rename your class files to some other extension during the build phase, and have a small bootstrapper in your visible package which creates a classloader that loads classes by looking in the same place as the visible API, and then load files with the alternative extension, and defineClass those into being), but that's a drastic step that introduces quite a bit of headache. I wouldn't take it unless you have excellent reasons to go down this rabbit hole.
In my project (a parser for various formats), I packaged the modules by functionality but there are a few classes which are needed by every module.
Now I'm not sure what's the best practice here:
Should every module/package have a duplicate of the class? The compiler will probably optimize this anyway, won't it?
Create a "Shared" package? This doesn't feel right...
Other options?
I wouldn't package duplicates of the class, that could cause you some endless headaches, because the same class file contents loaded by different classloaders is not considered to be the same class at all by Java, so you'll get impossible to understand error messages. And if all your jars are in the same class loader, only the first class will be loaded, so you may as well put it in one place only.
I would certainly package utility classes in their own jar and have a dependency from all the other packages, as is the case with other libraries you may use etc. You can even use the classpath attribute of the manifest to reference the library easily.
I would suggest you create an abstract class and provide implentation separately in each class and package them separately.
The title pretty much says all.
If I provide a library and someone uses it he can write classes and add them to packages that are defined by my library. However this rises security issues as this would mean that he gets access to methods within the library that are defined without access modifier.
Is there a solution to forbid a user to add classes to an existing package?
Yes (but). The sealed manifest attribute will seal a package, preventing classes from other sources from being included. (technote)
Though it kind of depends on the context. If the someone using it gets to play with the library jar file first, then it's game over.
If both the library and client code is loaded by the same class loader, then that is in generally problematic, although classes signed with different signatures cannot coexist within the same package. If different class loaders are used even with a parent-child relationship, then at runtime packages with the same name but loaded by different class loaders are different packages.
For Java PlugIn and Java WebStart, Trusted-Library manifest entry causes the jar to be slid into a parent class loader, protecting it from potentially untrusted application code.
I got two classes with the same package in different JARs. Until the previous version, both classes were identical, so i had no issues in loading them. Now, one of them has a new method added and if I want to access it, not only should I import the class with that package, i also need to make sure the jar with the correct class comes first in the classpath.
i.e. javac -classpath "%classpath%;a.jar;b.jar" MyClasses..
where a.jar has the class with my new method.
Now, how do i ensure this when my app goes to production, where it's deployed as an EAR file, with all the libraries under WEB-INF/lib?
How do I know which jar gets the preference over the other? Is it the alphabetical order like a.jar is given the first preference over b.jar?
I've read this safe-class-imports-from-jar-files thread and got to know about writing a custom classloader, but is there a better simpler solution that? Cos I'm just going to access this method in that whole JAR in this current project and writing a classloader seems a bit overkill.
And please don't ask me "Why the hell same class with same package in different JARs?" It's absolutely out of my control and it'll take some time to get this corrected.
Environment details: IBM WAS 6.1 on their 1.5 Java.
Please ask me more questions, if I don't make much sense. Thanks in advance!
You can try to change the startup script of your server and specify the jar with the correct class in the bootclasspath by using java -Xbootclasspath .... Otherwise there is no guarantee which one of the 2 jars will load up first.
As far as I know, the order of jars being loaded from WEB-INF/lib is arbitrary - I asked a similar question about JBOSS and got the reply ( from RedHat ) that it depends on the order that java.io.File.listFiles() returns them in ( and that is not a guaranteed order ).
A custom classloader would be an option, but have you considered repackaging the jars - removing the duplicated classes?
Websphere allows you to specify the order in which classloaders of a particular application are inquired when searching for a class (the classloaders are hierarchically structured, from the topmost that loads JRE classes, down to classloader loading classes in your WAR).
During deployment of an app, you can specify if the order of inquiring the classloaders when searching for a class. There are two modes - Parent first (i.e. query the topmost classloader first) and parent last (query the app classloader first). This can be specified on both EAR and WAR level.
Packaging the duplicated jars to different locations in the app (e.g. one to EAR's classpath, the other to WAR's WEB-INF/lib) and setting the classloader orderING apropriately may solve your problem. However, if both your JARs have to be on the same level (e.g. WEB-INF/lib), then there's no way to specify which one will be used when loading the duplicated class.
The order of the JARs in one application is likely to be alphabetical but the order of applications might not. Additionally, it depends on how the server handles classloading, i.e. whether it replaces existing classes or skips the new ones.
Although you already stated that, I'd still like to give that advice: Having the same class in multiple JARs deployed in one application (which could happen with versioned jars, for example) is always a bad idea. Your better off to invest the time to fix that instead of trying to mess with class loading.
This might come out to be pretty vague but I do remember resolving this issue a long time back by messing around with the WAS admin console for that given application and rearranging the relevant JAR files using their web UI. Not sure if this is an acceptable step in your case but worth a try in case everything else fails.
assuming you have some control over the deployment, fix the classloading yourself. combine the problematic jars yourself by unzipping them in reverse loading order into the same directory and then re-zipping into a new jar. then deploy the app with the new combo jar. no duplicate classes, problem solved.
or, just delete the dupe classes from the jars before deploying.
I'm quite restricted in the platform I'm currently working on (JDK 1.3, BD-J). One JAR file I would like to use attempts to perform a self-integrity check on load and if it fails it goes into an inoperable state. It's quite difficult to find out why this is happening but most sources point to that it cannot find/access it self through the BD-J structure, so it dies.
This rules out using it at load time and instead to load it in the application itself. This is quite a large library so I have to create quite an amount of interfaces so I can cast a loaded object to it and potentially use it. This is where my problem lies.
The interfaces are loaded on normal load time and the library is then loaded during run time and casted to the previously loaded interfaces, is this a problem? I'm receiving ClassCastException
I've based the interfaces off the libraries public methods as best I can, but when I attempt to cast to an interface I receive the ClassCastException. Note: It all loads fine, I can access constructors and read the method names. Just when casting it for it to be useable it fails.
The interface packages are different in my project to that of the toolkit, does this matter?
I'm running out of ideas, is there something I have overlooked?
Thanks.
I'm not sure I fully grok what your problem is - maybe some more details about what the class hierarchy looks like would help in figuring out the situation. From what you wrote I can guess two possible scenarios:
.1. The classes you want to use do not implement any interface.
In this case no matter what you name your interfaces, it will not work, since the classes you're loading do not implement them. You're stuck with using reflection if you can't load that jar as part of the boot classpath.
.2. The classes you want implement some interface that you're trying to replicate.
In this case you interface implementation must match the exact qualified name of the interface the classes are implementing. Normally, when loading the classes from the jar, the class loader will pick up the interfaces from the system class loader first, thus loading your interfaces, and everything should work.
If they use some crazy internal class loader, though, they might still try to load their own interfaces. You could try to figure out if that's the case by using "-XX:+TraceClassLoading", although I don't know if the 1.3 jre will understand that option.
Now if you're willing to experiment more, you could also try another approach. Write your own class loader that loads both the classes from that jar and the code you want to run. That way, your code would be able to directly refer to the classes in that jar, but to start your application the "main" method will have to be one that initializes this classloader, loads the "real" main class using reflection, and executes its main() method also via reflection.
Most probably the classes are loaded by different class loaders. http://mindprod.com/jgloss/classloader.html may give some idea.