Spring Boot + Spring-Loaded (IntelliJ, Gradle) - spring

I'd like to use hot swap with my Spring Boot project. Somehow I am not able to make it working by running it in my IDE (IntelliJ), despite of having this topic covered by documentation. I simply run the class with my main method with VM attributes:
-javaagent:/path/to/jar/springloaded.jar -noverify
My question is, how do I make it work? :-)
Further question is how to use spring loaded with Gradle and IntelliJ. I find it quite inconvenient to force the developer to download the JAR manually, place it somewhere and point to it with a JVM parameter. Is there any better way (should I configure my own task which does the job and run it from my IDE as a Gradle task)?

You need to configure the project as stated in the documentation:
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-reload-springloaded-gradle-and-intellij-idea
After that, you must configure your IDE to output the compiled classes in build/classes/main (with Idea plugin, you can configure the outputDir as specified in the above link, and then invoke gradle idea to have it done).
Then, if you launch the task (run / bootRun) or run the main class from the IDE's using the debug mode, hot code reloading should work when a class is compiled.
The gotcha here is that IntelliJ, unlike Eclipse, doesn't automatically compile a class when it is saved (even if you configure the compiler to "Build on save", it won't do it when is Running/Debugging). This is apparently a design decission made by IntelliJ - as stated here Intellij IDEA Java classes not auto compiling on save (CrazyCoder answer) .
It would be ideal if spring boot provided a configuration option to monitor your source code files and recompile them when they change - that is what Grails does. But I think such a think does not exist yet, and maybe is not even possible to combine that with gradle, which is the responsible of managing the classpath and that kind of things.
So there are two options as far as I can tell:
You remember to compile everything you edit (adding an easier Compile shortcut as suggested in the previous StackOverflow link might help).
You put some filesystem monitor (inotify-tools for Linux, launchd for Mac OS X are examples) that invokes gradle compileJava/compileGroovy when a change is detected in any source code file.
First is tedious, second is slow :) . Actually there's another option: you change your IDE :-D (or install the EclipseMode IntelliJ plugin).

If one wants to be able to run the application solely from IntelliJ (using Right Click -> Debug on the main method) and not involve Spring Boot's Gradle tasks at all, you simply need to do the following:
Configure the run configuration in IntelliJ to use the SpringLoaded agent. This is easy to do and an example is shown in the following screenshot:
Notice how I have added a VM Option: -javaagent:/path/to/springloaded-${version}.jar -noverify (which you can download here)
Debug using Right Click -> Debug like the following screenshot:
Everytime you make a change and want to reload it, just compile the project. The default shortcut is Cntrl+F9, but you can also access it from the menu Build -> Make Project

I managed to do this with IDEA in a Maven project, it should work with the Gradle version as well I guess, my procedure was the following.
Settings -> Compiler -> Make project automatically (only works while not running/debugging !)
Start the project with the sprint-boot-plugin outside of the IDE (a trick because of the above sentence).
The Maven plugins setup looks like the following:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Now change some code and reload the page.

