javac ignoring #SuppressWarnings("all") - java

I currently have an Enum, which has a constructor that can take null and contains with the following SuppressWarning annotation:
#SuppressWarnings("all")
public enum TheEnum {
...
AN_ENUM_VALUE(null),
...
...
private TheEnum(TheEnum ... args) {
if (args != null){
...
...
}
...
}
I'm currently using MyEclipse workbench 10.6, and it seems to pick up the annotation fine. During a compilation on a dev machine, however, I get warnings related to the 'TheEnum' class.
The strange this is that within the project, there are #SuppressWarnings("unchecked") all over the place, and the compiler manages to pick these up and ignore them just fine.
Because of legacy issues, we have to use JDK 1.5.0_17 to compile, but it looks like it should pick up the "all" suppression:
[root#xxx]:/opt/jdk1.5.0_17/bin# ./javac -X
...
-Xlint:{all,deprecation,unchecked,fallthrough,path,serial,finally,-deprecation,- unchecked,-fallthrough,-path,-serial,-finally}Enable or disable specific warnings
any suggestions as to where I should look to see why 'all' is being ignored?

Don't use it in the first place.
#SuppressWarnings("all")
says, you are doing something wrong. I would cancel a contract ASAP with any developer developing code like this. Warnings are there for a reason, and a blanket ignore says "I don't care if my code actually works" like nothing else. This is the total opposite to writing unit tests somewhat: blanket disabling the compiler checks.
Judging from http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/javac.html#options "all" might be an eclipse extension. Sun/Oracle Java 6 compiler probably only supports the following values: unchecked, path, serial, finally, fallthrough and deprecation.

Related

IntelliJ IDEA #SuppressWarnings for inspection with name of tool

I know I can suppress warnings from IntelliJ IDEA inspections like that:
#SuppressWarnings("CollectionDeclaredAsConcreteClass")
public PropertiesExpander(Properties properties) {
this.properties.putAll(properties);
}
For person from outside it may not be clear for which tool this suppression is needed.
PMD uses prefixes for that:
#SuppressWarnings("PMD.UnusedLocalVariable")
FindBugs uses dedicated annotation:
#SuppressFBWarnings("NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
Is there any way to clearly indicate that this suppression is just for IntelliJ IDEA?
A way of suppressing this for method arguments is to use a javadoc tag instead:
/**
* #noinspection CollectionDeclaredAsConcreteClass
*/
public PropertiesExpander(Properties properties) {
this.properties.putAll(properties);
}
This javadoc tag is very IntelliJ specific so no other tool should have any troubles with it. And you can easily identify it by just recognize it as it is or by adding some comment to it:
/**
* #noinspection IntelliJ CollectionDeclaredAsConcreteClass
*/
public PropertiesExpander(Properties properties) {
this.properties.putAll(properties);
}
And of course you can shorten it up even more:
/** #noinspection CollectionDeclaredAsConcreteClass */
public PropertiesExpander(Properties properties) {
this.properties.putAll(properties);
}
General approach
It is possible to suppress some warnings on a one-by-one basis with special comments instead of the #SuppressWarnings annotation.
Here's an example with lots of warnings:
If you press Alt+Enter on notUsedLocalVariable you can then choose Suppress for statement with comment on the context menu (after going right when choosing Remove variable ...).
On some variables/fields/methods the selected suppression is just Suppress for statement.
And now most (not all) of the warnings has been suppressed if you look in the right-hand field:
As you can see there can be several suppressions on the same comment line.
This can seem to be a bit tedious but I think it's the best you can do.
I found a way how to achieve that with #SuppressWarnings:
#SuppressWarnings("idea: CollectionDeclaredAsConcreteClass")
public PropertiesExpander(Properties properties) {
this.properties.putAll(properties);
}
Note that there must be space after idea:, otherwise it doesn't work.

Order of imports seems to matter for compilation to succeed?

I'm writing a unit test for some Java class in Eclipse. I always let Eclipse handle my imports automatically, and it orders them according to whatever default scheme.
Now, I have the following situation. The unit test builds and runs just fine in Eclipse. However, when I build the tests on the command line (using some Ant targets), javac complains that it can't find one of the classes I've imported. Since I'm testing something with caching, the relevant bits of the test file (Test.java let's say) are:
import com.abc.my.stuff.MyCacheLoader.Operation;
import com.google.common.cache.CacheLoader;
public class Test {
... test code, such as ...
List<MyKey> recordedKeys = dummyLoader.getKeysFor(Operation.LoadAll);
}
// Dummy cache loader which just records the keys
class MyCacheLoader extends CacheLoader<MyKey, MyValue> {
enum Operation { Load, LoadAll }
#Override
public MyValue load(MyKey key) throws Exception { .... whatever .... }
#Override
public MyValue loadAll(Iterable<MyKey> key) throws Exception { .... whatever .... }
public List<MyKey> getKeysFor(Operation op) { ... impl ... }
}
Notice that the import for my local class's inner enum appears before the import for the Guava CacheLoader class, because they are ordered alphabetically. When I try to compile this using Ant on the command line, javac can't find 'CacheLoader'. When I switch the order of the imports, the build succeeds (whereas Eclipse doesn't care either way).
I'm mystified as to what's going on here.
Apparently, the compiler in Eclipse doesn't have the following Sun bug:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6391197
which I found via:
http://unimplemented.blogspot.com/2007/08/my-java-puzzle-does-order-of-import.html
This explains why flipping the order of imports makes no difference to Eclipse, but it does to the Sun compiler. Part of the problem is fixed in Java 8, but my particular manifestation is targeted for a fix in Java 9: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7101822

ProGuard ignoring annotation default values

The Issue
I'm having a problem with ProGuard 4.11 (An application that can optimize, shrink and obfuscate Java code), using the proguard-maven-plugin (github.com/wvengen/proguard-maven-plugin) (Although that shouldn't matter that much, since the error is occurring at runtime and the maven plugin just calls the binary with some arguments as far as I know).
I have an annotation class which is stuctured like this (nothing special):
#Retention(RetentionPolicy.RUNTIME) #Target(ElementType.FIELD)
public #interface SqlValueCache {
String value();
SqlValueCache.Type type() default Type.OBJECT_UPDATE; //Type is just an ordinary enum
}
I also have some fields annotated with that annotation, but I'm skipping the type() parameter because I want to use the default value:
#SqlValueCache("nickname")
protected SqlValueHolder<String> nick;
Now I want to process that annotation at runtime:
SqlValueCache annotation = field.getAnnotation(SqlValueCache.class); //Skipped the validation
annotation.value(); //Works fine, because specified
annotation.type(); //java.lang.annotation.IncompleteAnnotationException, *not* using the default
As stated in the comment above, I get an IncompleteAnnotationException, stating that my annotation declaration is missing the type() value. But that value should be implied by the default Type.OBJECT_UPDATE! So now I'm wondering, why is that happening?
Assumptions
I assume that the default thing is stored in some kind of attribute that I need to specify in -keepattributes, but I haven't been able to figure out if this is true or which one it is.
Reproduction
It does work just as intended when not using ProGuard.
I have also made sure that the problem is in fact the missing implied value - The code runs as intended when using ProGuard, but explicitly specifying the type(), like so:
#SqlValueCache(value = "nickname", type = Type.OBJECT_UPDATE)
protected SqlValueHolder<String> nick;
I am using this method as a temporary workaround, but this isn't the prettiest solution in my opinion. Also, as stated above, I still want to know why this error is happening.
Thanks in advance for reading and investigating my question! :)
Appendix/Meta
Yes, I did search the web for solutions and did also use the StackOverflow searchbox. Using the title of this question and various other search queries, I could only find questions complaining about no annotations being kept at all or asking to keep subclasses of classes annotated, etc. I also searched for the exception, but the only useful result was the JavaDoc (docs.oracle.com/javase/7/docs/api/java/lang/annotation/IncompleteAnnotationException.html) of the exception I am encountering.
I'm sorry about the links, but I apparently need 10 reputation to post more than two, although I really like linking stuff :/
Stacktrace
I have attached the Stacktrace I got in console (class names won't be useful at all in my opinion - I can attach them if really necessary; My classes are also loaded by a custom class loader, but I doubt that that makes any difference):
java.lang.ExceptionInInitializerError
at <class with annotated field>
(...)
Caused by: java.lang.annotation.IncompleteAnnotationException: io.github.xxyy.common.sql.builder.annotation.SqlValueCache missing element type
at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:72) ~[?:1.7.0_51]
at com.sun.proxy.$Proxy18.type(Unknown Source)
at <line accessing type() of SqlValueCache>
(...)
ProGuard config
Here's my ProGuard config, should that be of any help (That's the part I find relevant to this question - Full file here: pastebin.com/u6wt00cj):
-dontskipnonpubliclibraryclassmembers
-target 1.7
-dontshrink
-dontoptimize
-repackageclasses io.github.xxyy.obf.towerdefense.client
-keepattributes SourceFile,LineNumberTable,*Annotations*,LocalVariable*Table
-keep,allowobfuscation class ** {
<fields>;
<methods>;
}
-keep,allowshrinking class **.annotation** {
<fields>;
<methods>;
}
# Also keep - Enumerations. Keep the special static methods that are required in
# enumeration classes.
-keepclassmembers enum ** {
public static **[] values();
public static ** valueOf(java.lang.String);
}
You also need to keep the AnnotationDefault attribute:
-keepattributes AnnotationDefault
You can get the same effect by changing *Annotations* to *Annotation* in your current -keepattributes option, so the wildcards match AnnotationDefault.

