Strip ScalaSignature annotation from resulting binaries - java

How can you strip java or scala annotations programmatically?
One of the deliverables for my current project is my code, plus whatever dependencies my code relies on, as an uber-jar. We're building an SDK therefore any of the included dependencies need to be renamed as to not interfere with the SDK client's dependencies (meaning if we're using apache commons version X, and they're using version Y, there aren't any conflicts). We used sbt-assembly to rename the packages our code relies on. For example org.apache.* -> com.mycompany.org.apache.*
Our project is largely written in scala and we're using sbt to build. Recently I determined that shading causes problems with the ScalaSignature annotation. This in turn causes build problems when the uber-jar is used in the client's project.
I have determined that removing ScalaSignature resolves the problem. I used ProGuard to do this, however that tool is overkill for what we are trying to achieve. I'm just curious if anyone out there has found a tool that can strip specified annotations. Or even better, a tool that will rename the packages while preserving ScalaSignature in a way that scalac is happy with.

You might want to check JavaAssist library, which clearly allows to get/add annotations dynamically.
Following that, it should be theoretically possible to use AnnotationsAttribute in the following way (haven't tried myself but let us know if the below has worked):
MethodInfo minfo = m.getMethodInfo();
AnnotationsAttribute attr = (AnnotationsAttribute)
minfo.getAttribute(AnnotationsAttribute.invisibleTag);
Annotation[] annotations = attr.getAnnotations();
// ...remove the unnecessary annotation from array...
attr.setAnnotations(annotations);

Related

Does Java classpath different order give "No Method found error"

I have a Scala Akka Application which connects to HBase (currently CDP earlier HDP) deployed on rancher; Never faced any trouble when connecting to HDP hbase; Since recent HDP to CDP change, with the same image we are getting no method found on one of the dependency's class in one of the container, where as another container of same image connects to hbase properly.; even though the jar exists in the same image and classpath also.
one of the noticeable difference is change in the order of classpath.
Does change in the classpath order will effect the jars availability.
Does java libraries/classes would load in different order when they would hit a faster CPU cycle at startup.
What could be the reason for such "no class method found".
It certainly can, if the same class file is present in different classpath entries. For example, if your classpath is: java -cp a.jar:b.jar com.foo.App, and:
a.jar:
pkg/SomeClass.class
b.jar:
pkg/SomeClass.class
Then this can happen - usually because one of the jars on your classpath is an older version than the other, or the same but more complicated: one of the jars of your classpath contains a whole heap of different libraries all squished together and one of those components is an older version.
There are some basic hygiene rules to observe:
Don't squish jars together. If you have 500 deps, put 500 entries on your classpath. We have tools to manage this stuff, use them. Don't make striped jars, uber jars, etc.
Use dependency trackers to check if there are version difference in your dependency chain. If your app depends on, say, 'hibernate' and 'jersey', and they both depend on google's guava libraries, but hibernate imports v26 and jersey imports v29, that's problematic. Be aware of it and ensure that you explicitly decide which version ends up making it. Presumably, you'd want to explicitly pick v29 and perhaps check that hibernate also runs on v29*. If it doesn't, you have bigger problems. They are fixable (with modular classloaders), but not easily.
*) Neither hibernate nor jersey actually depend on guava, I'm just using them as hypothetical examples.
For example, if you use maven, check out the enforcer plugin. (groupId: org.apache.maven.plugins, artifactId: maven-enforcer-plugin).
My bet is that there is another version of the jar somewhere in CDP, and occasionally it is loaded before the version that you ship with your project, causing the error.
So, when your container starts, try logging from which location the conflicting class is loaded. This question might help you: Determine which JAR file a class is from

"Hide" a maven artifact from the eclipse autocomplete

For reasons I don't even want to begin to get into.. I have a maven hierarchy that looks like the one below. In a nutshell, everything requires commonslang3, except one ancient artifact that requires commonslang2.
We have no issues with compile or runtime, the dependencies work as expected. The challenge we are having is at development time.
We'd like to ensure everyone on the team uses the commonslang3 APIs, but occasionally (because of the ancient artifact and Eclipse auto suggest), someone accidentally uses the commonslang2 APIs.
Normally, we would just force the desired version in our POM, but commonslang is a special snowflake. The package signature changed between comonslang2 and commonslang3, which means we would have compile failures if we excluded the older library. E.g.,
org.apache.commons.lang3.StringUtils
org.apache.commons.lang.StringUtils
My question is this, how can I configure maven/Eclipse, to use commonlang2 as needed during compile... but not populate it in the Eclipse class autosuggest list? My desired end state is that someone types 'stringuti' + ctrl + space, and the only option they see is commonslang3. I am aware that each developer can remove individual classes via (Window->Preferences->Java->Appearance->Type Filters) but that is not a viable solution for two reasons: 1) It's a large team with frequently changing resources... 2) I need an entire artifact removed, as in hundreds of classes.
Example Tree:
MyWar
-- MyModuleJar1
-- ...
-- MyModuleJar2
-- LibA
-- commonslang
-- ...
-- LibB
-- commonslang3
-- ...
-- LibC
-- commonslang3
-- ...
-- ...
In Eclipse:
Window->Preferences->Java->Appearance->Type Filters
Add org.apache.commons.lang.*
Because you want to affect auto-complete which is a function of the IDE, you are forced to change the setting in the IDE. You can export the preferences and share them of the rest of the team.
There is not much you can do about it in Eclipse other than type filters #JustinKSU mentioned.
But with Maven you can use Takari to access rules to prevent accidental inclusion of transitive dependencies. Of course this comes with a plethora of caveats with one ironically being that the Eclipse JDT compiler has to be used instead of plain javac.

