I want to null-check a List and assign it to a final class property:
private final ExcelDocument xls;
private final List<ValidationError> validationErrors;
public DefaultExaminationParser(ExcelDocument xls, List<ValidationError> validationErrors)
{
this.xls = xls;
this.validationErrors = validationErrors == null ? new ArrayList<>() : validationErrors;
}
The import is: import java.util.List; and import java.util.ArrayList;
The compiler in eclipse and maven is Java 1.8
My IDE (eclipse) does not show any problems with this line, but when I run a maven build it fails with following error:
DefaultExaminationParser.java:[84,51] error: incompatible types: List<? extends Object> cannot be converted to List<ValidationError>
The ValidationError is defined as:
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class ValidationError implements Serializable, DetailInfo {
[...]
}
I don't see any errors here. I can also run a unit test for this class without problems.
The problem was, that the compiler level for maven was set to 1.7 in one of the several parent pom files.
Used following snippet to set it explicitly to 1.8:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>utf8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
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. :)
This compiles find using Eclipse:
abstract class CollectionView implements Collection<Object> {
...
public Object[] toArray(Object[] o) {
if (fast) {
return get(map).toArray(o);
} else {
synchronized (map) {
return get(map).toArray(o);
}
}
}
...
}
class KeySet extends CollectionView implements Set<Object> {
protected Collection<Object> get(Map<Object, Object> map) {
return map.keySet();
}
protected Object iteratorNext(Map.Entry entry) {
return entry.getKey();
}
}
but it fails to compile when using Ant:
error: KeySet is not abstract and does not override
abstract method toArray(T[]) in Set
I can see why the code would compile using Eclipse: KeySet already inherits the implementation of the toArray(T[]) method from CollectionView.
But why does it fail when I compile using Ant?
<javac srcdir="src" destdir="bin" debug="on">
<compilerarg value="-Xlint:unchecked"/>
<compilerarg value="-Xlint:deprecation"/>
</javac>
First we should note the exact signature of the method expected to be implemented is:
<T> T[] toArray(T[] a);
And both javac and Eclipse do warn you about this 'Type safety' issue. And if you change the signature to be the expected one, javac is happy.
If you put an #Override to the method toArray, even with the signature which use raw Object type, both Eclipse and javac correctly see it as an override of the method declared by Collection. So this issue is not there.
The inconsistency, and I think the bug of javac, is that is any subclass implementation, javac doesn't recognize the super method Object[] toArray(Object[] o) to implement <T> T[] toArray(T[] a). If it did for the abstract class, i should also do it for every subclass.
It is not the first time javac has a bug about this. See this thread for instance. I have searched the Oracle bug database, I found nothing reported about what you have found.
Then there are a work around: in the abstrcat class, use the expected signature; Or do the override 'manually` in the subclasss:
public Object[] toArray(Object[] o) {
return super.toArray(o);
}
There are several cases where eclipse compiles fine and javac doesn't. If you do not mind, there are three ways that I know to build using the eclipse compiler.
Package eclipse pre-compiled classes (hacky, NOT recommended)
Use the eclipse compiler adapter with Ant. When you specify the property build.compiler, all javac tasks henceforth will be affected on your Ant build. You can set it to "org.eclipse.jdt.core.JDTCompilerAdapter". Note that you will have to include this class (and classes it depends on) in your ant build classpath. The most straightforward way is to add the necessary jars to the lib folder of your Ant installation
When building with maven configure this
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerId>eclipse</compilerId>
<compilerVersion>1.6</compilerVersion>
<source>1.6</source>
<target>1.6</target>
<optimize>true</optimize>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-eclipse</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</plugin>
in the plugins section of the build section of your pom.xml
I had to discover I have Java code in my project, which compiles and runs fine in Eclipse, but throws a compilation error in javac.
A self-contained snippet:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Integer> setOfInts = new HashSet<Integer>();
Set<Object> setOfObjects = covariantSet(setOfInts);
}
public static <S, T extends S> Set<S> covariantSet(Set<T> set) {
return new HashSet<S>(set);
}
}
Compilation in javac returns:
Main.java:10: incompatible types
found : java.util.Set<java.lang.Integer>
required: java.util.Set<java.lang.Object>
Set<Object> setOfObjects = covariantSet(setOfInts);
^
This error now prevents building the project in Maven. As the Eclipse compiler is built to be more tolerant, I now have to assume the definition and usage of snippets as above static method is no valid Java?
It seems that Sun's 1.6 JDK can't infer the correct type. The following seems to work on my machine:
Set<Object> setOfObjects = Main.<Object, Integer>covariantSet(setOfInts);
Note that you must invoke the static method prefixed with the class name
You are right. This problem indeed exists. Eclipse does not use javac. It uses its own compiler.
Actually javac is "right". Generics are erasures. Type S is not included into your byte code, so jvm does not have enough information about the return type at runtime. To solve the problem change the method prototype as following:
public static <S, T extends S> Set<S> covariantSet(Set<T> set, Class<S> returnType)
Now the return type is passed to the method at runtime and compiler should not complain.
In your Maven build skript you have set the compiler version.
In Ant it lookes like this:
<property name="source.version" value="1.5" />
search for 1.3 or 1.4, or compile to find that value in the maven skripts
With value 1.5 the compiler will accept the generics (see your error messages)
I know it's old question, but I want to mention, the function could be written as:
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Integer> setOfInts = new HashSet<Integer>();
Set<Object> setOfObjects = covariantSet(setOfInts);
}
public static <S> Set<S> covariantSet(Set<? extends S> set) {
return new HashSet<S>(set);
}
}
It's a little bit cleaner and you can use the function exactly how you intented to(with implicit generic typing).
Add the next plugin to your pom.xml:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
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"
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.