A customer requires a preview of a new feature of our product. They asked to have that feature sent to them in a jar file (like a patch). There's no problem with including the new classes in said jar file. However, an existing class was modified, which is needed to integrate the new feature. They just want to add this new jar file without having to update the core classes of our product. So, the question is: is it possible to override an already existing class using a separate jar? If so, how?
Thanks in advance.
There's a chance it'll work if you put the new jar earlier in the classpath than the original jar. It's worth trying, although it still sounds like a recipe for disaster - or at least, really hard to debug issues if somehow both classes are loaded.
EDIT: I had planned to write this bit earlier, but got interrupted by the end of a train journey...
I would go back to the customer and explain that while what they're asking is possible, it may cause unexpected problems. Updating the jar file is a much safer fix, with much less risk. The phrases "unexpected problems" and "risk" are likely to ring alarm bells with the customer, so hopefully they'll let you do the right thing.
Yes and no, it depends on your environment.
If you use, for example, OSGi and have your versions under control, it's just a matter of installing a new bundle with the exported package at a higher version (assuming your version ranges are lenient enough).
If you use plain old Java with no fancy custom class loading, you should be good to go putting it earlier on your class path (as others already mentioned).
If you do have custom class loading, you'll need to make sure that all the classes that your 'patched' class needs, and indeed the entire transitive dependency hull, is visible from the class loader which is loading the patched version, which might mean you need to ship the entire application, worst case.
All of the answers that stipulate putting the updated classes before the ones they are replacing in the classpath are correct, only provided the original JAR is not sealed or signed.
Yes, it may be possible, by putting it earlier on the classpath than your original jar. However, relying on the ordering of your classpath is not always going to lead to happiness. I'm not sure if it is even documented in the Java Language Spec; if not, then it's going to break for different JVMs and even different versions of the same JVM.
Instead, consider quoting a realistic time frame to integrate the new feature into the current codebase. This is perhaps not the answer you're looking for.
Probably more than you need for this specific case, but in generally if you just want to tweak or augment an existing class you can also use AspectJ with load-time weaving.
Related
I have a fairly new small team project with a new team and we have tried a few different approaches and had a few significant iterations in design.
We have some classes laying around that should not be used and a few classes that have such messes as ClassStuff and ClassStuffImproved. We have SVN but I don't think nuking all the junk and making people dig manually in the history is productive. Some things may need to be re implemented properly and the previous poor implementation would provide a reference. I do however want to break anything that depends on junk. I also want the project to build even if these junk files are broken.
Is there a folder convention? Should i put all nuked content in a textfile so at least its easily searchable when someone wonders where a class went?
What is the typical convention here?
I've kept SVN history logs in place by using TortoiseSVN's Repository Browser. If you have a recent SVN server, operations like "Move to", "Copy to" and "Rename" will keep history logs to the original. Be sure to update all checkouts after these kind of changes (else local changes will have a hard time merging with the new server reality).
Files you no longer use can be moved to a "museum" or "attic" branch (and try to keep the original directory structure in there).
Apache projects have been changing package names to indicate new versions that more or less break with previous versions (e.g. compare commons-lang 2.6 with 3.2). In SVN you can do this by simply renaming a directory in the trunk. I find it a good convention: all code that depends on the old version breaks but is easily updated by adding one character to the import statements (and ofcourse you verify the new version works as expected by reading the updated Apidocs).
Re-implementing junk is, in my experience, harder than it looks at first glance. I use the #Deprecated annotation at class-level at first for classes that need to be replaced. Your IDE will clearly show where deprecated code is used and the compiler will show warnings. Create the re-implementation in a package 'version 2'. Then you can update like you would update from commons-lang version 2 to commons-lang version 3. Once this is all done, delete the deprecated classes (or package).
Be careful when deleting deprecated classes though: the last time I tried deleting deprecated classes, I found a dependency on the deprecated classes in one little and old, but crucial and heavily tested, program and had to keep the deprecated classes in place to maintain backwards compatability.
I'm not sure there is a convention, in my projects I just copy it into a new version with a name like:
MyClass2.java
This way I won't get into trouble when I'm importing and old version of a source file. And I also comment the whole contents of MyClass.java with /* ... */. After some reasonable amount of time I drop the old MyClass version and leave MyClass2 in the project. I'm leaving it as MyClass2, so that it states that it this file has history and it's a more advanced version of my class, so brings a little fun into the process.
I've never seen anyone doing this, but the practice seems quite intuitive.
Is it possible to import and use two different classes with the same name and package in java?
For example, let's say I have two classes named "com.foo.Bar" that are slightly different. I'd like to be able to use both, but I have a restriction (because of stupid reflective crap) that forces me to keep the names and packages the same.
Is there some feature of java that would allow me to import and isolate each of these classes?
To elaborate, I changed my avro schemas in ways that they shouldn't have ever been changed (oops!) and now I'd like to go back and change the old avro files that can't be read with my new schema into files that can be read by my new schema. Avro seems to force you to use a specific class and package name to load the files.
Yes there is. You would need to implement your own Classloader and play some games to be able to access both during runtime.
I'm sure this is possible, because I ran into a very hard to debug issue where someone had a weird Classloader in their product that was messing up loading libraries and providing 2 different versions of the same file from 2 different versions of the library.
However, this sounds like an INCREDIBLY bad idea. I'd go back and find a different way of fixing your issue. This will only bring you heartache in the long run. Heck, it probably already is, as you investigate class loaders.
EDIT: To be specific, you cannot "import" both. But you can access both at runtime.
No, java packages are used precisely to avoid that problem.
Yes it is. It does require you to make your own ClassLoader, though
I had made a demo of that on github to!
If you really most definitely must do something like this, you can achieve it by using different classloaders and possibly reflection.
This is not the way Java works and it's not allowed on purpose - you shouldn't be doing stupid things which will screw up things for you.
There are no namespaces in Java, only in C#, so I assume you mean packages. There can only be one fully qualified name per project.
Technically it can be done using some low-level trickery such as rewriting the byte-level code. As far as I know the different java crypter/encrypters work like that - they have a lot of classes called A.class B.class C.class etc.
It sounds to me like you need to define your method signatures in an interface called com.foo.Bar. Then provide two different concrete implementations of the interface (say, com.foo.DefaultBar, and com.foo.SpecialBar). This way, you can program against the interface type, and switch between the two different implementations as required.
Can you elaborate on what you mean by "reflective crap"? That may provide insight into your exact issue.
Don't mess with the class loader or any other low level trickery. The best way to solve such issues it to have a clear design in the first place that anyone can understand.
As already mentioned writing your own Classloader or additionally use a OSGi framework like Equinox which does the classloading for you
I came across an application in which multiple versions of jar files are included. For instance commons-fileupload-1.8.jar and commons-fileupload-1.6.jar.
Would this cause any issues?
Thanks,
Raghuram
Yes, that's a bad idea. What will probably happen if you're lucky is that whichever of the two versions that comes first in the classpath will satisfy all the references. If that happens, then the other versions of the .jar file won't matter at all. However, old code that relies on an old version of the library might incorrectly pick up new versions of some classes, and so all sorts of weird bad things can happen.
Now, in an application with many separate class loaders, such a thing might work out, as long as the separate subsystems with separate class loaders keep the different versions separated. If you're talking about multiple references to a .jar in the system classpath, however, then it's not a case of multiple class loaders.
In my experience, yes it will. The jar that gets used will be the one that is loaded first and that is based on the class loader and not, I think, in a guaranteed order. So that means that some code might be depending on a feature in version 1.8 and then 1.6 gets loaded and throws an exception when you try to use it.
There will only be issues if both versions are actually loaded through the same class loader, e.g. by both appearing on the regular classpath.
It can be made to work if you load the different versions through separate class loaders.
Presumably the application you looked at is doing this. Or they just upgraded the JAR and forgot to delete the old version.
Definitely and it might give you different results sometimes depending on the app server and sometimes depending on the packaging.
If your application uses a class say X which is in both jars, the X.class one of them will be loaded by the classloader, and lets say that needs a class Y which is in both jars again one of them will be loaded (usually the first) but there is no guarantee that they will be from same jar.
So if there are two versions of same jar you need to inspect why this is happening and try and remove one of them. (If you are using maven there are different ways of achieving this)
yes it causes problems because only one of them will actually be used depending on which one gets loaded by the class loader(s) and what order they are loaded.
This is for an Android application but I'm broadening the question to Java as I don't know how this is usually implemented.
Assuming you have a project that targets a specific SDK version. A new release of the SDK is backward incompatible and requires changing three lines in one class.
How is this managed in Java without duplicating any code(or by duplicating the least amount)?
I don't want to create two projects for only 3 lines that are different.
What I'm trying to achieve in the end is a single executable that'll work for both versions. In C/C++, you'd have a #define based on the version. How do I achieve the same thing in Java?
Edit: after reading the comments about the #define, I realized there were two issues I was merging into one:
So first issue is, how do I not
duplicate code ? What construct is there that is the equivalent of a
#define in C.
The second one is: is it possible
to bundle everything in the same
executable? (this is less of a
concern as the first one).
It depends heavily on the incompatibility. If it is simply behavior, you can check the java.version system property and branch the code accordingly (for three lines, something as simple as an if statement).
If, however, it is a lack of a class or something similar that will throw an error when the class is loaded or when the code gets closer to execution (not necessarily something you can void reasonably by checking before calling), then the solution gets a lot harder. The notion of having a separate version is the cleanest from a code point of view, but it does mean you have to distribute two versions.
Another solution is reflection. Don't reference the class directly, call it via reflection (test for the methods or classes to determine what environment you are currently running in and execute the methods). This is probably the "official" approach in that reflection exists to deal with classes that you don't have or don't know you will have at compile time. It is just being applied to libraries within the JDK. It gets very ugly very fast, however. For three lines of code, it's ok, but doing anything extensive is going to get bad.
The last thing I can think of is to write common denominator code - that is code that gets the job done in both, finding another way to do it that doesn't trigger the problematic class or method.
I would isolate the code that needs to be different in a separate class (or multiple classes if necessary), and include / exclude them when building the project for the different versions.
So i would have like src/java/org/myproj/Foo.java which is the common stuff, and then oldversion/java/org/myproj/Bar.java and newversion/java/org/myproj/Bar.java which is the different implementations of the class that uses changed api.
Then I either compile "src/java and oldversion/java" or "src/java and newversion/java".
Possibly a similar situation, I had a method which wasn't available in the previous version of the JDK but if it was there I wanted to call it, I didn't want to force people to use the more recent version though. I used reflection to look for the method, if it was there I called it, if it wasn't I didn't.
Pretty hacky but might give you what you want.
Addressing Java in general, I see two primary approaches.
1). Refactor the specific code to its own library. Have different versions of that library. Effectively your app is creating an abstaction above the different SDKs. Heavyweight for 3 lines of code, but perhaps quite reasonable for larger scale problems.
2). Injection using annotation. Write your own annotation processor to manage the appropriate injection. More work, but maybe more fun.
Separate changing code in different classes with the same interface. Place classes in the same jar. Use factory design pattern to instantiate one or another class depending on SDK version.
Is there any tool that lists which and when some classes are effectively used by an app or, even-better, automatically trims JAR libraries to only provide classes that are both referenced and used?
Bear in mind that, as proven by the halting problem, you can't definitely say that a particular class is or isn't used. At least on any moderately complex application. That's because classes aren't just bound at compile-time but can be loaded:
based on XML config (eg Spring);
loaded from properties files (eg JDBC driver name);
added dynamically with annotations;
loaded as a result of external input (eg user input, data from a database or remote procedure call);
etc.
So just looking at source code isn't enough. That being said, any reasonable IDE will provide you with dependency analysis tools. IntelliJ certainly does.
What you really need is runtime instrumentation on what your application is doing but even that isn't guaranteed. After all, a particular code path might come up one in 10 million runs due to a weird combination of inputs so you can't be guaranteed that you're covered.
Tools like this do have some value though. You might want to look at something like Emma. Profilers like Yourkit can give you a code dump that you can do an analysis on too (although that won't pick up transient objects terribly well).
Personally I find little value beyond what the IDE will tell you: removing unused JARs. Going more granular than that is just asking for trouble for little to no gain.
Yes, you want ProGuard. It's a completely free Java code shrinker and obfuscator. It's easy to configure, fast and effective.
You might try JarJar http://code.google.com/p/jarjar/
It trims the jar dependencies.
For most cases, you can do it quite easily using just javac.
Delete you existing class files. Call javac with the name of your entry classes. It will compile those classes necessary, but no more. Job done.