Add plugins to an existing Ktor project - kotlin

I am learning Kotlin for backend.
I am using Ktor, and following tutorials on ktor.io website.
I am using IntelliJ IDEA CE (Community Edition), so I do not have access to Ktor configuration page nor plugins page (available on IntelliJ IDEA Ultimate, a premium edition).
I have to use the web based project generator, which asks me all the plugins I will need.
However, I might not know which plugins I will need : my project can grow, and I might need more plugins later.
Is there an efficient way to add plugins to an already existing project ?

The best way I can recommend is to read the official documentation where you can find descriptions for all standard plugins and how to add them to a project.

Go to your build.gradle.kts file in project.
There you have a plugin and a dependencies block.
plugins {
application
kotlin("jvm") version "1.7.20"
id("io.ktor.plugin") version "2.1.2"
kotlin("plugin.serialization") version "1.7.20"
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
}
....
dependencies {
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("io.ktor:ktor-server-freemarker-jvm:$ktor_version")
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
implementation("io.ktor:ktor-serialization:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-server-content-negotiation-jvm:2.1.2")
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
implementation("com.h2database:h2:$h2_version")
// Testing
testImplementation("org.junit.jupiter:junit-jupiter:$junitVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
testImplementation("org.assertj:assertj-core:3.23.1")
}
The correct line for implantation you will find usually on the specific webpage of the Ktor documentation.
After adding some lines in there you should press the gradle update button which appears when something changes in this file.

Related

How do I encapsulate version management for gradle plugins?

Problem
I have a setup of various distinct repos/projects (i.e. app1, app2, app3) that all depend on shared functionality in my base package.
The projects also use various other third-party dependencies (i.e. app1 and app3 use spring, all of them use kotlinx-serialization).
I want to synchronise the versions of all third-party dependencies, so that any project using my base package uses the same version of every third-party dependency. However, I don't want to introduce new dependencies to projects that do not use them (i.e. app2 does not use spring)
Solution attempts
For libraries, I have been able to solve this with the help of a gradle platform, which does exactly what I want - I specify the versions in my base package, then add the platform as a dependency to my projects and can then simply add dependencies by name (i.e. implementation("org.springframework.boot:some-package")) without having to specify a version number, because it uses the provided value from my platform.
However, for plugins, I have not been able to do this. Many libraries come with plugins and naturally the plugin should be at the same version as the library. I have tried various approaches, including writing a standalone plugin, but none have worked.
Current best idea
I added implementation("org.springframework.boot:spring-boot-gradle-plugin:3.0.2") to the dependencies of my standalone plugin. Then, I added the following code to my standalone plugin:
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.plugins.apply("org.springframework.boot")
}
}
This works and applies the plugin to my main project at the correct version. However, there are 2 major problems with this:
a) Now every project applies the spring plugin, including app2 (which does not use spring).
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
I just want to manage the versions of plugins and libraries of multiple projects/repos in a central place - this feels like a fairly basic use case - why is this so hard?
There are some great and detailed answers about dependency management, but unfortunately none worked to perform cross-project version management for plugins.
It seems that there is no gradle functionality to do this, but I got it working with a bit of a workaround. Here is my (working) approach, in hope that it helps someone else with this:
Create a Standalone gradle Plugin
In the build.gradle.kts of the plugin, include the maven coordinates (not its ID) of every other plugin whose version you want to manage in any of your projects in the dependency block with the api keyword. i.e. api("org.springframework:spring-web:6.0.2")
In the main projects, remove every other plugin from the plugins block, so that your custom standalone plugin is the only one remaining.
Create a file (i.e. a plugins.json or whatever you want) in the project root directory of all main projects and in there supply the plugin IDs of the plugins that you actually intend to use in that project. Just the IDs, no version numbers, i.e. "org.springframework.boot" for Spring's plugin. (Keep in mind that for plugins declared as kotlin("abc") you will have to add the prefix "org.jetbrains.com.", as the kotlin method is just syntactic sugar for that)
In your plugin source code, in the overriden apply method, look for. a file named plugins.json (or whatever you chose) in the project.buildFile.parent directory (which will be the directory of the project using this plugin, NOT of the plugin itself). From this file, read the plugin IDs
for every pluginID in the file, call project.plugins.apply(id)
How/Why it works:
The main project build.gradle.kts is executed, looks at the plugin block and applies your standalone plugin (which is the only one), which calls its apply method.
This plugin then applies other plugins based on their ID from the file.
Normally, this will throw an error because these plugins are not found, but because we defined them as dependencies with the api keyword in our standalone plugin, they are now available on the classpath and in exactly the version of that import statement.
Hope it helps someone!
I use version numbers in a gradle.properties file for this purpose. Since the introduction of Gradle version catalogs, my approach is probably a bit out of date, but I'll share it here anyway. It's based on the fact that plugin versions can be managed in settings.gradle.kts by reading values from the properties file.
In gradle.properties:
springBootVersion=3.0.2
In settings.gradle.kts:
pluginManagement {
val springBootVersion: String by settings
plugins {
id("org.springframework.boot") version springBootVersion
}
}
And finally in build.gradle.kts:
plugins {
id("org.springframework.boot")
}
dependencies {
val springBootVersion: String by project
implementation(platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"))
}
Notice that the plugin version is omitted in the build script because it is already specified in the settings file.
And note also that the method for accessing the property in the settings script is slightly different from that in the build script.
a) Now every project applies the spring plugin, including app2 (which does not use spring).
It is indeed better to avoid applying too many plugins - and that's why Gradle encourages reacting to plugins.
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
import org.springframework.boot.gradle.plugin.SpringBootPlugin
class BasePlugin : Plugin<Project> {
override fun apply(target: Project) {
// don't apply
//target.plugins.apply("org.springframework.boot")
// instead, react!
target.plugins.withType<SpringBootPlugin>().configureEach {
// this configuration will only trigger if the project applies both
// BasePlugin *and* the Spring Boot pluging
}
// you can also react based on the plugin ID
target.pluginManager.withPlugin("org.springframework.boot") {
}
}
}
Using the class is convenient if you want to access the plugin, or the plugin's extension, in a typesafe manner.
You can find the Plugin's class by
looking in the source code for the class that implements Plugin<Project>,
in the plugin's build config for the implementationClass,
or in the published plugin JAR - in the META-INF/gradle-plugins directory there will be a file that has the implementationClass.
This doesn't help your version alignment problem - but I thought it was worth mentioning!
b) I have many plugins to manage and no idea how to get the long implementation-string for most of them. I found the "org.springframework.boot:spring-boot-gradle-plugin:3.0.2" by looking up the plugin-id on https://plugins.gradle.org/ and then looking at the legacy plugin application section, which sounds like I am on the wrong track.
You're on the right track with the "long implementation string" as you call it. I'll refer to those as the 'Maven coordinates' of the plugin.
Gradle Plugin Maven Coordinates
The plugin id of the Kotlin JVM plugin is org.jetbrains.kotlin.jvm, but the Maven coordinates are org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0 .
The 'legacy' part refers to how the plugins are applied, using the apply(plugin = "...") syntax. The new way uses the plugin {} block, but under the hood, both methods still use the Maven coordinates of the plugin.
If you add those Maven coordinates (with versions) to your Java Platform, then you can import the platform into your project. But where?
Defining plugin versions
There are a lot of ways to define plugins, so I'll only describe one, and coincidentally it will be compatible with defining the version using a Java Platform.
If you're familiar with buildSrc convention plugins, you'll know that they can apply plugins, but they can't define versions.
// ./buildSrc/src/main/kotlin/kotlin-jvm-convention.gradle.kts
plugins {
kotlin("jvm") version "1.8.0" // error: pre-compiled script plugins can't set plugin versions!
}
Instead, plugin versions must be defined in the build config for buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// the Maven coordinates of the Kotlin JVM plugin - including the version
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
}
This looks a lot more traditional, and so I hope the next step is clean: use your Java Platform!
Applying a Java Platform to buildSrc
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
// import your Java Platform
implementation(platform("my.group:my-platform:1.2.3"))
// no version necessary - it will be supplied by my.group:my-platform
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
Note that this same method will also apply if your projects an 'included build' instead of buildSrc.
Once the plugin versions are defined in ./buildSrc/build.gradle.kts, you can use them throughout your project (whether in convention plugins, or in subprojects), they will be aligned.
// ./subproject-alpha/build.gradle.kts
plugins {
kotlin("jvm") // no version here - it's defined in buildSrc/build.gradle.kts
}

