Update for the impatient: it's simple, use package.- for sub-package scanning instead of package.*, as-per martoe's answer below!
I cannot seem to get onlyAnalyze working for my multi-module project: regardless of what package (or pattern) I set, maven-findbugs-plugin doesn't evaluate sub-packages as I'd expect from passing it packagename.*.
To prove either myself or the plugin at fault (though I always assume it's the former!), I setup a small Maven project with the following structure:
pom.xml
src/
main/java/acme/App.java
main/java/acme/moo/App.java
main/java/no_detect/App.java
which is very simple!
The POM has the following findbugs configuration:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<phase>verify</phase>
<goals><goal>findbugs</goal><goal>check</goal></goals>
</execution>
</executions>
<configuration>
<debug>true</debug>
<effort>Max</effort>
<threshold>Low</threshold>
<onlyAnalyze>acme.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
and every App.java has the following code with two obvious violations:
package acme;
import java.io.Serializable;
public class App implements Serializable
{
private static final class NotSer {
private String meh = "meh";
}
private static final NotSer ns = new NotSer();// Violation: not serializable field
public static void main( String[] args )
{
ns.meh = "hehehe";// Vilation: unused
System.out.println( "Hello World!" );
}
}
Note that no_detect.App has the same content as above, but my expectation is that it wouldn't be evaluated by findbugs because I have the "onlyAnalyze" option set to acme.* which I assume would evaluate acme.App and acme.moo.App and nothing else.
I now execute a mvn clean install to clean, build, test, run findbugs, package, install, which produces the following findbugs report (snipped for brevity) and results in a build failure which is expected because acme.App and acme.moo.App:
<BugInstance category='BAD_PRACTICE' type='SE_NO_SERIALVERSIONID' instanceOccurrenceMax='0'>
<ShortMessage>Class is Serializable, but doesn't define serialVersionUID</ShortMessage>
<LongMessage>acme.App is Serializable; consider declaring a serialVersionUID</LongMessage>
<Details>
<p> This field is never read. Consider removing it from the class.</p>
</Details>
<BugPattern category='BAD_PRACTICE' abbrev='SnVI' type='SE_NO_SERIALVERSIONID'><ShortDescription>Class is Serializable, but doesn't define serialVersionUID</ShortDescription><Details>
<BugCode abbrev='UrF'><Description>Unread field</Description></BugCode><BugCode abbrev='SnVI'><Description>Serializable class with no Version ID</Description></BugCode>
To summarise: only acme.App is analysed, acme.moo.App isn't (bad) and neither is no_detect.App (good).
I tried with two wildcards in the onlyAnalyze option but that produces a successful build but with a findbugs error (Dangling meta character '*' etc).
I tried with onlyAnalyze set to acme.*,acme.moo.* which analyzes all the expected classes (acme.App and acme.moo.App) which means it "works" but not as I expect; i.e. I have to explicitly declare all parent-packages for the classes I want to analyze: that could get large and difficult to maintain on a multi-module project!
Do I have to define every package I want analyzed, or can I declare a wildcard/regex pattern that will do what I want?
I'd rather not use the inclusion/exclusion XML because that requires far more setup and reasoning that I don't currently have time for...
To cite the Findbugs manual: "Replace .* with .- to also analyze all subpackages"
Related
One of the problems I've encountered while using DTOs is that I often find myself shipping (accidentally) entities along with DTOs. To mitigate this problem, I created another Maven project with an annotation (#ValidDTO) and its processor that finds if a DTO annotated with #ValidDTO has #Entity annotated fields.
This is my annotation.
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.TYPE)
public #interface ValidDTO {}
And, this is my processor.
#SupportedAnnotationTypes("com.aj.annotations.ValidDTO")
#SupportedSourceVersion(SourceVersion.RELEASE_11)
public class ValidDTOProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> set,
RoundEnvironment roundEnv) {
List<Entity> entityFields = roundEnv.getElementsAnnotatedWith(ValidDTO.class)
.stream()
.filter(element -> element.getKind()==ElementKind.CLASS || element.getKind()==ElementKind.INTERFACE)
.map(Element::getEnclosedElements)
.flatMap(List::stream)
.filter(element -> element.getKind()==ElementKind.FIELD)
.map(element -> element.getAnnotation(Entity.class))
.collect(Collectors.toList());
if (!entityFields.isEmpty()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Types annotated with ValidDTO " +
"cannot have member variables that are #Entity annotated");
}
return true;
}
}
This is how my POM.xml looks for the Maven project with the annotation and its processor
<groupId>com.aj</groupId>
<artifactId>aj-annotations</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<generatedSourcesDirectory>${project.build.directory}/generated-sources/
</generatedSourcesDirectory>
<proc>none</proc>
<annotationProcessors>
<annotationProcessor>
com.aj.annotations.processors.ValidDTOProcessor
</annotationProcessor>
</annotationProcessors>
<debug>true</debug>
</configuration>
</plugin>
</plugins>
</build>
So, I installed this package as a dependency in another projected and annotated a DTO with it. I purposefully added couple of entities as member variables to see the error.
#ValidDTO
public class FacilityDTO {
private User user;
private List<User> users;
}
where,
#Entity
#Table("User")
public class User {}
is an entity.
Now, my custom annotation works perfectly good when I run mvn clean install or build project. I can see the expected "Types annotated with ValidDTO cannot have member variables that are #Entity annotated" in the terminal.
However, I do not see the error in the editor of the IDE. I've tried both Intellij and Eclipse and I do not see any red squiggly line underneath the annotation telling me that the DTO is invalid.
The closest expected desired behavior I can reference is a compile error when using #FunctionalInterface on an interface that has more than one abstract method.
I just need help configuring my IDE. Any help is appreciated!
In IntelliJ you can create custom inspections. These can be used to alert you to the presence of custom search patterns in your code (https://www.jetbrains.com/help/idea/creating-custom-inspections.html).
For your case:
Go to settings -> Editor -> Inspections. Activate "Structural search inspection" and add a "Search Template":
(UPDATE 06/2020: "Structural search" is not under "General" anymore but is now a separate topic)
Add following structural search:
You may change "Severity" to "Error" to get the red squiggly lines. :)
I have a 3rd party class which is added to build time initialization by Quarkus, but it requires run time initialization due to static thread usage. When adding it to run time initialization native build then complains about it being in both.
Example project which re-produces this: https://github.com/hshorter/quarkus-avro-decode-example
With "--initialize-at-run-time=org.apache.avro.specific.SpecificDatumReader":
Error: Classes that should be initialized at run time got initialized
during image building: org.apache.avro.specific.SpecificDatumReader
the class was requested to be initialized at build time (from the
command line). To see why
org.apache.avro.specific.SpecificDatumReader got initialized use
-H:+TraceClassInitialization
Without "--initialize-at-run-time=org.apache.avro.specific.SpecificDatumReader":
Error: Detected a started Thread in the image heap. Threads running in
the image generator are no longer running at image run time. To see
how this object got instantiated use -H:+TraceClassInitialization. The
object was probably created by a class initializer and is reachable
from a static field. You can request class initialization at image run
time by using the option --initialize-at-build-time=. Or
you can write your own initialization methods and call them explicitly
from your main entry point. Detailed message: Trace: object
org.apache.avro.specific.SpecificDatumReader
Any help much appreciated.
We just fought through this, in the generated code there's a static initializer like this:
private static BinaryMessageEncoder ENCODER = new BinaryMessageEncoder(MODEL$, SCHEMA$);
private static BinaryMessageDecoder DECODER = new BinaryMessageDecoder(MODEL$, SCHEMA$);
We modified the Velocity templates in the Avro code generation to:
Add the #io.quarkus.runtime.annotations.RegisterForReflection annotation
initialize the statics in the constructor using lazy initialization.
Remove those classes from the runtime class init.
The downside is that you have to maintain your custom codegen templates. It's relatively easy though, here's the maven config to automate your codegen:
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>${avro.version}</version>
<executions>
<execution>
<id>schemas</id>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<templateDirectory>${project.basedir}/src/main/resources/avro/templates/</templateDirectory>
</configuration>
</execution>
</executions>
</plugin>
you can find the base templates at https://github.com/apache/avro/tree/master/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic
Sorry, I'm late to the party but the issue you have here is that while you asked SpecificDatumReader to be initialized at runtime, another class initialized at build time requires SpecificDatumReader to be initialized.
So basically, you have some detective work to do to determine why this class was initialized and probably mark the class initializing that one as runtime initialized too.
Note that sometimes it can be a bit hairy.
I've got the following unit test class, which referenced some test data files as classpath resources:
package com.altanqa.project.parser;
public class ParserTest {
private static final URL VALID_DIR = ParserTest.class.getResource("testData-VALID");
private static final URL INVALID_DIR = ParserTest.class.getResource("testData-INVALID");
private static final URL MISSING_DIR = ParserTest.class.getResource("testData-MISSING");
// ...
}
The directory structure referenced here looks like this:
src/test/java/com/altanqa/project/parser
ParserTest.java
src/test/resources/com/altanqa/project/parser
testData-VALID
data
test-data.txt
testData-INVALID
data
test-data.txt
testData-MISSING
data
The Parser under test takes a directory, and knows to look in it for data/testData.txt. The test class here runs the parser, giving it valid data, invalid data, and missing data, to check that the parser deals with each situation correctly. Valid and invalid data works fine, but the MISSING URL is null. If I put a different file (say, a.txt) in test-data-MISSING, the URL is properly initialised, but without any files in the directory, it is null. Is this a peculiarity of the class loader? Is there a way around it, i.e. to have the MISSING_DIR URL initialise correctly, even with nothing in its subdirectory?
Thank you!
Credit goes to Boris for highlighting that this is a Maven issue, rather than anything with the Java. The maven-resources-plugin needs to be configured to include empty directories when copying resources. In the pom:
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
...
<configuration>
<includeEmptyDirs>true</includeEmptyDirs>
</configuration>
...
</plugin>
</plugins>
</build>
I am using
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
</plugin>
and
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
to test my project.
I have a very simple test method
#Test
public void test() throws IOException {
Assume.assumeTrue("I am broken, so I am ignored", false);
// some tests
}
When I build the project with maven, I get the following code snippet in the surefire xml file
<testcase name="test" classname="com.stackoverflower.experiment.Test" time="0">
<skipped/>
</testcase>
I do not think this is the correct behavior of surefire, since assumption failures are treated the same as ignored, it should follow the same logging standard as ignored. This JIRA clearly says the reason should be logged.
What do you think? Is there a workaround to get that message into the xml file?
Thanks
Update 1
The reason I cannot use #Ignore is because I do not want to hard code a test as ignored. I want it to be skipped based on some precondition that cannot be determined at coding time.
The Assume methods mostly used in JUnit Theories when combinations of DataPoints are not valid for a given Theory. You should be using the Assert class, not Assume. Here is your example test, re-written to use the correct class:
#Test
public void test() throws IOException {
Assert.assertTrue("I am broken", false);
// some tests
}
It is meant for false assumptions to be ignored, as mentioned in the Javadoc for Assume
I have a java project with tests written in groovy.
I use TestNG as unit testing framework.
I also have several tests written in java.
After maven test-compile phase all tests (both groovy and java) are compiled and placed in the similar folder inside target/test-classes/.
When I want to run tests with maven, only java tests are run.
When I tried to run groovy test from the IDE (IntelliJ IDEA), it runs perfectly.
I decompiled groovy test and here is what I have:
package mypackage.core;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.testng.annotations.Test;
#Test
public class Sample
implements GroovyObject
{
public Sample()
{
Sample this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
this.metaClass = $getStaticMetaClass();
MetaClass tmp20_17 = this.metaClass;
this.metaClass = ((MetaClass)ScriptBytecodeAdapter.castToType(tmp20_17, $get$$class$groovy$lang$MetaClass()));
tmp20_17;
while (true)
return;
}
#Test
public void testSomething()
{
CallSite[] arrayOfCallSite = $getCallSiteArray(); Registry registry = arrayOfCallSite[0].callConstructor($get$$class$mypackage$core$internal$Registry());
arrayOfCallSite[1].call(registry, null); for (return; ; return);
}
static
{
tmp10_7 = new Long(0L);
__timeStamp__239_neverHappen1314379332415 = (Long)tmp10_7;
tmp10_7;
tmp28_25 = new Long(1314379332415L);
__timeStamp = (Long)tmp28_25;
tmp28_25;
Class tmp48_45 = ((Class)ScriptBytecodeAdapter.castToType($get$$class$mypackage$core$Sample(), $get$$class$java$lang$Class()));
$ownClass = (Class)tmp48_45;
tmp48_45;
return;
while (true)
return;
}
}
Has anyone met similar issue? What can be wrong here?
Can it be connected with the fact that class Sample implements GroovyObject? Can it be connected with bad surefire version?
Thanks!
UPD:
Groovy-related settings in pom.xml:
<dependency>
<groupId>org.codehaus.groovy.maven.runtime</groupId>
<artifactId>gmaven-runtime-1.6</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
...
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
Groovy tests are placed in mymodule/src/test/groovy/.., java tests are placed in mymodule/src/test/java/...
After test-compile phase they both are in mymodule/target/test-classes/...
I don't have special section for surefire in my pom.xml, but from looking at local repository .m2 I can say that surefire plugin of version 2.4.3 is being used.
Test classes must end with "Test" in order to be selected by maven test phase. Just rename the class to SampleTest.