AspectJ Load time weaver doesn't detect all classes

I am using Spring's declarative transactions (the #Transactional annotation) in "aspectj" mode. It works in most cases exactly like it should, but for one it doesn't. We can call it Lang (because that's what it's actually called).
I have been able to pinpoint the problem to the load time weaver. By turning on debug and verbose logging in aop.xml, it lists all classes being woven. The problematic class Lang is indeed not mentioned in the logs at all.
Then I put a breakpoint at the top of Lang, causing Eclipse to suspend the thread when the Lang class is loaded. This breakpoint is hit while the LTW weaving other classes! So I am guessing it either tries to weave Lang and fails and doesn't output that, or some other class has a reference that forces it to load Lang before it actually gets a chance to weave it.
I am unsure however how to continue to debug this, since I am not able to reproduce it in smaller scale. Any suggestions on how to go on?
Update: Other clues are also welcome. For example, how does the LTW actually work? There appears to be a lot of magic happening. Are there any options to get even more debug output from the LTW? I currently have:
<weaver options="-XnoInline -Xreweavable -verbose -debug -showWeaveInfo">
I forgot tom mention it before: spring-agent is being used to allow LTW, i.e., the InstrumentationLoadTimeWeaver.
Based on the suggestions of Andy Clement I decided to inspect whether the AspectJ transformer is ever even passed the class. I put a breakpoint in ClassPreProcessorAgent.transform(..), and it seems that the Lang class never even reaches that method, despite it being loaded by the same class loader as other classes (an instance of Jetty's WebAppClassLoader).
I then went on to put a breakpoint in InstrumentationLoadTimeWeaver$FilteringClassFileTransformer.transform(..). Not even that one is hit for Lang. And I believe that method should be invoked for all loaded classes, regardless of what class loader they are using. This is starting to look like:
A problem with my debugging. Possibly Lang is not loaded at the time when Eclipse reports it is
Java bug? Far-fetched, but I suppose it does happen.
Next clue: I turned on -verbose:class and it appears as if Lang is being loaded prematurely - probably before the transformer is added to Instrumentation. Oddly, my Eclipse breakpoint does not catch this loading.
This means that Spring is new suspect. there appears to be some processing in ConfigurationClassPostProcessor that loads classes to inspect them. This could be related to my problem.
These lines in ConfigurationClassBeanDefinitionReader causes the Lang class to be read:
else if (metadata.isAnnotated(Component.class.getName()) ||
metadata.hasAnnotatedMethods(Bean.class.getName())) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
return true;
}
In particular, metadata.hasAnnotatedMethods() calls getDeclaredMethods() on the class, which loads all parameter classes of all methods in that class. I am guessing that this might not be the end of the problem though, because I think the classes are supposed to be unloaded. Could the JVM be caching the class instance for unknowable reasons?
OK, I have solved the problem. Essentially, it is a Spring problem in conjunction with some custom extensions. If anyone comes across something similar, I will try to explain step by step what is happening.
First of all, we have a custom BeanDefintionParser in our project. This class had the following definition:
private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class<?> getBeanClass(Element element) {
try {
return Class.forName(element.getAttribute("class"));
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class " + element.getAttribute("class") + "not found.", e);
}
}
// code to parse XML omitted for brevity
}
Now, the problem occurs after all bean definition have been read and BeanDefinitionRegistryPostProcessor begins to kick in. At this stage, a class called ConfigurationClassPostProcessor starts looking through all bean definitions, to search for bean classes annotated with #Configuration or that have methods with #Bean.
In the process of reading annotations for a bean, it uses the AnnotationMetadata interface. For most regular beans, a subclass called AnnotationMetadataVisitor is used. However, when parsing the bean definitions, if you have overriden the getBeanClass() method to return a class instance, like we had, instead a StandardAnnotationMetadata instance is used. When StandardAnnotationMetadata.hasAnnotatedMethods(..) is invoked, it calls Class.getDeclaredMethods(), which in turn causes the class loader to load all classes used as parameters in that class. Classes loaded this way are not correctly unloaded, and thus never weaved, since this happens before the AspectJ transformer registered.
Now, my problem was that I had a class like so:
public class Something {
private Lang lang;
public void setLang(Lang lang) {
this.lang = lang;
}
}
Then, I had a bean of class Something that was parsed using our custom ControllerBeanDefinitionParser. This triggered the wrong annotation detection procedure, which triggered unexpected class loading, which meant that AspectJ never got a chance to weave Lang.
The solution was to not override getBeanClass(..), but instead override getBeanClassName(..), which according to the documentation is preferable:
private static class ControllerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected String getBeanClassName(Element element) {
return element.getAttribute("class");
}
// code to parse XML omitted for brevity
}
Lesson of the day: Do not override getBeanClass unless you really mean it. Actually, don't try to write your own BeanDefinitionParser unless you know what you're doing.
Fin.
If your class is not mentioned in the -verbose/-debug output, that suggests to me it is not being loaded by the loader you think it is. Can you be 100% sure that 'Lang' isn't on the classpath of a classloader higher in the hierarchy? Which classloader is loading Lang at the point in time when you trigger your breakpoint?
Also, you don't mention AspectJ version - if you are on 1.6.7 that had issues with ltw for anything but a trivial aop.xml. You should be on 1.6.8 or 1.6.9.
How does ltw actually work?
Put simply, an AspectJ weaver is created for each classloader that may want to weave code. AspectJ is asked if it wants to modify the bytes for a class before it is defined to the VM. AspectJ looks at any aop.xml files it can 'see' (as resources) through the classloader in question and uses them to configure itself. Once configured it weaves the aspects as specified, taking into account all include/exclude clauses.
Andy Clement
AspectJ Project Lead
Option 1) Aspect J is open source. Crack it open and see what is going on.
Option 2) Rename your class to Bang, see if it starts working
I would not be surprised if there is hard coding to skip "lang' in there, though I can't say why.
Edit -
Seeing code like this in the source
if (superclassnameIndex > 0) { // May be zero -> class is java.lang.Object
superclassname = cpool.getConstantString(superclassnameIndex, Constants.CONSTANT_Class);
superclassname = Utility.compactClassName(superclassname, false);
} else {
superclassname = "java.lang.Object";
}
Looks like they are trying to skip weaving of java.lang.stuff.... don't see anything for just "lang" but it may be there (or a bug)

