Enum annotations in Kotlin - enums

I have an enumeration that is serialized/deserialized by Gson:
enum class PacketType {
NONE;
[SerializedName("request")]
REQUEST;
[SerializedName("response")]
RESPONSE;
[SerializedName("event")]
EVENT;
}
Unfortunately, I noticed that Gson ignores SerializedName annotations and uses upper case names for enum values. I decided to find out why serialization doesn't work as intended and found out that Kotlin drops all annotations for enum values. How can I make these annotations appear in generated bytecode?

Looks like a bug to me. Please report to the issue tracker.
As a temporary workaround, you can write this class in Java

The issue is now fixed, your code now works fine in Kotlin M9 (0.9.66). If you upgrade to that it'll work as you expect.
e.g.
app build.gradle
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.jetbrains.kotlin:kotlin-stdlib:0.9.66'
compile 'com.google.code.gson:gson:2.3'
}
top-level build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.2'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.9.+'
}
}
I confirmed this by making an enum with no relation between the enum names and SerializedName names, and it worked as expected.

If you need to use enum as #Query param in Retrofit you can override toString():
override fun toString(): String {
try {
val annotations = javaClass.getField(name).annotations
for (annotation in annotations) {
if (annotation is SerializedName) {
return annotation.value
}
}
} catch (e: NoSuchFieldException) {
throw RuntimeException(e)
}
return super.toString()
}

Related

Gradle 7: how to get list of implementation dependencies (including sub) in a task?

I need to have full list of dependencies necessary to run a project (so subdependencies are also important!).
task generateLibsDescriptor() {
doFirst {
configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each {
println it
}
}
}
This code works, but there are also compileOnly dependencies listed. I tried to change compileClasspath to implementation, but had an error Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'.
Is it possible to have a list of just implementation dependencies (with subdependencies)?
Configuration compileClasspath extends compileOnly and implementation. New config should be created which extends only implementation but resolvable.
configurations {
resolvableImpl.extendsFrom(implementation)
resolvableImpl.canBeResolved(true)
}
task generateLibsDescriptor() {
doFirst {
configurations.resolvableImpl.resolvedConfiguration.resolvedArtifacts.each {
println it
}
}
}

QueryDSL annotation processor and gradle plugin

