I've a chunk of code that works fine in isolation, but used a dependency in a clients project fails. A call to
Document doc = impl.createDocument(null,null,null);
fails (looks like the problem at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4913257). The instance of 'impl' in my unit tests is an instance of com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl. In my clients code, its an instance of org.apache.xerces.dom.DOMImplementationImpl.
How do I trace back this dependency? I've tried manually going through all classes and jar files in the classpath, but cannot find the provider of org.apache.xerces.dom.DOMImplementation. Is is possible to observe when classes are loaded (and why)? How is the particular DOM implementation selected? Can I for now force the jvm to use a particular implementation?
The implementation in the "com.sun.org.apache..." package is the Xerces that's packaged as part of the JRE. The one starting "org.apache..." is the standalone distribution from Apache. They can be run together in the same application, but it can get quite confusing.
Your client's project would appear to contain a copy of the standalone apache distribution (probably xercesImpl.jar). Ask them to rem,ove it and see if it starts using the built-in JRE code.
Most likely you do not have xercesImpl.jar in the client project's classpath
I think you could deal with endorsed standards stuff.
Related
I have a java project where I use an external jar (not controlled by me).
Until now whenever that a new version of that library is out, I update
my project to use the more recent one, but now is required that the
project uses different versions of that library.
My problem is I don't have any clue how to do that.
How do I tell in java to make the imports according a version of a jar,
What I need to do:
int versionFlag = getVersion2use();
if(verssionFlag = 0){
use imports from last version
}else if(verssionFlag = 1){
use imports from last version 1
} else if(verssionFlag = 2){
use imports from last version 2
}
This is to be used at runtime!
This is usually something that a project will do at build time rather than dynamically at runtime.
That said, here's a good answer on how to add a jar to the system classloader dynamically at runtime, which is something you could work into your general logic above:
How should I load Jars dynamically at runtime?
With respect to imports, there's no way around the fact that you can't dynamically pick your imports. So if you're lucky the two versions have the same basic API. If that holds, add the jar to classloader as early as possible in your app and then develop like normal.
If the two versions have different a different API, however, then you're going to have to write some very convoluted code that tries building objects and almost ubiquitously catches all the many different class load / class incompatibility exceptions (such as ClassNotFoundException). Worse, you'll probably have to do this behind some sort of facade or factory architecture so that you can actually keep running software insulated from all these class loading shenanigans. In short, if the two have different APIs you may actually be better off writing two separate products.
At Runtime
Classes with the same name in the same package follow a first available rule. The first one that is on the classpath is the one that is used.
You can not easily do what you want at runtime without a wrapper program to move the libraries into and out of the system classpath before the Java application is started.
A launcher script/program that dynamically builds the classpath and only includes the version you need of each library and passes it to java -cp is the only way to do what you want at runtime.
At build time
If it is at build time, then just use something like the shade plugin in Maven to build an uberjar with all the required libraries embedded in a single .jar for each of the versions. So 3 versions would be 3 separate uberjar assemblies.
If you can do it at build time, you can use a dependency manager, like Maven.
It provides you with a means to be able to select which versions of which library you use at build-time.
If you need to do this at runtime, you might need to package all libraries in your project. You can use shading (see here) to make sure you don't get import issues, because when importing different versions of libs you end up with similar imports.
Shading can help you make for example:
com.foo.Class in jarv1
com.foo.Class in jarv2
To become
com.foo.v1.Class in jarv1
com.foo.v2.Class in jarv2
This will make sure your code can still use all libs you want.
When using multiple APIs in a single project, the JAR files required for each API are added to the project in addition to other needed libraries such as Apache Commons, logging, etc. that are already used by the project. This sometimes results in a large number of jar files.
When a certain API or library is no longer used, it would be nice to remove the JAR files associated with it. However, there is a risk that another API or library requires it. This would NOT always become apparent during the building of the project. Sometimes, JARs that are missing throw errors only at runtime.
I have the following questions:
What is the best way to deal with this issue? In other words, be able to remove JARs without running the risk of runtime errors later?
I have been told that Maven solves this problem. Does it? Would it work if the external APIs used are not Maven-based? Would I be able to remove JARs without worrying about runtime errors? Do I need to rewrite my entire project to be based on Maven?
How do non-JVM platforms deal with the issue of shared libraries and removing them? Is Java lacking in this area or it is a common issue for all platforms?
Yes I agree Maven could help you in this case. Basically in Maven compile & runtime dependencies for each artifact (jar/war/ear/etc) are declared on pom.xml file. If multiple dependencies depends on same artifacts the latest version is used -- for example:
A-1.0.jar -- depends on --> C-2.0.jar
B-1.0.jar -- depends on --> C-2.1.jar
Only C-2.1.jar is is included in your project.
If a required dependency couldn't be found / taken out, Maven build will automatically fail. So to avoid runtime dependency missing, you can declare a dependency in runtime scope to a particular artifact -- and when you no longer need it you just take it out
There is an old trick I used to use on UNIX many years ago, it might still work for you. First use UNIX "touch" to set the date/time on all your files to the current date/time. Then wait for at least one minute. Then run your application. Then run UNIX "ls -lut" to list all your files, but this time the ones that were not used will have the date/time set in the first step whereas those that were used will have a more recent date/time due to the "u" switch reporting the last used date/time.
I just tried testing an application that uses Apache Camel 2.10.3, and immediately, upon the DefaultCamelContext being instantiated, got the following exception:
java.lang.NoSuchMethodError: org.slf4j.Logger.trace(Ljava/lang/String;Ljava/lang/Object;)V
at org.apache.camel.impl.DefaultPackageScanClassResolver.<init>(DefaultPackageScanClassResolver.java:70)
at org.apache.camel.impl.DefaultCamelContext.<init>(DefaultCamelContext.java:222)
I made sure that slf4j-api-1.6.6 (which is what Camel 2.10.3 ships with) was on the runtime classpath. Next, I suspected that I might have other dependencies that also used SLF4J, but that relied on a different version of it. So I opened Eclipse, and ran a type search for org.slf4j.Logger and sure enough, I see that class listed in 2 distinct JARs: slf4j-api-1.6.6.jar (as expected!), and another 3rd party jar, widget-lib-3.0.jar.
So I opened up widget-lib3.0.jar, and see SLF4J packaged up inside of it like so:
widget-lib-3.0/
com/
<Widget Lib's compiled classes>
org/
slf4j/
spi/
...
impl/
...
<A bunch of SLF4J classes, like LoggerFactory.class, etc.>
There's no way to tell what version of SLF4J it's using here, but I'd be willing to bet that it's a version that's earlier than 1.6.x, which is what Camel 2.10.3 wants.
So my best, slightly-educated guess is that at runtime, the JRE classloaders are finding widget-lib-3.0.jar#org/slf4j/Logger first, loading it, and then they go to load the Camel JARs and their dependencies. Then, when DefaultPackageScanClassResolver calls the SLF4J trace(String,Object) method, it's not finding the 1.6.6 version of SLF4J, rather, it's finding whatever version came with widget-lib-3.0.jar, and that method/overload doesn't exist.
Am I on track of way off base? If I'm off base, what does this mean to you, SO? And if I am on track, then my proposed solution would be to re-JAR widget-lib-3.0.jar without the org/slf4j packages in it (no other, more modern versions exist). My theory being that slf4j-api-1.6.6, which is backwards compatible, would be the only SLF4J version that gets loaded, and would then work for both JARs. Any thoughts? Thanks in advance.
Am I on track of way off base?
No. It looks like you are on-track here.
The way to confirm it would be to take the copy of org.sfl4j.Logger in the widget library JAR, and use javah to see if it has the void trace(String, Object) method or not.
Once you have confirmed it, there are a number of solutions:
The cleanest solution would be to get hold of the source code for the widget library, recompile it against the version of sfl4j that you need, and build a new version of the JAR without embedding sfl4j in it. (It is possible that you will need to modify the source of the widget library, but unlikely).
A simpler solution might to make sure that you put the newer (and supposedly backwards compatible) slf4 API JAR ahead of the widget library JAR on the classpath. That way, the old versions of slf4j in the widget JAR will be "shaded" by the newer ones with the extra method that Camel needs.
"There's no way to tell what version of SLF4J it's using here, but I'd
be willing to bet that it's a version that's earlier than 1.6.x, which
is what Camel 2.10.3 wants"...
Why not decompile the class file from the widget-lib-3.0.jar and see if the required method is there or not?
Your approach is the right one. SLF4J 1.x is API-compatible between versions. (Are you using Maven by the way? It's designed to prevent exactly this kind of problem).
What is widget-lib? Is there a version of it that doesn't include its dependencies? If there is, you should use that.
I am experimenting with my first GWT app that uses GIN. I am essentially mimicking the exact same source code example found in their Getting Started tutorial. At the end of that tutorial, the author mentions that you need to supply the the GWT compiler with a classpath pointing to the compiled Java binaries. The example they use for doing this is done using an Ant target.
Although I will have an automated (Ant-driven) build that also emulates this build task, I want to be able to run/debug my GWT apps from inside of Eclipse without having to kick off an Ant build.
So I ask: when running my GWT app inside Eclipse (Dev Mode), how do I configure the GIN library to point to the compiled classes? Thanks in advance!
All you should need to do is add the gin.jar (and dependencies, probably just guice.jar) to your project's classpath.
It should not be required to copy these to WEB-INF/lib/, though depending on your server setup, you may require guice.jar there, but never gin.jar.
I'm looking into a solution that displays the subversion revision number and last modification date in my application (written in GWT, therefore reflection is not available). Encode the revision in subversion keyword doesn't work as it applies only to the current file. Is there a better solution using annotation? (e.g., a separate class that's executed during the compile time, grab the latest revision # on the whole project and inject the revision and last modification date to the source code)
I kn
#SvnRevision("$Id$")
public class Foo {
}
Then your classes are all annotated with their version. You need to make sure the annotation is defined as having runtime retention so it can be queried at runtime.
EDIT
OK, since SVN doesn't have that feature, I'd write a Maven plugin to emulate it. Maven has access to the SCM information for every file so during the build phase you could have it do the same keyword expansion.
Annotations are not really designed for this. It's easiest to do it as part of the build.
Using Ant you can generate a file that contains the version information, include it in your application's JAR, load it as a resource on the server, and serve it out to the browser-side code by RPC. Ant can also do string replacement in files as it copies them, which you can use to include the version number in your application's HTML files (no need for RPC then).
No idea about Maven, but I would be very surprised if it could not do the same kind of thing.
Subversion still has the $Id$ feature, but it needs to be enabled explicitly using the svn:keywords property on the files (set it to 'Id').
See also: http://svnbook.red-bean.com/en/1.5/svn.advanced.props.special.keywords.html
So the idea of Jherico above with #SvnRevision would work.