IntelliJ run vs running a jar, with a Springboot Kotlin, Multi module Gradle project with Social Oauth2 - intellij-idea

TL;DR: Why does everything run fine when started via IntelliJ, and why is it broken when call java -jar app.jar. And how do I fix this?
Alright, I have some issues with a backend I am trying to dockerize. I have an application created with Spring Boot (1.4.2.RELEASE) following the Spring Oauth (2.0.12.RELEASE) guide on their page. I follow the Gradle version, since I prefer Gradle over Maven. Also I am using Kotlin instead of Java. Everything is fine, I start via IntelliJ my backend with static front end, I can login via Facebook (and Google and Github), I receive a nice Principal witch holds al the information I need, and I can modify Spring Security to authorize and permit endpoints. So far so good.
Now for the bad part, when I run either ./gradlew clean build app:bootrun or ./gradlew clean build app:jar and run the jar via java -jar (like I will do in my Docker container), my backend comes up. My static front end pops up. Now I want to login via Facebook, I end up on the Facebook login page, I enter my credentials, and... nothing!
I end up back on my homepage, not logged in, no log messages that mean anything to me, just silence. The last thing I see in the log is:Getting user info from: https://graph.facebook.com/me
This Url will give me in my browser:
{
"error": {
"message": "An active access token must be used to query information about the current user.",
"type": "OAuthException",
"code": 2500,
"fbtrace_id": "GV/58H5f4fJ"
}
}
When going to this URL via an IntelliJ start, it will give me credential details. Obviously something is going wrong, but I have no clue what. Especially since a run from IntelliJ works fine. There is some difference between how the jar is started, and how IntelliJ's run config works, but I have no clue where to search for what. I could post trace logging, or all my Gradle files, but perhaps thats too much info to put in 1 question. I will defenitly update this question if someone needs some more details :)
The structure outline of this project is as follows:
root:
- api: is going to be opensourced later, contains rest definitions and DTOs.
- core: contains the meat. Also here is included in the gradle file
spring-boot-starter, -web, -security, spring-security-oauth2, and some jackson stuff.
- rest: contains versioned rest service implementations.
- app: contains angular webjars amongst others, the front end, and
my `#SpringBootApplication`, `#EnableOAuth2Client`
and the impl of `WebSecurityConfigurerAdapter`.
Why does everything run fine when started via IntelliJ, and why is it broken using bootRun or the jar artefact. And how do I fix this?

I found it, the problem was not Multi module Graldle, Spring boot, or Oauth2 related. In fact it was due to a src set config of Gradle, where Java was supposed to be in a Java src set folder, and Kotlin in a Java src set folder:
sourceSets {
main.java.srcDirs += 'src/main/java'
main.kotlin.srcDirs += 'src/main/kotlin'
}
As Will Humphreys stated in his comment above, IntelliJ takes all source sets, and runs the app. However, when building the jar via Gradle, these source sets are stricter. I had a Java file in my Kotlin src set, which is no problem for IntelliJ. But the jar created by Gradle takes into account the source sets as defined in the build.gralde file, which are stricter.
I found my missing bean issue with the code below:
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
The Bean I missed was called AuthenticationController, which is a #RestController, and kinda crucial for my authentication code.

Related

How to provide an HttpClient to ktor server from the outside to facilitate mocking external services?

I am trying to provide an HttpClient from the outside to my ktor server so that I can mock external services and write tests, however I get this exception when I run my test:
Please make sure that you use unique name for the plugin and don't install it twice. Conflicting application plugin is already installed with the same key as `Compression`
io.ktor.server.application.DuplicatePluginException: Please make sure that you use unique name for the plugin and don't install it twice. Conflicting application plugin is already installed with the same key as `Compression`
at app//io.ktor.server.application.ApplicationPluginKt.install(ApplicationPlugin.kt:112)
at app//com.example.plugins.HTTPKt.configureHTTP(HTTP.kt:13)
at app//com.example.ApplicationKt.module(Application.kt:14)
at app//com.example.ApplicationTest$expected to work$1$1.invoke(ApplicationTest.kt:39)
at app//com.example.ApplicationTest$expected to work$1$1.invoke(ApplicationTest.kt:38)
and thats a bit unexpected to me because I am not applying the Compression plugin twice as far as I can tell. If I run the server normally and manually call my endpoint with curl then it works as expected. What am I doing wrong?
I added a runnable sample project here with a failing test.
sample project
official ktor-documentation-sample project.
The problem is that you have the application.conf file and by default, the testApplication function tries to load modules which are enumerated there. Since you also explicitly load them in the application {} block the DuplicatePluginException occurs. To solve your problem you can explicitly load an empty configuration instead of the default one:
// ...
application {
module(client)
}
environment {
config = MapApplicationConfig()
}
// ...

Network calls not working in EXE distribution build of Compose for Desktop Application