Is there a way to ignore a single FindBugs warning?

With PMD, if you want to ignore a specific warning, you can use // NOPMD to have that line be ignored.
Is there something similar for FindBugs?
The FindBugs initial approach involves XML configuration files aka filters. This is really less convenient than the PMD solution but FindBugs works on bytecode, not on the source code, so comments are obviously not an option. Example:
<Match>
<Class name="com.mycompany.Foo" />
<Method name="bar" />
<Bug pattern="DLS_DEAD_STORE_OF_CLASS_LITERAL" />
</Match>
However, to solve this issue, FindBugs later introduced another solution based on annotations (see SuppressFBWarnings) that you can use at the class or at the method level (more convenient than XML in my opinion). Example (maybe not the best one but, well, it's just an example):
#edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
value="HE_EQUALS_USE_HASHCODE",
justification="I know what I'm doing")
Note that since FindBugs 3.0.0 SuppressWarnings has been deprecated in favor of #SuppressFBWarnings because of the name clash with Java's SuppressWarnings.
As others Mentioned, you can use the #SuppressFBWarnings Annotation.
If you don't want or can't add another Dependency to your code, you can add the Annotation to your Code yourself, Findbugs dosn't care in which Package the Annotation is.
#Retention(RetentionPolicy.CLASS)
public #interface SuppressFBWarnings {
/**
* The set of FindBugs warnings that are to be suppressed in
* annotated element. The value can be a bug category, kind or pattern.
*
*/
String[] value() default {};
/**
* Optional documentation of the reason why the warning is suppressed
*/
String justification() default "";
}
Source: https://sourceforge.net/p/findbugs/feature-requests/298/#5e88
Here is a more complete example of an XML filter (the example above by itself will not work since it just shows a snippet and is missing the <FindBugsFilter> begin and end tags):
<FindBugsFilter>
<Match>
<Class name="com.mycompany.foo" />
<Method name="bar" />
<Bug pattern="NP_BOOLEAN_RETURN_NULL" />
</Match>
</FindBugsFilter>
If you are using the Android Studio FindBugs plugin, browse to your XML filter file using File->Other Settings->Default Settings->Other Settings->FindBugs-IDEA->Filter->Exclude filter files->Add.
Update Gradle
dependencies {
compile group: 'findbugs', name: 'findbugs', version: '1.0.0'
}
Locate the FindBugs Report
file:///Users/your_user/IdeaProjects/projectname/build/reports/findbugs/main.html
Find the specific message
Import the correct version of the annotation
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
Add the annotation directly above the offending code
#SuppressWarnings("OUT_OF_RANGE_ARRAY_INDEX")
See here for more info: findbugs Spring Annotation
At the time of writing this (May 2018), FindBugs seems to have been replaced by SpotBugs. Using the SuppressFBWarnings annotation requires your code to be compiled with Java 8 or later and introduces a compile time dependency on spotbugs-annotations.jar.
Using a filter file to filter SpotBugs rules has no such issues. The documentation is here.
While other answers on here are valid, they're not a full recipe for solving this.
In the spirit of completeness:
You need to have the findbugs annotations in your pom file - they're only compile time, so you can use the provided scope:
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>findbugs-annotations</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
This allows the use of #SuppressFBWarnings there is another dependency which provides #SuppressWarnings. However, the above is clearer.
Then you add the annotation above your method:
E.g.
#SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
justification = "Scanning generated code of try-with-resources")
#Override
public String get() {
try (InputStream resourceStream = owningType.getClassLoader().getResourceAsStream(resourcePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream, UTF_8))) { ... }
This includes both the name of the bug and also a reason why you're disabling the scan for it.
Finally you need to re-run findbugs to clear the error.
I'm going to leave this one here: https://stackoverflow.com/a/14509697/1356953
Please note that this works with java.lang.SuppressWarningsso no need to use a separate annotation.
#SuppressWarnings on a field only suppresses findbugs warnings
reported for that field declaration, not every warning associated with
that field.
For example, this suppresses the "Field only ever set to null"
warning:
#SuppressWarnings("UWF_NULL_FIELD") String s = null; I think the best
you can do is isolate the code with the warning into the smallest
method you can, then suppress the warning on the whole method.

Categories