I'm running SpringBoot and Gradle in IntelliJ Idea. Auto-reloading is working 100%
Static Content is autoreloading (instantly)
Thymeleaf is autoreloading (instantly)
Controllers / Java Files require me to hit the "build" button (takes a few seconds), but does not require a restart - it's just to compile the files, so Spring Loaded can pick them up.
Step 1: Get SpringLoaded going
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE"
classpath 'org.springframework:springloaded:1.2.1.RELEASE'
}
}
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'
apply plugin: 'spring-boot'
repositories {
jcenter()
mavenLocal()
mavenCentral()
}
mainClassName = 'com.noxgroup.nitro.NitroApp'
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.2.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
}
jar {
baseName = 'org.noxgroup-nitro'
version = '0.1.0'
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
Step 2: Create an Application.properties
And add the following:
spring.thymeleaf.cache=false
Step 3: Run the bootRun task
(Not just the standard run task - this adds SpringLoaded functionality)
Step 4: Compile your Java
Make your Java files by hitting "Make Project" or pressing "Ctrl/Command + F9

In later versions of gradle you can start a command window and run
gradle -t classes
This will start a process that look for changes to source code and recompile them.
In another run.
gradle bootRun
Any changes to java or groovy src will automatically be recomplied. While not intellij specific, your good to go with intellij. You could even make do with you favorite text editor (e.g. submlime, atom ).
credits/link

Related

How to get Intellij to recognize properties in application.yml

I am trying to get Intellij to recognize my properties using gradle. I have followed the steps here. So this means I have a #ConfigurationProperties annotated class with some properties.
I added the spring dependency to process them:
dependencies {
optional "org.springframework.boot:spring-boot-configuration-processor"
}
compileJava.dependsOn(processResources)
I added the plugin (I've tried not using the plugin and just making it a compile dependency, no change)
buildscript {
repositories {
maven { url 'http://repo.spring.io/plugins-release' }
}
dependencies { classpath 'io.spring.gradle:propdeps-plugin:0.0.9.RELEASE' }
}
apply plugin: 'propdeps'
apply plugin: 'propdeps-maven'
apply plugin: 'propdeps-idea'
When I run the build, I see a build/classes/java/main/META-INF/spring-configuration-metadata.json file is created based off of my properties.
When I try to use the property in either application.yml or application.properties, Intellij says it cannot resolve it.
The docs does say it should be called additional-spring-configuration-metadata.json and may expect it to be called that to process it, but I do not see a way to make the build name it that way nor configure Intellij to expect otherwise.
Has anyone got this working with gradle? Or is this a bug.
Edit I created a repo with a pair of projects to demonstrate this. One for gradle and one for maven. I created the projects from start.spring.io and basically just added the properties configuration. I also used a straight compile dependency in both cases instead of optional / compileOnly.
I had not confirmed this before, but the code assist does work for maven, but not gradle. Both create a spring-configuration-metadata.json in the META-INF in their respective build folders. I am not exactly sure who is not picking it up.
Misc relevant versions
Intellij: 2017.3.4
Springboot: 1.5.9
Gradle: 4.4.1
Java: 8.161
Turn the annotation processing on
Do not delegate IDE build/run actions to Gradle
Rebuild your project in IDE: Build -> Rebuild Project
As far as I can tell, IntelliJ (at the time of this writing, 2018.1.2) wants the spring-configuration-metadata.json file to either be in a main source root (src/main/resources/META-INF/ or src/main/java/META-INF/) or in its default output directory for it to pick it up for autocompletion of properties in your source tree. To expand on phospodka's comment, you can add something like this to your build.gradle to satisfy IntelliJ.
task copyConfigurationMetadata(type: Copy) {
from(compileJava) {
include 'META-INF/spring-configuration-metadata.json'
}
into "out/production/classes"
}
compileJava {
dependsOn processResources
finalizedBy copyConfigurationMetadata
}
This answer is a combination of the (at this time) other two answers with a minor twist. In my case this is what "fixed" the issue: (in 2019.03.01-Ultimate)
Turn on the default annotation processing (File>Settings>Build, Execution, Deployment>Annotation Processors>Enable Annotation processing
Select Obtain processors from project classpath
Select Store generated sources relative to module output directory
keep other defaults
click OK
Add the code in #thyme's answer to your build.gradle
EXCEPT instead of into "out/production/classes"
use: into "build/generated/sources/annotationProcessor"
Now you should be able to run gradle clean/build and Intellij should be able to find your "additional metadata" definitions.
Notice that even though the build.gradle code doesn't explicitly mention 'additional-spring-configuration-metadata.json', it is exactly that "additional metadata" that ends up in the annotationProcessor folder as "spring-configuration-metatdata.json" where Intellij finds it.
EDIT: Also note, you need to clean / rebuild after adding any new "additional metadata" items before Intellij will see the new entries (in the freshly regenerated file).

I need debugging tips for a broken project dependency

It's configured the same here as it is everywhere else. In fact I use an import for configuring integration tests. I've done everything I can think of including a rename of local integration test tasks and configurations, including looking at the dependency tree, including running with --debug. Yet for some reason Gradle insists that the property integrationTest doesn't exist on the sourceSet for an inter-project dependency:
integrationTestCompile project(':components:things-components:abc-stuff').sourceSets.integrationTest.output
...now I'm not particularly fond of this syntax and I've already griped up a storm about inter-project test dependencies and how they should be in a test utility component. However, I'm doing it this way because this appears to be what IntelliJ will accept. Writing like this causes trouble:
integrationTestCompile project(path: ':components:things-components:abc-stuff', configuration: 'integrationTest')
How can I figure this out? I just don't get why only one project has this issue.
For the record, I've also tried:
integrationTestCompile project(path: ':components:things-components:abc-stuff', configuration: 'integrationTestCompile')
The issue is that Gradle hasn't been told to make a jar for you that you can consume in your other project.
integrationTestCompile project(':components:things-components:abc-stuff').sourceSets.integrationTest.output
Gets the classFiles and the dependencies, thats why its working using that notation. If you want to use the configuration notation you will need to tell gradle to publish a jar on the integrationTest. The jar doesn't have to be published to a repository, but will be used for the internal builds.
You can do this by doing:
configurations {
integrationTest
}
task integrationTestJar (type: Jar) {
baseName = "${project.name}-integ-test"
from sourceSets.integrationTest.output
}
artifacts {
integrationTest integrationTestJar
}
If you end up doing this in a log of projects, I would recommend writing a quick plugin that does this for you.

Gradle, OSGI and dependency management

I'm new to Gradle, please, help me to understand the following. I'm trying to build an OSGI web app via Intellij Idea + Gradle. I've found that Gradle has OSGI plugin, which is described here:
https://docs.gradle.org/current/userguide/osgi_plugin.html
But I have no idea on how to add dependency on, for example, org.apache.felix.dependencymanager which is OSGI bundle. So, I need this jar while compilation, and I don't need it in my resulting jar. I think, that I need something similar to maven 'provided' scope, or something like that.
P.S. Does anyone understand, what 'TBD' means in Gradle documentation? Does this means it has to be implemented in future, or is some mechanism is implemented, but is not yet described in docs?
Please check out the plugin I wrote, osgi-run, which was designed to make it extremely easy to play with OSGi without using any external tools like Eclipse (though osgi-run can generate a Manifest file for you, which you can point at from your IDE to get IDE OSGi support - this is what I do using IntelliJ), just Gradle.
With osgi-run, you just add a dependency to whatever you want as with any Java project... whether it should be provided by the environment or not does not matter at compile time, this is a deployment-time concern.
For example, add to your build.gradle file:
apply plugin: 'osgi' // or other OSGi plugin if you prefer
repositories {
mavenCentral() // add repos to get your dependencies from
}
dependencies {
compile "org.apache.felix:org.apache.felix.dependencymanager:4.3.0"
}
Note: the osgi plugin is just required to turn your jar into a bundle. osgi-run does not do that.
If you have any runtime dependencies that should be present in the OSGi environment but not in the compile classpath, do something like this:
dependencies {
...
osgiRuntime 'org.apache.felix:org.apache.felix.configadmin:1.8.8'
}
Now write some code, and once you're ready to run a OSGi container with your stuff in it, add these lines to the build.gradle file:
// this should be the first line
plugins {
id "com.athaydes.osgi-run" version "1.4.3"
}
...
// deployment to OSGi container config
runOsgi {
// which bundles do you want to add?
// transitive deps will be automatically added
bundles += project
// do not deploy jars matching these regexes (not needed, this is the default)
excludedBundles = ['org\\.osgi\\..*']
// make the manifest visible to the IDE for OSGi support
copyManifestTo file( 'auto-generated/MANIFEST.MF' )
}
Run:
gradle createOsgiRuntime
And find your full OSGi environment, ready to run, in the build/osgi directory.
Run it with:
build/osgi/run.sh # or run.bat in Windows
You can even run it during the build already:
gradle runOsgi
So you probably want to make your own provided configuration.
configurations {
// define new scope
provided
}
sourceSets {
// add the configurations to the compile classpath but not runtime
main.compileClasspath += configurations.provided
// be sure to add the provided configs to your tests too if needed
test.compileClasspath += configurations.provided
}
dependencies {
// declare your provided dependencies
provided 'org.apache.felix:org.apache.felix.dependencymanager:4.3.0'
}
Also the suggestion above about using the bndtool directly instead of the gradle provided osgi plugin is a good one. The gradle plugin has many deficiencies and is really just a wrapper to the bndtool anyways. Also the gradle team has declared they do not have the bandwidth or expertise to fix the osgi plugin [1].
[1] https://discuss.gradle.org/t/the-osgi-plugin-has-several-flaws/2546/5

Debugging simple Gradle Java App in Intellij

I'm trying to debug a simple gradle Java app in IntelliJ and I'm having the hardest time. I can run my tasks from the JetGradle plugin just fine, but when I right click the task and try to debug it, I get a message in the console that says it's connected, and then it says the equivalent disconnected:
Disconnected from the target VM, address: '127.0.0.1:59303', transport: 'socket'
Not really sure how to proceed from here. I've tried looking online etc and haven't really found much except people talking about how JetGradle plugin isn't very good.
Here is my build.gradle if it matters.
apply plugin: 'java'
apply plugin: 'idea'
task(parse, dependsOn: 'classes', type: JavaExec) {
main = 'com.test.creator.Main'
classpath = sourceSets.main.runtimeClasspath
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.code.gson:gson:2.2.4'
compile 'org.xerial:sqlite-jdbc:3.7.2'
compile 'net.sourceforge.jexcelapi:jxl:2.6.12'
}
Any help or direction would be appreciated.
A few additional things to note:
I'm using the gradle Daemon as it dramatically decreased my build time with certain projects, so I'd prefer to keep that on.
I'm using the Gradle Wrapper.
I'm running IntelliJ 12.1.6
I'm not sure if right-clicking the Gradle task and hitting Debug is the right solution (you might be debugging Gradle here, rather than your application). The easiest solution is to just run/debug the com.test.creator.Main class directly (from IntelliJ).
PS: Gradle support is much better in IntelliJ 13 (EAP).

how to tell gradle to download all the source jars

Ideally, we would like to add a task for downloading all the source jars for the first level and transitive dependencies of our project. Is there a way to do that?
If not, is there a command line option to supply like maven has to get all the sources downloaded onto our machines?
It seems like that should just be the default these days at least for first level dependencies as it gives you the javadoc in eclipse then which is very nice when doing the code completion stuff.
The eclipse task can be configured with downloadSources. Following is an example of that configuration
apply plugin: 'java'
apply plugin: 'eclipse'
eclipse {
classpath {
downloadSources=true
}
}
So run
gradle cleanEclipse eclipse
to have it download sources.
If you use Eclipse and want to navigate the source code of your dependencies there, then the Eclipse plugin does this for you.
Install the eclipse plugin by adding apply plugin: "eclipse" to your build.gradle file. Then run gradle eclipse to generate the Eclipse .project, .classpath and .settings files. The plugin will download all available sources automatically and add references them in the .classpath file (see the sourcepath attribute of the classpathentry element).
To import the project into Eclipse, choose File > Import... > Existing Projects into Workspace and select your project.
(I'm not sure whether the Idea plugin does the same for Idea users, but it may do).
Another catch not mentioned in other answers is when you are using mavenLocal() repository in your gradle.build file. If there are downloaded jar in that local maven repo but no downloaded sources or javadocs in that repo, then gradle will not even try to download javadocs or sources for you. Even with enabled eclipse.classpath.downloadJavadoc and eclipse.classpath.downloadSources.
The solution is to remove mavenLocal() from repositories or place it to bottom of the list. Or you can setup maven to download sources and javadocs and clean your maven local repository (~/.m2/repository).
A more detailed description of the problem is here.
Here is how to add the required configuration in Gradle using the IDEs' plugins:
For Eclipse:
apply plugin: 'java'
apply plugin: 'eclipse'
eclipse {
classpath {
downloadJavadoc = true
downloadSources = true
}
}
For IntelliJ
apply plugin: 'java'
apply plugin: 'idea'
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
To run these plugins:
gradle cleanEclipse eclipse
gradle cleanIdea idea
Piohen's comment above should be it's own answer (since it was the only solution that worked for me)
Right click your project, then select "Build Path" --> "Configure Build Path";
Select "Order and export"
Select "Web App Libraries", and click "Bottom" button, then the "Web App Libraries" will be on the bottom;
And to get this into the Gradle Eclipse plugin (so you don't need to do it manually every time):
Why is Eclipse not attaching 3rd party libs source files to a WTP-faceted Gradle project?
There is only one problem here. This only works if you are generating NEW projects. If you are working on mature projects that can't be re-generated using Gradle, the above suggestions will not work.
One thing I should add is that the version of Gradle/Builsdhip plugin strongly depends on the version of Java you use to start Eclipse. They must all be compatible. I had to switch my Eclipse (current version) from Java 11 back to Java 8 to fix Buildship (3.0.1) errors. We are, and have been, stuck on Gradle 4.2.1 for some time due to API changes in Gradle breaking our build. So to move to Java 11 we have to move to a new version of Gradle, Buildship, and Eclipse. Ugh! Oh yeah, this also fixed the issue mentioned in this thread. Source is now avalable again.

Resources