How to use newer version of a library with Spring Boot

I am developing an application using Spring Boot 1.5.9.RELEASE, and I am using Gradle as build tool.
I want to use SelenumHQ 3.8.1 in the project.
When I was building the project I noticed that Selenium 2.53.1 was added to project (not 3.8.1), so I investigated and found out the reason.
There exists following statement in my build.gradle:
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:1.5.9.RELEASE")
}
}
and in that file selenium.version property is set to '2.53.1'.
So I have changed the statement to
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:1.5.9.RELEASE") {
bomProperty 'selenium.version', '3.8.1'
}
}
}
but now IDEA shows both 3.8.1 and 2.53.1 as dependencies of the project, and when I build the artifact using gradle, there only exists 2.53.1 dependencies and no sign of 3.8.1.
How can I change this behavior and use Selenium 3.8.1?
P.S. The Selenium is made of multiple jar files, so it is not just a single Jar file, and I want to update all of them in the most minimized using gradle.
I have found the answer in here.
I shall override the property using: ext['selenium.version']='3.8.1' statement.
When IntelliJ imports the dependencies, it will not remove old dependencies like this.
You would have to clean it out, then re-import the dependencies. Of course, another dependency might also have a dependency on 2.53.1 that would bring it in again.
I don't know Gradle, but maven you can do a mvn tree dependency that would list all the dependencies and further down and you can look through it to see if selenium is a dependency for another dependency too.