Cannot understand how to configure build.gradle for using querydsl annotation processor without any jpa/jdo/mongo. I want to use #QueryEntity annotation to generate Q classes so then I will be able to compose dynamic SQL queries using DSL support then convert query to plain text and provide it to Spring R2DBC DatabaseClient executor.
Is there a way what gradle querydsl apt plugin and querydsl annotation processor to use for generating Q classes with #QueryEntity annotations in build.gradle file?
I'm using gradle 5, Spring Data R2DBC, Spring Boot, plan to integrate queryDsl with annotation processsor.
That's my currect build.gradle:
plugins {
id 'java'
id 'org.springframework.boot' version '2.2.1.RELEASE'
id "com.ewerk.gradle.plugins.querydsl" version "1.0.8"
}
apply plugin: 'io.spring.dependency-management'
group = 'com.whatever'
repositories {
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
}
ext {
springR2dbcVersion = '1.0.0.RELEASE'
queryDslVersion = '4.2.2'
}
dependencies {
implementation("com.querydsl:querydsl-sql:${queryDslVersion}")
implementation("com.querydsl:querydsl-apt:${queryDslVersion}")
implementation('org.springframework.boot:spring-boot-starter-webflux')
compileOnly('org.projectlombok:lombok')
annotationProcessor('org.projectlombok:lombok')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}")
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation('io.projectreactor:reactor-test')
}
test {
useJUnitPlatform()
}
Generally speaking, you shouldn't use the QueryDSL plugin.
In order to configure QueryDSL generation you just need the relevant querydsl module, the annotation processors and the generated source dir. For instance, with lombok integration, this configuration should work (you might need to play with the exact QueryDSL modules you need):
buildscript {
ext {
springBootVersion = '${springBootVersion}'
queryDslVersion = '4.2.2'
javaxVersion = '1.3.2'
}
}
plugins {
id 'idea'
}
idea {
module {
sourceDirs += file('generated/')
generatedSourceDirs += file('generated/')
}
}
dependencies {
// QueryDSL
compile "com.querydsl:querydsl-sql:${queryDslVersion}"
annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:general")
// Lombok
compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
implementation("org.projectlombok:lombok:${lombokVersion}")
// Possibly annotation processors for additional Data annotations
annotationProcessor("javax.annotation:javax.annotation-api:${javaxVersion}")
/* TEST */
// Querydsl
testCompile "com.querydsl:querydsl-sql:${queryDslVersion}"
testAnnotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:general")
// Lombok
testImplementation("org.projectlombok:lombok:${lombokVersion}")
testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}")
testCompileOnly("org.projectlombok:lombok:${lombokVersion}")
}
Additional information: https://github.com/querydsl/querydsl/issues/2444#issuecomment-489538997
I want to leave this answer here as I struggled for several hours finding a solution that works for Kotlin (The question doesn't have a Java restriction as far as I can tell and there is really little information around for this). Kotlin supports annotation processing with the kapt plugin. In order to use this plugin for QueryDSL you need to 1. define the kapt plugin, and 2. specify the dependency that will do the processing, in this case com.querydsl:querydsl-apt. I used the general task for my plugin execution, but according to the documentation there are other options available (probably this can be useful for JPA, JDO, Hiberante with some extra tweaks)
plugins {
kotlin("kapt") version "1.4.10"
}
dependencies {
implementation("com.querydsl:querydsl-mongodb:4.4.0")
kapt("com.querydsl:querydsl-apt:4.4.0:general")
}
Now, whenever you run gradle build the annotation processing will trigger the DSL query class generation for your classes annotated with #QueryEntity. I hope it helps in case someone needs this for Kotlin.
This worked for me (Please follow the exact same order in the dependency)
sourceSets {
generated {
java {
srcDirs = ['build/generated/sources/annotationProcessor/java/main']
}
}
}
dependencies {
api 'com.querydsl:querydsl-jpa:4.4.0'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor('com.querydsl:querydsl-apt:4.4.0:jpa')
annotationProcessor('javax.annotation:javax.annotation-api')
}
This works!!!
ext {
queryDslVersion = '4.2.1'
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', 'build/generated/sources/annotationProcessor/java/main']
}
}
}
dependencies {
compile("com.querydsl:querydsl-core:${queryDslVersion}")
compile("com.querydsl:querydsl-jpa:${queryDslVersion}")
}
dependencies {
compile "com.querydsl:querydsl-jpa:${queryDslVersion}"
compileOnly 'org.projectlombok:lombok:1.16.18'
annotationProcessor(
"com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
"javax.annotation:javax.annotation-api:1.3.2",
"org.projectlombok:lombok"
)
for sinle gradle project just add next lines to the same build.gradle
for multi module gradle project add next lines to build.gradle of module where are jpa entities:
implementation("com.querydsl:querydsl-core:${queryDslVersion}")
annotationProcessor(
"com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
"javax.annotation:javax.annotation-api:1.3.2")
and next line to build.gradle of module where are jpa repositories:
implementation("com.querydsl:querydsl-jpa:${queryDslVersion}")

NoSuchMethodException QueryDSL with Spring Boot & Spring Data Mongo

I am trying to implement Query DSL on my Spring Boot 2.0.4.RELEASE app that uses Spring Data Mongo 2.0.4.RELEASE & Gradle 4.10.
I am using Spring Tool Suite for running it locally.
Did the following steps which I found from multiple sources including Spring data documentation:
created gradle/querydsl.gradle which has below content to generate Q classes
apply plugin: "com.ewerk.gradle.plugins.querydsl"
sourceSets {
main {
java {
srcDir "$buildDir/generated/source/apt/main"
}
}
}
querydsl {
springDataMongo = true
querydslSourcesDir = "$buildDir/generated/source/apt/main"
}
dependencies {
compile "com.querydsl:querydsl-mongodb:4.1.4"
compileOnly "com.querydsl:querydsl-apt:4.1.4"
}
sourceSets.main.java.srcDirs = ['src/main/java']
Calling above gradle file from main build.gradle as shown below
buildscript {
ext { springBootVersion = "2.0.4.RELEASE" }
repositories { mavenCentral() }
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.9"
}
}
plugins {
id "java"
id "eclipse"
id "org.springframework.boot" version "2.0.4.RELEASE"
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
sourceCompatibility = 1.8
repositories { mavenCentral() }
dependencies {
...
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}")
...
}
apply from: 'gradle/querydsl.gradle'
/* Added this because Eclipse was not able to find generated classes */
sourceSets.main.java.srcDirs = ['build/generated/source/apt/main','src/main/java']
compileJava.dependsOn processResources
processResources.dependsOn cleanResources
After this updated the Repository annotated interface as below. Note: I also use Fragment Repository FragmentOrderRepository for some custom queries.
public interface OrderRepository<D extends OrderDAO>
extends EntityRepository<D>, PagingAndSortingRepository<D, String>, FragmentOrderRepository<D>, QuerydslPredicateExecutor<D> {}
Then in controller created a GET mapping as shown here
#RestController
public class OrderController {
#GetMapping(value="/orders/dsl", produces = { "application/json" })
public ResponseEntity<List> getOrdersDSL(#QuerydslPredicate(root = OrderDAO.class) Predicate predicate, Pageable pageable, #RequestParam final MultiValueMap<String, String> parameters) {
return (ResponseEntity<List>) orderService.getTools().getRepository().findAll(predicate, pageable);
}
}
Then in my runner class I added EnableSpringDataWebSupport annotation
#SpringBootApplication
#EnableSpringDataWebSupport
public class SampleApp {
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
}
With this my app starts up without any errors but when I try hitting the path http://localhost:5057/orders/dsl?email=test#test.com
I get a NoSuchMethodException with message No primary or default constructor found for interface com.querydsl.core.types.Predicate.
Can anyone please help with some pointers to solve this issue?
It seems that parameters are not getting resolved to a type.
---- UPDATE 09/19/19 ----
While debugging I found that a class HandlerMethodArgumentResolverComposite which finds ArgumentResolver for given MethodParameter from a List of argumentResolvers(of type HandlerMethodArgumentResolver). This list does not contain QuerydslPredicateArgumentResolver. Hence it is not able to resolve the arguments.
This means QuerydslWebConfiguration which adds above resolver for Predicate type is not getting called, which in turn indicates that some AutoConfiguration is not happening.
Probably I am missing some annotation here.
Found the mistake I was doing, was missing EnableWebMvc annotation on my Configuration annotated class.
Details are in this documentation.

