Mapstruct AnnotationProcessor with IntelliJ and Gradle - java

I am trying to get the Mapstruct annotation processor to work in IntelliJ in a Gradle project.
Ideally, I would expect for all configuration to be in the gradle-file and that anyone could just import the project into IntelliJ and get a complete setup without having to set any preferences manually.
But I am okay with compromises on that.
I am using IntelliJ 2018.3 and Gradle 5.0 with Java 11 (i.e. the latest and greatest). The Mapstruct version is 1.2.0.FINAL.
What I have done:
Configured the Mapstruct annotation processor in my build.gradle:
compile "org.mapstruct:mapstruct-jdk8:${mapstruct_version}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstruct_version}"
Selected "Delegate IDE build/run actions to Gradle" in the Preferences under "Build, Execution, Deployment -> Build Tools -> Gradle -> Runner"
In the directory build/classes/java/main/com/myapp/mypackage/mapper/ I see a MyMapperImpl.class and a MyMapperImpl.java, so code generation seems to work.
Now I would expect that when I select my annotated abstract MyMapper class and press ctrlH, that the generated MyMapperImpl appears in the hierarchy view.
If I manually mark build/classes/java/main/ as a "generated sources" directory (which I really don't want to have to do, see above), the class still does not appear in the hierarchy. But the source code is marked with a lot of errors, as no classes from my project are found, apparently.
Needless to say: I can flawlessly run tests that use the mapper, both from IntelliJ and the command line.

Use this, my team is also using mapstruct and we use it in our
build.gradle, you will need to bring the idea plugin for gradle as well
def generatedSources = "$buildDir/generated"
def generatedOutputDir = file("$generatedSources")
/*
create generated .java files in different folder than classes
In IntelliJ 2016.3.x: Enable Annotation Processing, then set generated sources,
relative to module output dir, at path '../../generated'
*/
compileJava {
doFirst {
generatedOutputDir.exists() || generatedOutputDir.mkdirs()
options.compilerArgs = [
'-s', "${generatedSources}"
]
}
}
idea {
module {
downloadSources = true
// tell intellij where to find generated sources
sourceDirs += generatedOutputDir
}
}
You will able to run your code even without Gradle runner with this workaround

Related

Recreation of jpa metamodels

I want to use JPA meta-models in my project. I added required dependency to my project, added generation to JavaCompile task and meta-models are successfully generated. If I want to run the code again, It doesn't compile. It fails with:
Error:java: Problem with Filer: Attempt to recreate a file for type project.models.AdministrationUser_
for every single meta-model. I am running it in Idea as spring boot run. If I use gradle task boot run than it will run just fine, no problem, but I need Idea run, because I need to set active profiles. It also shows, that problem is probably not in code but somewhere in run task configuration but I have no idea what to change and I tried to change several things but I'm just firing blanks.
I'm using Gradle 5.4.1., Idea 2019.2 and Java 11.
Here are important parts of my build.gradle file:
dependencies {
annotationProcessor("javax.xml.bind:jaxb-api")
annotationProcessor("org.hibernate:hibernate-jpamodelgen")
}
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Something similar was already asked here but one answer suggest to delete hibernate-jpamodelgen what (if I understand it correctly) seems like absurd solution - because it wont work if you delete it. Other answer suggest using some maven plugin, so not an option for me either.
I'm stuck on this problem for a longer time, have no one to talk to about it and I'm completely out of ideas so I'm pretty desperate and any help will be much appreciated.
First, make modifications to your build.gradle.
You point to the wrong directory in the sourceSets section. Fix it:
sourceSets {
generated {
java {
srcDirs = ['src/generated']
}
}
}
Then make further improvements:
With Gradle 5.2 and Intellij 2019.1 annotationProcessors is the only piece of configuration you need. See https://github.com/tbroyer/gradle-apt-plugin/blob/v0.21/README.md
Get rid of
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Lastly, consider delegating your build and test tasks to Gradle (this is now the default). Go to File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using Gradle

Gradle + Eclipse : use class from existing project in a new project

I know there are a lot of questions that seem similar. I have also spent a few hours getting to grips with Gradle multiprojects. But I still don't understand what the best course of action is here. Incidentally I am using Groovy as my coding language, but explanations referencing Java would be just as good.
I have developed an Eclipse Gradle project, "ProjectA", which in particular has a class, IndexManager, which is responsible for creating and opening and querying Lucene indices.
Now I am developing a new Eclipse Gradle project, "ProjectB", which would like to use the IndexManager class from ProjectA.
This doesn't really mean that I would like both projects to be part of a multiproject. I don't want to compile the latest version of ProjectA each time I compile ProjectB - instead I would like ProjectB to be dependent on a specific version of ProjectA's IndexManager. With the option of upgrading to a new version at some future point. I.e. much as with the sorts of dependencies you get from Maven or JCenter...
Both projects have the application plugin, so ProjectA produces an executable .jar file whose name incorporates the version. But currently this contains only the .class files, the resource files, and a file called MANIFEST.MF containing the line "Manifest-Version: 1.0". Obviously it doesn't contain any of the dependencies (e.g. Lucene jar files) needed by the .class files.
The application plugin also lets you produce a runnable distribution: this consists of an executable file (2 in fact, one for *nix/Cygwin, one for Windows), but also all the .jar dependencies needed to run it.
Could someone explain how I might accomplish the task of packaging up this class, IndexManager (or alternatively all the classes in ProjectA possibly), and then including it in my dependencies clause of ProjectB's build.gradle... and then using it in a given file (Groovy or Java) of ProjectB?
Or point to some tutorial about the best course of action?
One possible answer to this which I seem to have found, but find a bit unsatisfactory, appears to be to take the class which is to be used by multiple projects, here IndexManager, and put it in a Gradle project which is specifically designed to be a Groovy library. To this end, you can kick it off by creating the project directory and then:
$ gradle init --type groovy-library
... possible to do from the Cygwin prompt, but not from within Eclipse as far as I know. So you then have to import it into Eclipse. build.gradle in this library project then has to include the dependencies needed by IndexManager, in this case:
compile 'org.apache.lucene:lucene-analyzers-common:6.+'
compile 'org.apache.lucene:lucene-queryparser:6.+'
compile 'org.apache.lucene:lucene-highlighter:6.+'
compile 'commons-io:commons-io:2.6'
compile 'org.apache.poi:poi-ooxml:4.0.0'
compile 'ch.qos.logback:logback-classic:1.2.1'
After this, I ran gradle jar to create the .jar which contains this IndexManager class, initially without any fancy stuff in the manifest (e.g. name, version). And I put this .jar file in a dedicated local directory.
Then I created another Gradle project to use this .jar file, the critical dependency here being
compile files('D:/My Documents/software projects/misc/localJars/XGradleLibExp.jar' )
The file to use this class looks like this:
package core
import XGradleLibExp.IndexManager
class Test {
public static void main( args ) {
println "hello xxx"
Printer printer = new Printer()
IndexManager im = new IndexManager( printer )
def result = im.makeIndexFromDbaseTable()
println "call result $result"
}
}
class Printer {
def outPS = new PrintStream(System.out, true, 'UTF-8' )
}
... I had designed IndexManager to use an auxiliary class, which had a property outPS. Groovy duck-typing means you just have to supply anything with such a property and hopefully things work.
The above arrangement didn't run: although you can do build and installdist without errors, the attempt to execute the distributed executable fails because the above 6 compile dependency lines are not present in build.gradle of the "consumer" project. When you put them in this "consumer" Gradle project's build.gradle, it works.
No doubt you can add the version to the generated .jar file, and thus keep older versions for use with "consumer" projects. What I don't understand is how you might harness the mechanism which makes the downloading and use of the dependencies needed by the .jar as automatic as we are used to for things obtained from "real repositories".
PS in the course of my struggles today I seem to have found that Gradle's "maven-publish" plugin is not compatible with Gradle 5.+ (which I'm using). This may or may not be relevant: some people have talked of using a "local Maven repository". I have no idea whether this is the answer to my problem... Await input from an über-Gradle-geek... :)
You should be able to update the Eclipse model to reflect this project-to-project dependency. It looks something like this (in ProjectB's build.gradle):
apply plugin: 'eclipse'
eclipse {
classpath.file.whenMerged {
entries << new org.gradle.plugins.ide.eclipse.model.ProjectDependency('/ProjectA')
}
project.file.whenMerged {
// add a project reference, which should show up in /ProjectB/.project's <projects> element
}
}
These changes may be to the running data model, so they may not actually alter the .classpath and .project files. More info can be found here: https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseModel.html
This issue is discussed here: http://gradle.1045684.n5.nabble.com/Gradle-s-Eclipse-DSL-and-resolving-dependencies-to-workspace-projects-td4856525.html and a bug was opened but never resolved here: https://issues.gradle.org/browse/GRADLE-1014

query DSL : test cases that use query DSL works fine, but editor pane of IntelliJ reports that generated meta models cannot be resolved

I am trying to integrate query DSL with Spring, Gradle, and IntelliJ 15. I've configured my IntelliJ project after checking out this link. It leads executing a test case method that use query DSL to a green light, but editor pane still reports that meta models (Q classes) cannot be resolved. except things that the link provides, Is there something that I have to configure, in order to fix this IDE error?
Here's what I have tried:
enabled annotation processor on Preferences -> Build, Execution, Deployment -> Annotation Processors
in "Store generated sources relative to:" select Module content root.
make sure that the generated folder is registered on Project Structure.
imported two dependencies to build.gradle: querydsl-jpa and querydsl-apt.
added the following script to build.gradle
apply plugin: 'idea'
idea {
module {
sourceDirs += file('generated/')
}
}
if you want to check out a minimum implementation of this problem, git-clone this. any comments would be awesome. 'It works for my desktop' would be OK!! I really need your help.

Annotation processor in Gradle outputs source files to build/classes making javadoc fail. How to fix it?

I have an annotation processor that is automatically picked up by the Java compiler at build time (using SPI). During a gradle build, the generated java sources of this annotation processor are put in build/classes as Gradle tells the annotation processor that this is the place to output generated source files.
When the standard javadoc task is run, it tries to create javadoc for all files in build/classes, including *.java. This failes because javadoc only expects *.class files, making the whole build fail.
So my question is:
Is this a Gradle bug/feature?
How do I fix it/make it work?
It seems the problem is that the generated source files are not picked up, making the javadoc fail because it had nothing to process.
I'm posting the solution here in case somebody is experiencing the same problem:
The problem with compile time source generation in gradle is that the outputted sources are not automatically picked up by the javadoc. This is a problem if all your sources are auto generated. The build will fail with an error saying that no sources could be processed. In the other case your build will succeed but you will have no javadoc of your generated java sources.
The root problem here is gradle's poor integration with generating sources that are both generated and compiled during the same compile step. To remedy this I changed my build files to this.
project layout:
rootproject
rootproject/annotationProcessor
rootproect/userOfAnnotationProcessor
build file of userOfAnnotationProcessor
def generatedSources = "$buildDir/generated-src"
def generatedOutputDir = file("$generatedSources")
compileJava {
doFirst {
generatedOutputDir.exists() || generatedOutputDir.mkdirs()
options.compilerArgs = [
'-s', "${generatedSources}"
]
}
}
sourceSets {
main {
java {
srcDirs += generatedOutputDir
}
}
}
javadoc {
source = sourceSets.main.resources
}
compileJava.dependsOn clean
The trick here is to not add your generated sources to a custom sources set, else we'll run into troubles when trying to build aggregated javadoc in our root project. However this solution has the nasty side effect that our generated sources or added twice for some reason when trying to build after a first clean+build. The solution here is to always do a clean+build.
Now when doing an aggregate javadoc build, we'd like our generated source javadoc to be part of it as well.
This is how our rootproject build file looks like:
def exportedProjects = [
":annotationProcessor",
":userOfAnnotationProcessor",
]
task alljavadoc(type: Javadoc) {
source exportedProjects.collect { project(it).sourceSets.main.allJava }
classpath = files(exportedProjects.collect { project(it).sourceSets.main.compileClasspath })
destinationDir = file("${buildDir}/docs/javadoc")
}
alljavadoc.dependsOn(":userOfAnnotationProcessor:compileJava")
If we had used a custom source set previously, gradle would now start complaining about source set properties not being found. Why? I don't know... A last important thing to notice is that our alljavadoc depeonds on the compilation step of userOfAnnotationProcessor, this is needed to make sure our generated source files are there when the aggregated javadoc is build.
I hope I've helped sombody with this explanation!
I am not quite sure weather it is a bug or not. But as a workaround just filter the sources of javadoc.
Depending on how your build script looks like, it should look something like thistask
myJavadocs(type: Javadoc) {
classpath = sourceSets.main.output.filter { it -> !it.name.endsWith('.java') }
}

Gradle - how to run the tests from a different gradle project and still get coverage data

Does anyone know how to run the tests from a different gradle project and still get emma coverage reporting data?
Here is my current layout:
Root/
settings.gradle (no explicit build.gradle - just defines all subprojects)
SubProjectA/
build.gradle
src/ (all real source is here)
SubProjectATest/
build.gradle
src/ (all testing code is here)
SubProjectB/ (similar structure as A)
SubProjectBTest/ (similar structure as ATest)
I am currently using the emma plugin, and I would like to build SubProjectA and run all the tests in SubProjectATest from within the build.gradle of SubProjectA.
Here are some things I tried inside the build.gradle of SubProjectA
testCompile project(':SubProjectATest').sourceSets.test.classes (as suggested by this article), but I got an error "Could not find property 'sourceSets' on project"
Just the straight-up testCompile project(':SubProjectATest'), but then I get "..SubProjectA/build/classes/test', not found" and also "Skipping task ':SubProjectA:compileTestJava' as it has no source files."
Simply adding a sourceSet like the following:
test {
java {
srcDir '../SubProjectATest/src'
}
}
Adding the source set in (option 3) is the only option that worked, but it seems sloppy to do it this way. Does anyone know how to do this using project dependencies?
Update #1
I also tried one of the answers below to use test.dependsOn and the tests do run, but the emma plugin reported the following: build/classes/test', not found
1. and 2. just add classes to the test compile class path. This doesn't have any effect on which tests are going to be executed.
3. is the wrong approach because you should not add sources from project X to project Y.
If what you want is that gradle :SubProjectA:test also executes :SubProjectATest:test, all you need to do is to add a task dependency:
SubProjectA/build.gradle:
test.dependsOn(":subProjectATest:test")
By the way, what is your motivation for putting the tests in a separate project?

Categories