Using MessagePack with Android

Has someone tried to use MessagePack with an Android app?
Is it possible? I have tried to use the Jar from msgpack-java and received the following Exception:
Caused by: java.lang.ExceptionInInitializerError
at org.msgpack.Packer.pack(Packer.java:532)
at org.msgpack.MessagePack.pack(MessagePack.java:31)
... 15 more
Caused by: java.lang.ExceptionInInitializerError
at org.msgpack.template.TemplateRegistry.<clinit>(TemplateRegistry.java:38)
... 17 more
Caused by: java.lang.VerifyError: org.msgpack.template.BeansFieldEntryReader
at org.msgpack.template.builder.BeansTemplateBuilder.<init (BeansTemplateBuilder.java:42)
at org.msgpack.template.builder.BuilderSelectorRegistry.initForJava(BuilderSelectorRegistry.java:73)
at org.msgpack.template.builder.BuilderSelectorRegistry.<clinit>(BuilderSelectorRegistry.java:38)
... 18 more
The code that I use is very simple
PrintWriter out = new PrintWriter(socket.getOutputStream());
Message msg = new Message();
msg.body = "asdasdasd";
msg.from = "qwe";
msg.to = "ttt";
byte[] bytes = MessagePack.pack(msg);
out.print(bytes);
out.flush();
I have javassist.jar, msgpack-0.5.2.jar, slf4j-api-1.6.2.jar and slf4j-jdk14-1.6.2.jar in my lib directory.
In my server application this code works fine with the same libraries.
(Hopefully) FINAL UPDATE
msgpack : 0.6.8 works on Android without any problems
msgpack-rpc : 0.7.0 works on Android with one caveat.
Specifically, you need to add the following to onCreate for API Level 8 (Android 2.2.1), and possibly lower:
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
due to this bug.
If you want to see a simple example, here's a pair of projects set up for this purpose:
https://github.com/mikkoz/msgpack-android-test-server/tree/master/msgpack-android-test-server
https://github.com/mikkoz/msgpack-android-test-client/tree/master/msgpack-android-test-client
Previous Versions
UPDATE: as of 0.6.7 msgpack should be compatible with Android (there is a small dependency exclusion issue). Check the text below for msgpack-rpc (which also might be adapted in the future).
NOTE: If you're also using msgpack-rpc, you need to do the following steps:
Download the msgpack-rpc source from git://github.com/msgpack/msgpack-rpc.git (specifically, the "java" folder).
Change the main msgpack artifact version to the one you've built.
In org.msgpack.rpc.loop.netty.NettyEventLoop, change the NioClientSocketChannelFactory to OioClientSocketChannelFactory(getWorkerExecutor()).
Build the MessagePack-RPC in the same way as in the case of the main MessagePack JAR (see Step 11 above).
The NettyEventLoop replacement is due to this issue:
http://markmail.org/message/ypa3nrr64kzsyfsa .
Important: I've only tested synchronous communication. Asynchronous might not work.
And here's the reason for msgpack not working with Android prior to 0.6.7:
The reason for the error is that MessagePack uses several java.beans classes that are not included in the Android SDK. You're probably using the MessagePackBeans annotation.
This is a similar problem to the one described here, for which the general solution is outlined here. Unfortunately, in our case it requires a rebuild of msgpack. Here's what I did (you can almost certainly skip Steps 5 and 8, but I haven't tried it that way) :
Download the MessagePack source from https://github.com/msgpack/msgpack-java.git.
Import the MessagePack source as a project in your IDE.
Download the Apache Harmony source for the relevant packages from http://svn.apache.org/repos/asf/harmony/enhanced/java/trunk/classlib/modules/beans/src/main/java .
Copy these packages into your MessagePack project's src/main/java folder:
java.beans
java.beans.beancontext
org.apache.harmony.beans
org.apache.harmony.beans.internal.nls
In your MessagePack project, remove the following classes:
PropertyChangeListener
IndexedPropertyChangeEvent
PropertyChangeEvent
PropertyChangeListenerProxy
PropertyChangeSupport
Rename the java.beans packages to something different, e.g. custom.beans .
Change all java.beans references to the renamed ID, so again e.g. custom.beans. This applies especially to BeansFieldEntryReader (this class is the reason for the original error).
Change the custom.beans references for the five classes you removed in Step 5 back to java.beans.
In the org.apache.harmony.beans.internal.nls.Messages class, comment out the method setLocale, and remove the imports associated with it.
Remove all classes that still have errors, except Encoder. In that class, comment out all references to the classes you've removed. You should now have an error-free project.
Build the MessagePack JAR:
If you're using Maven, change the version in the pom.xml to something unique, run Maven build with the install goal, then add the dependency in your Android project with that version.
If you're not using Maven, you have to run the jar goal for Ant with the included build.xml. Replace the msgpack JAR in your Android project with this one.
If you're publishing your app, remember to include the relevant legal notice for Apache Harmony. It's an Apache License, just like MessagePack.
That should do it. Using your example code, and my own data class, I was successfully able to pack and unpack data.
The entire renaming ritual is due to the fact that the DEX compiler complains about java.* package naming.
There is a critical msgpack bug saying data packed with msgpack will get corrupted on the Dalvik VM. http://jira.msgpack.org/browse/MSGPACK-51
There is an ongoing effort by #TheTerribleSwiftTomato and the MessagePack core team to get MessagePack working on Android, please see the related GitHub issue. The fix mentioned in #TheTerribleSwiftTomato's answer is to be found here.
Update
I've managed to get it at least running on Android by (painstakingly) adding all the necessary javassist Classes which are currently required for the build to succeed. An extra 600KB gain in size, yet at least it seems to work. All in all, it appears to be working to some extent on Android, eventually check out the lesser-known resources about Message Pack such as its User Group and its Wiki for more information.
On a side-note, be sure to use a HTTP Request Library (such as LoopJ's Android Async HTTP or Apache's HttpClient) which can handle binary data.
Last but not least you can ping me if there is interest in this jar which makes MessagePack seemingly work on Android – credits go out of course to #TheTerribleSwiftTomato who supplied the fix above!
I suggest you write this in the main proguard-rules file-
-dontwarn org.msgpack.**
-keep class org.msgpack.** { *; }