Creating a fat jar using Kotlin and Gradle - compile vs implementation?

I'm tinkering with a simple "hello world" project in Kotlin & Gradle (see below). I've added the "fat jar" collection stuff to bring in the Kotlin dependency, but when I try to run java -jar build/libs/hello-1.0-SNAPSHOT.jar I get the java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics error because the dependencies aren't available at runtime.
I've solved that problem by changing implementation to compile, which makes everything work fine. But from what I understand, we shouldn't be using compile anymore, and neither api nor implementation makes the "fat jar" collection process work, and as I look at the other options for dependencies I'm not sure which to use.
Question: what's the "right" thing to do in a case like this?
// build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.41'
}
group 'com.example.test'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// if I change "implementation" to "compile", running the jar works
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
jar {
manifest {
attributes "Main-Class": "ApplicationKt"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
// Application.kt
fun main(args: Array<String>) {
println("hello world")
}
The compile dependency is deprecated. You should use implementation to declare your implementation dependencies, and compileClasspath to get all the compilation dependencies.

Gradle custom plugin : add dependency from extension object

I'm trying to write a plugin which adds dependencies to project.dependencies according to informations gathered in the plugin extension object. But it seems to be impossible.
Indeed, the data from extension object is only available in a new task or in project.afterEvaluate closure, but dependencies added in those places are ignored.
The following code tries to add the dependency in afterEvaluate but the dependency is ignored :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
project.afterEvaluate {
def version = project.myplugin.version
project.dependencies.add("compile", "org.foo:bar:$version") // --> ignored
}
}
}
class MyPluginExtension {
def version
}
In the following code the dependency injection works but I don't have access to the extension object :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
def version = project.myplugin.version // == null
project.dependencies.add("compile", "org.foo:bar:$version") // --> fail because $version is null
}
}
class MyPluginExtension {
def version
}
Is there a solution ?
Update: I managed to figure this out since my original answer. The way do this is to add a DependencyResolutionListener in which you add the dependencies and then remove the listener so it doesn't try to add them on later resolution steps.
compileDeps = project.getConfigurations().getByName("compile").getDependencies()
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileDeps.add(project.getDependencies().create("org.foo:bar:$version"))
project.getGradle().removeListener(this)
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {}
})
I have a working example of a plugin that uses this here
Original Answer:
This is also late but for anyone dropping in. With the latest gradle (2.6 at the time of writing), you can add a DependencyResolutionListener and add any dependencies before dependencies are resolved.
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
depsToAdd.each { dep ->
compileConfig.getDependencies()
.add(project.getDependencies().create(dep))
}
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
}
})
However, as of this writing I was having some issues getting this to work with Android Studio IDE. The problem is tracked in my question here
I originally implemented this solution using the DependencyResolutionListener approach by Saad. However, the listener itself is called only when something iterates over the configuration associated with the dependency. For example, if you want to dynamically add a dependency to compile, you have to make sure that something later on does something like:
project.configurations.compile.each {
...
}
But this is something that happens as a matter of course, since compile is a known configuration for any project that uses the java plugin. However, if you are using a custom configuration (as I was), then the listener approach won't work unless you explicitly iterate over your custom configuration.
I was able to find a better way to do this, and within afterEvaluate as the OP originally wanted. I'm using a custom configuration here, but I don't see a reason why it wouldn't work for compile either:
project.afterEvaluate {
def version = project.myPlugin.version
project.configurations.myConfig.dependencies.add(
project.dependencies.add("myConfig", "org.foo:bar:$version")
)
}
Of course, at some point something still has to iterate over the dependencies for them to actually get resolved.
The easiest way to do this:
project.dependencies {
delegate.compile("com.android.support:appcompat-v7:25.0.1")
}
Don't know if that's still relevant, but you can workaround this by explicitly adding your compile configuration to Java classpath in doFirst:
variant.javaCompile.doFirst {
variant.javaCompile.classpath += project.configurations.myconfiguration
}

Resources