The new version of Jython 2.7.0 standalone weighs in at 36155 KB; the previous released version 2.5.3 is 14005 KB.
Is there any way to safely remove functionality from the .jar file, if certain Python packages are not needed? I want to use the updated Jython release but the file size is an issue now, much more so than 2.5.3.
From the Jython developer mailing list:
Yes, sorry about that. We are focused on correctness first, then we
will go and trim down in subsequent 2.7.x releases, where x > 0. A
huge chunk of that additional bloat is supporting unicodedata and the
corresponding ICU4J jar we now use.
It should be a straightforward matter of programming to remove this
ICU4J dependency. Possibly this and other optimizations, especially
startup time, would be suitable for a Google Summer of Code student,
given that we are planning to participate this summer.
the reason it is bigger is that in the new version the compiled class files are included in the Lib folder.
see http://bugs.jython.org/issue1403 for a discussion
In the last entry there is a post where the speedup is measured with all class files removed from the Lib and included. So I conclude that it should be possible to remove the class files from the jar and zip it up again for a smaller package.
Related
In the coming version of Apache Netbeans, there's a new feature that looks impressive but I don't understand what it's all about.
https://github.com/apache/incubator-netbeans/pull/918
What is an expanded JDK? How can it be useful?
Expanded must be a synonym for exploded. This is hinted by the fact that this pull request is about using freshly compiled JDKs.
So, what is an exploded JDK then? This is explained at https://github.com/openjdk/jdk/blob/master/doc/building.md#running-make:
[An exploded JDK] is a minimal (or roughly minimal) set of compiled
output needed for a developer to
actually execute the newly built JDK. The idea is that in an
incremental development fashion, when doing a normal make, you should
only spend time recompiling what's changed (making it purely
incremental) and only do the work that's needed to actually run and
test your code.
The easy guess is that the terms expanded and exploded are used because, in this case, the modules are still available as a raw set of folders and class files instead of neatly compressed unique files. This last stage of neat packaging is a waste of time when you continually modify the JDK itself. So, it's skipped over while testing the JDK.
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 currently refactoring a large Java application. I have split up one of the central (Eclipse) projects into about 30 individual "components", however they are still heavily inter-dependent. In order to get a better idea of what depends on what I am looking for some way to graph the compile time dependencies.
All tools I have found so far are capable of graphing package or class dependencies or the dependencies between Eclipse plugins, however what I have in mind should just take a look at the classpath settings for each Eclipse project and build a coarser grained graph from that.
Later I will then go deeper, however right now this would just mean I would not be able to see the forest for all of the trees.
Check out JBoss Tattletale. It might not do all you ask but it's worth checking out. It's still relatively new though.
The tool will provide you with reports that can help you
Identify dependencies between JAR files
Find missing classes from the classpath
Spot if a class is located in multiple JAR files
Spot if the same JAR file is located in multiple locations
With a list of what each JAR file requires and provides
Verify the SerialVersionUID of a class
Find similar JAR files that have different version numbers
Find JAR files without a version number
Locate a class in a JAR file
Get the OSGi status of your project
Remove black listed API usage
Structure101 is capable of visualizing class and method JAR level dependencies in Jboss 5.
See the screenshot below or view it larger.
One tool that I believe would do what you want is Understand. It's not free, but you can download a free trial edition before investing any money into it.
Take a look at Dependency Finder
I am not sure if there is a(n Eclipse) classpath analysis tool.
May be Understand mentioned by MattK can help.
The closest I would pick amongst all the static code analysis tool referenced here would be JarAnalyzer (no graph though), able to detect "Physical dependencies" amongst jars.
Sounds like a use case for Degraph. It analyzes a bunch of class files and jar's, and visualizes the dependencies.
What makes it suitable for your usecase (I think) is the possibility to define arbitrary groups of classes to be bundled together. So you can reproduce your jar structure, seeing dependencies, especially cyclic dependencies.
You can unfold the groups to see their contained classes or collapse them to simplify the view.
For a quick impression what is possible, take a look at the Degraph Examples.
Example for Log4j:
JDeps is already included in the JDK, and shows JAR dependencies. For example:
jdeps -R -cp "my\jar\dir\*;my\other\jar\dir\*" my\classes\dir
Check out Class Dependency Analyzer (CDA): http://www.dependency-analyzer.org/
I have found it very useful for tidying up jars.
for the record (and for improving this knowledge base), I found Shrimp very helpful:
http://www.thechiselgroup.org/shrimp
Also, for easy dependency-checking, Byecycle is worth a try, but seems not to be updated anymore:
Byecycle
Both tools also offer Eclipse integration.