Eclipse 3.5+ - Annotation processor: Generated classes cannot be imported

I am using a 3rd party annotation processor for generating meta-data code (.java files) from the annotated classes in my project.
I have successfully configured the processor through Eclipse (Properties -> Java Compiler -> Annotation Processing) and the code generation works fine (code is automatically created and generated). Also, Eclipse successfully auto-completes the generated classes and their fields, without any errors. Let's say that I have a class "some.package.Foo" and that the generated meta-data class is "some.package.Foo_". By the help of auto-completion, I can get the following code in the Eclipse editor, without any errors:
import some.package.Foo_;
...
public class Test {
void test() {
Foo_.someField = null; // try to access a field from the generated class Foo_
}
}
However, as soon as I actually build the project (or just save the file since Build automatically is enabled), I get the error which tells that "some.package.Foo_" cannot be resolved.
It seems like Eclipse is generating and compiling the some.package.Foo_ at the same time, or more likely.
I found two temporary solutions (which are practically hindering the use of the annotation processor in the first place):
Before each build of that generated classes, right click on every generated file go to Properties and uncheck the "Derived" tick. After that, I do the cleanup of the project and the imports are fine - there are no more errors. However, if I do the cleanup one more time, the errors again show up, because the generation of the files causes the "Derived" tick to be checked again (automatically). So this is really annoying and time-consuming.
I also uncheck the "Derived" tick
from all those files, and this time
I uncheck the "Derived" tick from
the source folder and packages which
contain those files. Then I disable
the annotation processor, and then
do the cleanup. There are no more
import errors, even if I do another
cleanup, but there is no benefit of
using the annotation processor,
because if I was to change something
which would update the model, I need
to turn the annotation processor
back on, and repeat this tedious
procedure to turn it off, after it
has generated the new version of
those files.
Is this a bug in Eclipse? If yes, is there a better workaround or quick-fix than the two I have stated above? If not, what should I try to solve the problem?
I also tried rearranging the order of the libraries on the build path and it doesn't help.
I assume that you are generating sources in the last processor round. This is not recommended way and leads exactly to the problem that you had.
Explanation is here: http://code.google.com/p/acris/wiki/CodeGenerationPlatform_Pitfall_Rounds
So the my advise is to generate sources in regular processing rounds and final round should be used just for notification that processing is over or something like that.
Hopefully this helps you.
I have a similar problem, and the only thing I've found is that it's the imports specifically that don't work, but the references in the class itself do work. The workaround I've used is to use the FQCN in all cases where the generated class is needed (except when the generated class is in the same package, since then the import is obviously not needed).
So to use your example, I'd do:
public class Test {
void test() {
some.package.Foo_.someField = null; // try to access a field from the generated class Foo_
}
}
My only guess then is that the eclipse compiler is processing the imports before doing the annotation processing, which imho must be a bug in eclipse.
I know this question is over a year old, so I'd be interested to know if you've found any other way to fix it.
We were experiencing a similar problem and apparently just solved it, so thought of sharing it at SO, in case it helps someone.
We are using:
Eclipse Indigo (Build id: 20120216-1857)
m2e Connector for maven
openJPA for static metamodel class generation
Our problem:
Say, we have a package named com.abc.xyz and an entity class in there named OurEntity. When we build the projects (JPA, EJB, EAR etc. all together with an mvn clean at the beginning) the metamodel classes get generated. And also get appropriately packaged within the PU jar. But when we try to import the generated metamodel class com.abc.xyz.OurEntity_, Eclipse cannot resolve it. OP apparently got past this point:-). Maven build failed, saying it could not resolve that class. Not much help from google except for a few bug reports such as this one: https://bugs.eclipse.org/bugs/show_bug.cgi?id=350378
That bug report said importing the whole package as opposed to the single class helped. So, tried that, but with no benefit. It also said (and so did David Heitzman) that using the fully qualified class name worked for them. That did not work either.
The solution:
Added the PU jar to Eclipse build path for the project that needed to use the metamodel classes. All of a sudden all the red underlines went away (not a surprise). But the fear was there might be two PUs in the same ear. But maven automagically took care of that.
As this rather old question got some attention without pointing to the very probable eclipse bug the OP was specifically asking for, I'd like to complement the above answers with a pointer to the eclipse bug tracker:
Cannot resolve import for generated class IF processing annotations with parameters referencing constants
The workarounds include
doing a wildcard import of the package defining the generated classes (i.e. import some.package.*;)
using the fully qualified name of your generated class, i.e. referring to some.package.Foo in your code and not using an import
switch to a newer Eclipse. This specific eclipse bug is resolved with Eclipse version 4.4 (aka Luna).

Modern equivalent of javadeps?

I am looking for a replacement for javadeps, which I used to use to generate sections of a Makefile to specify which classes depended on which source files.
Unfortunately javadeps itself has not been updated in a while, and cannot parse generic types or static imports.
The closest thing I've found so far is Dependency Finder. It almost does what I need but does not match non-public classes to their source files (as the source filename does not match the class name.) My current project has an interface whose only client is an inner class of a package-private class, so this is a significant problem.
Alternatively if you are not aware of a tool that does this, how do you do incremental compilation in large Java projects using command-line tools? Do you compile a whole package at a time instead?
Notes:
javadeps is not to be confused with jdepend, which is for a very different purpose.
This question is a rewrite of "Tool to infer dependencies for a java project" which seemed to be misunderstood by 2 out of 3 responders.
I use the <depend> task in ant, which is ok, but not 100% trustworthy. Supposedly JavaMake can do this dependency analysis, but it seems to be rarely updated and the download page is only sometimes available.

Categories