I have a Kotlin Gradle project. I added Lombok as a dependency and also registered it with kapt
compileOnly("org.projectlombok:lombok:$lombokVersion")
kapt("org.projectlombok:lombok:$lombokVersion")
I would like to use the #Slf4j annotation for automatic logger generation. It works for Java classes but not for the Kotlin ones.
Is using Kotlin and Lombok together even possible as of now? If I annotate a Kotlin class with #Slf4j and use log inside it I get
Unresolved reference: log
Evidently no annotation processing is applied.
Lombok does not run on your source code, but on the AST. Anyway, it is an annotation processor that is run at compile-time by the Java compiler. The Kotlin compiler does not use these annotation processors. See also the answer https://stackoverflow.com/a/35530223/2621917 straight from the horse’s mouth.
You cannot use annotation #Slf4j, but manually create its object in the class required.
Refer https://www.reddit.com/r/Kotlin/comments/8gbiul/slf4j_loggers_in_3_ways/
If all you want to use Lombok for is #Slf4j, then I'd suggest using kotlin-logging instead: https://github.com/MicroUtils/kotlin-logging
It's a simple wrapper around slf4j, so instead of annotating your class with #Slf4j, you use:
// Place definition above class declaration to make field static
private val logger = KotlinLogging.logger {}
// ...
logger.debug { "A message only logged if debug is enabled. With $variable support." }
Lombok's builder annotation support has been added to kotlin 1.8 as of late December 2022.
You can learn how to configure the plugin here.
In Short, add
plugins {
id 'org.jetbrains.kotlin.plugin.lombok' version '1.8.0'
id 'io.freefair.lombok' version '5.3.0'
}
to your Groovy/Gradle files, and/or take a look at the sample project.
It's not supported and, by the looks of things, it isn't going to be.
from kotlin 1.7.20 with K2 compiler it is possible.
https://kotlinlang.org/docs/whatsnew1720.html#support-for-kotlin-k2-compiler-plugins
For logging the best I could do - because #Slf4j did not work - was like creating abstract log class like:
package org.example
import org.slf4j.LoggerFactory
import org.slf4j.Logger
abstract class Log {
val log: Logger = LoggerFactory.getLogger(this.javaClass)
}
and usage:
package org.example
class MyClass {
companion object : Log() {}
#Test
fun someFun() {
log.info("Logging info")
}
}
I can't see how it would work without additional support from the lombok team.
Lombok is based on annotation processing so it runs during compilation time and runs on your source code, so I guess it assumes Java's syntax.
Related
I am having below class
class TestClientSpec extends Specification {
TestClient testClient
RestTemplate restTemplate
def setup() {
restTemplate = Mock()
testClient = new TestClient(restTemplate)
}
def 'successful'() {
given:
def url = 'https://test123'
when:
testClient.getValue(url)
then:
1 * restTemplate.getForEntity(url, Test.class)
}
}
when I try to run this test got below error
Cannot create mock for class org.springframework.web.client.RestTemplate. Mocking of non-interface types requires a code generation library. Please put an up-to-date version of byte-buddy or cglib-nodep on the class path.
org.spockframework.mock.CannotCreateMockException: Cannot create mock for class org.springframework.web.client.RestTemplate. Mocking of non-interface types requires a code generation library. Please put an up-to-date version of byte-buddy or cglib-nodep on the class path.
After that I have added cglib dependnecy and it's working fine
implementation group: 'cglib', name: 'cglib-nodep', version: '3.3.0'
But not sure why it's throws the above error and how cglib fixing this?
Spock uses a bytecode generator lib, either cglib or byte-buddy, to generate classes at runtime in order to be able to mock classes.
The dependencies on these libs are optional, so that you can choose whether to use them or not, as you might as well use some dedicated mock libraries for all your mocking needs (e.g. PowerMock).
How adding a bytecode lib to the classpath can fix this?
Well, by enabling the necessary code in Spock... in case of Cglib, CglibMockFactory.
Spock seems to find out which lib is available to use for mocking at MockInstantiator... if you know Java reflection, doing that kind of thing is not hard, just do something like Class.forName("org.objenesis.Objenesis") and if that doesn't throw ClassNotFoundException you can use that.
According to the documentation, Lombok has 3 annotations for constructor generation:
#NoArgsConstructor - generates an empty constructor;
#AllArgsConstructor - generates a constructor that initializes all
fields;
#RequiredArgsConstructor - generates a constructor that
initializes only final fields.
They all have an onConstructor property that allows you to specify the annotations with which the generated constructor should be marked.
According to the Javadoc, the syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
Up to JDK7:
#NoArgsConstructor(onConstructor=#__({#AnnotationsGoHere}))
From JDK8:
#NoArgsConstructor(onConstructor_={#AnnotationsGohere}) // note the underscore after onConstructor
I am working on JDK8. However, only the JDK7 variant works for me, while the JDK8 variant does not work (a constructor without annotations is generated).
I checked on JDK11 - same result.
I check with Refactor -> Delombok -> #Constructors.
For example, like this:
#AllArgsConstructor(onConstructor = #__(#Deprecated))
public class SomeClass {
}
the following code is generated:
public class SomeClass {
#Deprecated
public SomeClass() {
}
}
But like so:
#AllArgsConstructor(onConstructor_ = #Deprecated)
public class SomeClass {
}
code like this is generated:
public class SomeClass {
public SomeClass() {
}
}
I noticed that the documentation on the Lombok site only contains a JDK7 style example.
The Javadoc is incorrect or am I doing something wrong?
I found, that it's not the Lombok's bug, it's Lombok IntelliJ plugin bug.
Constructor annotations add in compiled code.
Delombok tool of Lombok IntelliJ plugin incorrect convert Lombok's annotations to vanilla Java code.
So I'm trying to get the Hibernate Validator Annotation Processor working in a Kotlin project, to check my JSR 380 annotations, with not much luck.
Unfortunately the documentation does not mention how to set it up with Gradle, and obviously with Kotlin we have to use "Kapt" to enable java annotation processors.
Hibernate Validator Annotation Processor Documentation: http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#validator-annotation-processor
Kapt Documentation: https://kotlinlang.org/docs/reference/kapt.html
I currently have the following config in my build.gradle file relating to the processor:
plugins {
id "org.jetbrains.kotlin.kapt" version "1.3.11"
...
}
apply plugin: 'org.jetbrains.kotlin.kapt'
...
dependencies {
implementation 'org.hibernate:hibernate-validator:6.0.14.Final'
implementation 'org.glassfish:javax.el:3.0.1-b09'
kapt 'org.hibernate:hibernate-validator-annotation-processor:6.0.14.Final'
...
}
kapt {
arguments {
arg('methodConstraintsSupported', 'false')
arg('verbose', 'true')
}
}
However whenever I build, I cannot see any output relating to the validator annotation processor and I do not get any build errors when deliberately applying an incorrect annotation (e.g. applying a #Min() annotation to a String field.
If someone could advise on how to get the processor working I would be eternally grateful! :)
I got this working in my build.gradle.kts like so (I'm using Kotlin Script as opposed to Groovy):
plugins {
...
id("org.jetbrains.kotlin.kapt") version "1.3.72"
...
}
dependencies {
...
kapt(
group = "org.hibernate.validator",
name = "hibernate-validator-annotation-processor",
version = "6.0.2.Final"
)
...
}
This correctly gave me errors when building, but only when I applied the validation annotation to the getter. When I was mistakenly applying it to just the constructor argument the validation did not work, and I saw no errors from the annotation processor. For example:
class Thing(
#get:AssertTrue
var name: String
)
I am working with SpringData's Neo4j graph DB hello-worlds example and I ran across the following code in WorldRepositoriesImpl.java...
#Autowired private WorldRepository worldRepository;
Furthermore, WorldRepository is defined as...
public interface WorldRepository extends MyWorldRepository,
GraphRepository<World>,
NamedIndexRepository<World>
{/* no method defined here */}
Now the odd part, no class that I can find actually implements WorldRepository.So, a few questions...
How is this possible? Where is this documented? Is there a way to make this a bit more explicit (less mysterious)?
Running the code with a debugger attached shows that the worldRepository instance wired up by Spring is a proxy object created at runtime.
Looking at the pom.xml and the dependencies included, it looks like the spring-neo4j library bundles in some Aspects that create this implementation class at runtime.
In other words, there is no implementation of this interface declared in the source code - but one is created at runtime with AspectJ and other tools.
If I create a custom annotation like this:
public #interface TODO
{
String msg();
String start_date();
}
then a method:
#TODO
(
msg="will be developed!",
start_date="05/01/2010"
)
public static void Calculator()
{
}
after I call it:
Calculator();
If I wanted that the compiler warn me about it how could I do that?
There was a similar question, some weeks ago. Here is the link to both the question and my answer.
You can easily adapt this code to your needs.
You must write an annotation processor and invoke apt to run it on your code.
Use the Annotation Processing Tool (apt) to make your own AnnotationProcessor and print the message with javax.annotation.processing.Messager
If you are using an IDE, there are plenty of good options. For Eclipse:
use the built-in plug-in which locates all TODO, FIXME, etc. words in your code and puts them in a special view.
register your own custom builder which can show you the warnings