I have used Ktor JVM Client for doing network calls in Compose for Desktop Application.
Network calls are working fine in Debug build means when I am just running the application it's working fine.
But when I create the EXE distribution file, by executing the packageExe task in Gradle, it's creating an EXE file. I have installed it on my machine. Then I am running the application and I am seeing that network calls are not working. I have checked internet is working properly.
Please provide a solution to fix this issue. Thanks in advance.
Your question doesn't leave any details about the failure type (compile error? runtime exception? Empty data? etc.).
But if I had to speculate based on such limited information, I'd guess it's probably this: https://github.com/JetBrains/compose-jb/issues/429
Specifically, when packaging, you need to specify which JVM modules you want to be packed into your distributable app, and likely you are missing your crypto module. Try something like this:
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
modules("jdk.crypto.ec")
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "untitled"
}
}
}

IntelliJ Tomcat Automatic Redeploy when running a gradle task

What I am trying to accomplish:
I want the tomcat 9 server to execute an automatic redeploy when I run a Gradle task that updates my HTML files.
This is my setup:
I use IntelliJ 2020.03 (ultimate edition), tomcat 9, the application is a GWT application.
My Configuration for Tomcat:
This is what I see when I hit the "Configure ..." button next to the line labeled "Application server:"
This is my Gradle task I run but tomcat does not automatically redeploy the changes
Things I already tried:
According to [https://www.jetbrains.com/help/idea/updating-applications-on-application-servers.html] I should have an option to "Update resources". But my options are limited to:
Therefore I assume I need to have "Exploded artifacts in local application server run configurations".
Therefore I headed to Tomcat -> Edit Configuration
I replaced the deploy to the exploded artifact:
Using this I get the following error message on starting up tomcat:
[2021-02-12 08:46:05,533] Artifact Gradle : NewComApp.war (exploded): com.intellij.javaee.oss.admin.jmx.JmxAdminException: com.intellij.execution.ExecutionException: C:\Users\heckner\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war not found for the web module.
So I decided to compare the artifact that "works" (but does not update the HTML files) with the "exploded" artifact which would be probably the right one but throws an error message on startup of tomcat.
This is the one which works ("NewComWar.war"):
This is the one which does throw an error message on startup ("NewComApp.war (exploded)":
As you can see in the image under "... which works". the war already seems to be "exploded". So why does IntelliJ does not offer the "update resources"?
But never the less, when I switch in Tomcat Edition to "NewCompApp.war (exploded)" i am able to select "update resources" in the drop down:
So probably this would be the way to go.
It obviously boils down to the point: What is wrong with the artifact declaration above so that tomcat throws the error message?
The feedback was: "ctually "NewComWar.war" is an archive that contains exploded artifact, that's why only "Redeploy" is possible. Please check that exploded artifact is created in "Output directory". "
Now the question is how I can add the exploded war to the Output Directory?
I tried:
but then I can only select from:
When I add this, it looks like this:
When I run Tomcat, it still says:
[2021-02-12 12:24:54,224] Artifact Gradle : NewComApp.war (exploded): com.intellij.javaee.oss.admin.jmx.JmxAdminException: com.intellij.execution.ExecutionException: C:\Users\heckner\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war not found for the web module.
Now I found the following tip (thanks Evgeny):
https://youtrack.jetbrains.com/issue/IDEA-178450#focus=streamItem-27-4068591.0-0
I switched under Settings -> Build, Execution, Deployment -> Build Tools -> Gradle: "Build and Run:" IntelliJ IDEA
I added this snipped to build.gradle:
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
with war
}
war.dependsOn explodedWar
I switched the artifact which is deployed to the tomcat to
this automatically added the Gradle task:
Build 'Gradle:NewComApp.war (exploded) artifact to the
which is defined like this:
This accomplishes two things:
I can choose "Update resources" on my Edit Configuration for Tomcat like shown below:
My deployment runs well under tomcat
But ... :-)
Updates to the HTML files (within the war file) are not exploded to the NewComWar.war directory.
When I start tomcat I see the following file structure under C:\users<myname>\IdeaProjects\NewComApp\Libs\
The reason for this is that we use a Gradle task that generates the HTML files.
This task is called "copyHTML"
Under build.gradle it is defined now as follows:
war {
from 'war'
dependsOn copyHtml
exclude excludeInWar
doFirst {
manifest {
def version = ant.hasProperty('gitversion') ? ant.gitversion : 'undefined version'
println "Version: ${version}"
attributes("Implementation-Title": project.name, "Implementation-Version": version, "Built-By": new Date())
}
}
}
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
with war
}
war.dependsOn explodedWar
copyHtml {
dependsOn generatorClasses
inputs.dir 'html'
inputs.dir 'email'
inputs.dir 'email.Tags'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'html'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'html.MeetingApp'
inputs.dir props.getProperty('generator.htmlfiles.prefix') + 'staticHtml'
inputs.properties props
outputs.dirs 'war', 'resources/com/newcomapp/server/mail'
doFirst {
ant.properties["generator.classpath"] = sourceSets.generator.runtimeClasspath.getAsPath()
}
}
task warWithoutGwt(type: War, dependsOn: war) {
}
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(warWithoutGwt)) {
compileGwt.enabled = false
}
}
When I run the Gradle task "warWithoutGWT" while tomcat still runs it says:
C:\Users<myname>\IdeaProjects\NewComApp\build\libs\exploded\NewComApp.war\WEB-INF\classes\com\newcomapp\server\integration\GeoLite2-Country.mmdb (The operation is not applicable to a file with an open area assigned to a user)
I assume that tomcat still holds a reference to that file, and the Gradle task tries to overwrite it (although there was no change to that file). Furthermore, I assume that this kills the rest of the Gradle task so that it does not update the HTML files (it's only an assumption though). How can I arrange an exploded war so that write-protected files are omitted and do not kill the rest of the Gradle task execution?
My answer up to now for this problem is: I changed the gradle script:
task explodedWar(type: Copy) {
into "$buildDir/libs/exploded/${war.archiveFileName.get()}"
exclude "**/*.mmdb"
with war
}
war.dependsOn explodedWar
so I added an "exclude for mmdb files". And this really works.
Is this a correct and good solution or do I overlook something? The reason I am asking is that changing HTML files in the scope of tomcat should be something very common with tomcat based projects. So I wonder if there is a more standardized, easier solution to this? It seems quite clumsy to copy and explode with additional gradle tasks the war file instead of IDEA take care of this.

How to generate openapi client from uri in Gradle

I'm probably trying to do something strange, since this doesn't seem like a common question (or maybe I'm asking it all wrong). I was expecting this to be straightforward.
Basically, what I am looking for is a way to do the same as the following, except by using the gradle openapi-generator plugin:
openapi-generator generate -i www.example.com/openapi-doc -g spring
What I have tried is the following (and the associated errors):
inputSpec.set("www.example.com/openapi-doc") --> Cannot convert URL {} to a file
inputSpec.set(URL("www.example.com/openapi-doc").readText()) --> specified for property 'inputSpec' does not exist
The actual code looks something like this:
tasks.register<GenerateTask>("generateClient") {
validateSpec
generatorName.set("spring")
library.set("spring-cloud")
// inputSpec.set("$openapiSpecDir/client/openapi.json") <-- *I am currently using a file, which I don't want to do*
inputSpec.set("https://www.example.com/openapi-doc")
outputDir.set(generatedClientDir)
apiPackage.set("org.example.api")
modelPackage.set("org.example.model")
skipOverwrite.set(false)
templateDir.set("$rootDir/src/main/resources/openapi/templates/client")
configOptions.put("java8", "false")
configOptions.put("serializationLibrary", "jackson")
configOptions.put("dateLibrary", "java8")
}
Assuming you're using the OpenAPI Generator Gradle Plugin, at the time of writing this answer, getting the inputSpec from a URL is not supported. However, for Maven this has been implemented (Issue #2241 closed with PR #3826), so chances are good to have it implemented with a feature request that gets the Gradle plugin on par with its Maven counterpart.
That being said, you may want to look into Gradle Download Task. Gradle Download Task is a plugin that let's you download files from a URL. The downloaded file can be used to feed it into the OpenAPI generator.

Micronaut Kotlin: Rest Controllers not working

I'm creating a simple application with 2 Rest Controllers with Kotlin. However, for every endpoint
the error is
{"message":"Not Found","_links":{"self":{"href":"
My inferences till now, for which I have searched for solutions and they didn't worked.
1.The generated folders are also empty. Might be an issue
As I have tried both IDE and command line. So, ruling out any issue from Idea
Tried to add a Java Controller in the project, but that also didn't run
Tried to use Micronaut annotations along with Spring Web annotations, didn't worked.
Is there anything else that I can change to make it work?
Ideally I would want to make it work with Kotlin and not Java.
Environment:
IDE - IntelliJ Idea 2020.2 Ultimate
Build - tried both Maven and Gradle, command line and IDE
EnableAnnotations: Done
Delegate Gradle Runner: Done
JDK: 11 ( but set as 8 in build file)
Update1 - Got few things working
Got the base code from Micronaut Launcher site
My Sample code
#RestController
#Validated
#RequestMapping("/hello")
open class UserController {
#Get("/echo")
fun echo():String{
return "hello"+ System.currentTimeMillis()
}
}
AOP is not working and thus had to use #Validated and open. The build.gradle has allOpen plugin.
#GetMapping should work as per https://micronaut-projects.github.io/micronaut-spring/latest/guide/#springMvc
I had to use Micronaut's #Get to make it working
Please help in fixing these 2 issues.