How to use gradle in intellij idea plugin project?

I am developing an idea plugin, and it is an intellij idea project.
I want to use gradle to manage the dependency.
How to config?
There is now a Gradle plugin for building IntelliJ Platform Plugins. In order to use it, you will need to add the following snippet to your build.gradle file.
plugins {
id "org.jetbrains.intellij" version "0.0.31"
}
apply plugin: 'org.jetbrains.intellij'
For more information, please see this guide to help you get started.
Ok, there are multiple ways to create an IntelliJ project, "templates" if you like, and unfortunately you can only pick one of them (IntelliJ plugin or gradle).
Thankfully, it's easy to configure a project for gradle in IntelliJ.
First, create a new project from the IntelliJ Platform Plugin template. You don't need to choose any Additional Libraries and Frameworks. This will give you a project structure including META-INF/plugin.xml and the Project SDK should be something like IDEA IU-129.451.
From here, simply create a new file named build.gradle at the top level of your project, including for example this line:
apply plugin: 'java'
Now, close the project. You can now use File -> Import Project..., choose the build.gradle file that you just created, and import the project. Accept the defaults for importing and hit OK.
The project is now opened with both gradle and intellij plugin enabled!
Notice that the source root src has disappeared and you will need to right click on src in the Project pane and select Mark Directory As -> Source Root.
To prepare the plugin for deployment, there is still the menu option in the Build menu for that - if you want to automate that part via gradle, good luck and please let us know how it's done ;)

How to specify library dependencies for an IntelliJ IDEA plugin?

I am developing a plugin for IntelliJ IDEA. The way I am going about this is by creating a plugin project in IDEA, then packaging this into a jar with appropriate META-INF/plugin.xml, and installing the plugin from the jar.
The problem is that I would like to add a dependency on org.scala-lang:scala-library:2.11.0. I have this specified as a library dependency in the IDEA project, but this information never seems to get passed along to the generated JAR.
How can I include this information in such a way that IntelliJ IDEA will recognize it?
As far as I understand, you want to bundle some library (e.g. scala library) with your plugin.
This is pretty simple.
Go to Project Settings, select module and go to Dependencies tab. Set scope for the library you want to bundle to 'Compile'. In this example it is 'checker-framework' library. 'groovy-2.3.6' library will not be bundled due to its scope set to 'Provided'. Save changes.
Prepare plugin for deployment
Then you got plugin, zipped, ready for deployment (uploading to repo or installing locally) in the root of project. It will contain lib folder with all necessary jars.
The officially supported plugin dependency management solution is to use Gradle with the gradle-intellij-plugin, via Gradle's dependencies or the intellij.plugins entry points. If you want to add a dependency on an artifact (ex. hosted on Maven Central), then configure dependencies just as you normally would in a Gradle based project:
buildscript {
repositories {
mavenCentral()
}
}
dependencies {
compile("org.scala-lang:scala-library:2.11.0")
}
The intellij.plugins entry point will add an artifact in the current project, as well as a <depends> tag to your plugin.xml file. To install an external plugin alongside your own, for example if you are using the Plugin Extensions feature (suppose the plugin is hosted on the JetBrains Plugin Repository), use the following snippet:
plugins {
id "org.jetbrains.intellij" version "0.2.13"
}
intellij {
//...
plugins "org.intellij.scala:2017.2.638"
}

IntelliJ IDEA gradle project setup

What is the best procedure for beginning a new Gradle project in IntelliJ IDEA 12.1.3?
I created a skeleton build.gradle file with the idea plugin to generate the project files:
apply plugin: 'java'
apply plugin: 'idea'
task wrapper(type: Wrapper) {
gradleVersion = '1.6'
}
When I open the new project IntelliJ complains that the Groovy SDK is not configured for module:
After configuring the Groovy SDK I get Java inspection issues in the build file:
Am I doing something wrong or is this normal for a Gradle based project in IDEA? Should I just disable inspection?
UPDATE
I was able to import a skeleton project and IDEA automatically configured everything properly. However I am still receiving Java inspection errors in build.gradle as shown above.
If you use Gradle's idea task to generate project files, this is normal, as there is no way to tell IDEA what the class path of the build script itself is. If you instead use IDEA's Gradle integration ("Import from Gradle model"), this problem doesn't exist.
This question was asked anout a year ago for IDEA 12, and at that time the Gradle support was still... let's say "in progress". In the meantime IDEA 13 is out, and the Gradle support plugin has improved dramatically. See this (short) video for the current capabilities: https://m.youtube.com/watch?v=3Euo6xzCwY4 It isn't a stupid menu walkthrough, it is a concrete example.