Lombok getters/setters are not visible from my annotation processor - java

I did custom implementation of javax.annotation.processing.AbstractProcessor and it work.
But my processor do not found getters, setters and constructors which generated by Lombok.
Here my propceesor(do I need to create minimal example?):
https://github.com/hohserg1/ElegantNetworking/blob/1.12.2-annotation-processor/src/main/java/hohserg/elegant/networking/annotation/processor/ElegantPacketProcessor.java#L62
Example class:
ElegantPacket //my
#Value //lombok
public class Test implements ClientToServerPacket {
int some; //it visible
//int getSome() //generated by Lombok, it invisible
//public Test(int some) //generated by Lombok, it invisible
}

If you wish to run both Lombok and another annotation processor, then you should delombok your code and run your annotation processor on the result.
This is what the Checker Framework Gradle Plugin does, as explained in the Checker Framework Manual.
Explanation:
Most annotation processors either produce output (say, issue warnings) or generate new classes. Lombok is an annotation processor that modifies existing code. It does so by accessing internal APIs of the javac compiler (it also supports eclipsec). These manipulations cause javac to emit bytecode that contains Lombok's changes to your classes. However, those changes are invisible to earlier phases of the compiler, notably your annotation processor. Another way of saying all this is that Lombok does not play well with other annotation processors.

Ok, I solve this by using annotationProcessor of gradle dependency configuration:
dependencies {
//gradle 4.6+
annotationProcessor 'org.projectlombok:lombok:1.18.8', "io.gitlab.hohserg.elegant.networking:annotation-processor:2.8"
...
}
Also not all lombok changes visible from my annotation processor still. Changes of fields access modifiers is not visible, but it can be determine from lombok annotations. As example, #Value makes package-private fields to private.
Also plugin apt may be used on gradle less that 4.6
buildscript {
repositories {
...
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
...
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.9'
}
}
dependencies {
apt 'org.projectlombok:lombok:1.18.8', "io.gitlab.hohserg.elegant.networking:annotation-processor:2.7"
...
}

Related

Add an annotation to a class from a place other than class definition

I need to add an annotation to a class which is in a sperate dependency (in a separate jar) of the project that I am working on. I am wondering that can I do this in Java?
As an example, I need to add an annotation HisClass without touching the source code of HisClass.
public class HisClass{
// ...
}
So, that the above class should be look like below at the build time,
#MyAnnot ( me = MyClass.class )
public class HisClass{
// ...
}
There are many ways:
Write a compiler plugin for javac, and make it add the annotations. This will be quite difficult, as the plugin API has nearly no documentation.
2.(Maybe not possible) Inject the annotation after compiling. Add an extra buildstep after compiling and use a library like ASM to add this annotation to the classfile.

Is there anyway in Idea to recognize a getter/setter generated by lombok in the same module

So looking at my class files the getters/setters are being generated just fine, but I'm trying to write a copy method that looks like this in the same jar.
#Data
public class SoftwareVersions {
private String applicationVersion;
void copyTo( MonitorFoleyConnection mfc ) {
mfc.setApplicationVersion( applicationVersion );
}
}
in gradle
annotationProcessor("org.projectlombok:lombok:1.+")
compileOnly("org.projectlombok:lombok:1.+")
is it possible to get intellij to recognize the existance of this method?
Yes, you have. You just need to do two things:
Install the Lombok Plugin for Intellij:
Enable the annotation processing:

Post-compile weaving aspects into a project using Gradle

Background
Performing post-compile weaving of projects using:
AspectJ 1.9.4
io.freefair.aspectj.post-compile-weaving 4.1.1
Java 11.0.3
Gradle 5.6.2 (Groovy 2.5.4, Kotlin 1.3.41)
This project does not use Maven or Spring.
Layout
The projects include:
app.aspects - Contains a single LogAspect class annotated with #Aspect.
app.aspects.weaver - No source files, only dependencies to declare aspects and project to weave.
app.common - Defines #Log annotation referenced by pointcuts described in LogAspect.
app.program.main - Files to be woven with jointpoints described in LogAspect.
Gradle
Build files that relate to aspects are defined here. The idea is that weaving is independent from the application so neither the application's common classes nor the main program need know about weaving. Rather, the main program need only reference #Log from the common package and AJC will take care of the weaving.
app.aspects
apply plugin: "io.freefair.aspectj.post-compile-weaving"
dependencies {
// For the #Log annotation
compileOnly project(':app.common')
// The LogAspect's joinpoint references the Main Program
compileOnly project(':app.program.main')
// Logging dependency is also compiled, but not shown here
}
app.aspects.weaver
apply plugin: "io.freefair.aspectj.post-compile-weaving"
dependencies {
compileOnly "org.aspectj:aspectjrt:1.9.4"
// This should set the -aspectpath ?
aspect project(":app.aspects")
// This should set the -inpath ?
inpath(project(":app.program.main")) {
// Only weave within the project
transitive = false
}
}
Classes
Log
The Log annotation is straightforward:
package com.app.common.aspects;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR })
public #interface Log {
boolean secure() default false;
}
Main Program
The main program resembles:
package com.app.program.main;
import com.app.common.aspects.Log;
#Log
public class Program {
/** This is the method to weave. */
protected void run() throws InterruptedException, TimeoutException {
}
}
Logging Aspect
The logging aspect resembles (see the code from a related question):
#Aspect
public class LogAspect {
// In the future this will target points annotated with #Log
#Pointcut("execution(* com.app.program.main.Program.run(..))")
public void loggedClass() {
}
#Around("loggedClass()")
public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
return log(joinPoint, false);
}
private Object log(final ProceedingJoinPoint joinPoint, boolean secure) throws Throwable {
// See last year's code for the full listing
log.info("\u21B7| {}{}#{}({})", indent, className, memberName, params);
}
}
Problem
It appears weaving is taking place, but the advice cannot be found:
.../app.aspects/build/classes/java/main!com/app/aspects/LogAspect.class [warning] advice defined in com.app.aspects.LogAspect has not been applied [Xlint:adviceDidNotMatch]
Question
What needs to change so that weaving of the LogAspect into Program's run() method works using Gradle?
Options File
The ajc.options file shows:
-inpath
.../app.aspects/build/classes/java/main
-classpath
.../.gradle/caches/modules-2/files-2.1/org.aspectj/...
-d
.../app.aspects/build/classes/java/main
-target
11
-source
11
It is disconcerting that -aspectpath isn't shown and -inpath is listing app.aspects instead of app.program.main.
Merging apps.aspects and apps.aspects.weaver into the same project has produced:
Join point 'method-execution(void com.app.program.main.Program.run())' in Type 'com.app.program.main.Program' (Program.java:396) advised by around advice from 'com.app.aspects.LogAspect' (LogAspect.class(from LogAspect.java))
While this solves the problem, I don't understand why LogAspect needs to be in the same project that performs the weaving. The Gradle file becomes:
apply plugin: "io.freefair.aspectj.post-compile-weaving"
dependencies {
compileOnly "org.aspectj:aspectjrt:1.9.4"
compileOnly project(':app.common')
compileOnly project(':app.program.main')
compileOnly org_apache_logging_log4j__log4j_api
inpath(project(":app.program.main")) {
transitive = false
}
}
compileJava.ajc.options.compilerArgs += "-showWeaveInfo"
compileJava.ajc.options.compilerArgs += "-verbose"

Writing custom Lombok Annotation handlers

I want to write custom Lombok Annotation handlers. I know http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html. But the current lombok jar file does not contain many .class files, but files named .SCL.lombok instead.
I found, the .SCL.lombok files are the .class files, the build script of Lombok does rename them while generating the jar file, and the ShadowClassLoader is capable of loading these classes -- and the acronym SCL seems to come from this. It seems the reason for this is just to "Avoid contaminating the namespace of any project using an SCL-based jar. Autocompleters in IDEs will NOT suggest anything other than actual public API."
I was only able to compile my custom handler by
unpacking the contents of the lombok.jar
renaming the .SCL.lombok files to .class
adding the resulting directory to the compile classpath
In addition, to be able to use my custom handler, I needed to create a new fat jar containing both the lombok classes and my custom handler. The custom lombok class loader essentially prevents adding custom handlers in other multiple jars.
Is this the only way to extend Lombok? Or am I missing something?
I am using the following buildscript
apply plugin: 'java'
repositories {
jcenter()
}
configurations {
lombok
compileOnly
}
def unpackedAndRenamedLombokDir = file("$buildDir/lombok")
task unpackAndRenameLombok {
inputs.files configurations.lombok
outputs.dir unpackedAndRenamedLombokDir
doFirst {
mkdir unpackedAndRenamedLombokDir
delete unpackedAndRenamedLombokDir.listFiles()
}
doLast {
copy {
from zipTree(configurations.lombok.singleFile)
into unpackedAndRenamedLombokDir
rename "(.*)[.]SCL[.]lombok", '$1.class'
}
}
}
sourceSets {
main {
compileClasspath += configurations.compileOnly
output.dir(unpackedAndRenamedLombokDir, builtBy: unpackAndRenameLombok)
}
}
tasks.compileJava {
dependsOn unpackAndRenameLombok
}
dependencies {
compile files("${System.properties['java.home']}/../lib/tools.jar")
compile "org.eclipse.jdt:org.eclipse.jdt.core:3.10.0"
compile 'javax.inject:javax.inject:1'
lombok 'org.projectlombok:lombok:1.16.6'
compileOnly files(unpackedAndRenamedLombokDir)
}
In the meantime Reinier Zwitserloot created a new git-branch sclExpansionUpdate, that contains an updated version of the ShadowClassLoader:
ShadowClassLoader is now friendlier to trying to extend lombok.
Your (separate) jar/dir should have a file named
META-INF/ShadowClassLoader. This file should contain the string
'lombok'. If you have that, any classes in that jar/dir will be loaded
in the same space as lombok classes. You can also rename the class
files to .SCL.lombok to avoid other loaders from finding them.
I guess this did not yet make it into the main branch because it certainly has not been tested that much - I just tried it out for myself and it contains a little bug that prevents loading the required META-INF/services from extensions. To fix it you should replace two method calls to partOfShadow with inOwnBase:
[... line 443]
Enumeration<URL> sec = super.getResources(name);
while (sec.hasMoreElements()) {
URL item = sec.nextElement();
if (!inOwnBase(item, name)) vector.add(item); // <<-- HERE
}
if (altName != null) {
Enumeration<URL> tern = super.getResources(altName);
while (tern.hasMoreElements()) {
URL item = tern.nextElement();
if (!inOwnBase(item, altName)) vector.add(item); // <<-- AND HERE
}
}
I tested it with the above fix and it seems to work fine (not tested much though).
On a side note: with this new extension mechanism, it is now finally also possible to have the extensions annotation handlers and annotations in a different namespace than "lombok" - nice!
Using the input from this question and from the other answer (by Balder), we managed to put together a custom Lombok annotation handler: Symbok. Feel free to use that as a sample for writing your own.
BTW, instead of writing a custom Lombok handler, you could also implement a javac plugin instead -- it might be simpler.

Is there any way to mock JNI methods in my android project to do Junit testing?

Hi I need to write Junit tests for an Android project but it has JNI methods as it uses webkit.Is there any way I can test those android methods(I dont want to test JNI methods).
Its like:
public void androidMethod(){
//some android code
nativeInit(); //how do I mock such methods?
//some code again
}
I have tried powermock,easymock,roboelectric but wasnt successful.Please help me.
I yesterday found I could solve this with Mockito (I didn't try powermock or easymock). Assuming your class is class C, my solution is:
C c=spy(new C);
doNothing().when(c).nativeInit();
c.androidMethod()
verify(c).nativeInit();
This does, of course, require that nativeInit is visible to the test.
Similar Problem
I had the same problem event though I was already using mockito in JUnit tests under src/test. Once I added tests under src/androidTest I started having issues, including this crash:
Mockito cannot mock/spy because :
- final class
And after making the class open, manually, I still got crashes in the JNI layer as it tried to load the *.so library (which wouldn't happen if mocks were working properly).
Working Solution
Instead, what I had to do was open the class for testing purposes using Kotlin's all-open plugin. The process is also explained well in this recent medium post but it boils down to the following four simple changes that are also modeled in one of the architecture components sample apps:
1. Make these additions to build.gradle:
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:${versions.kotlin}"
}
}
apply plugin: "kotlin-allopen"
allOpen {
// marker for classes that we want to be able to extend in debug builds
annotation 'com.your.package.name.OpenClass'
}
2. Add the corresponding annotations in the debug flavor. For example: app/src/debug/java/com/your/package/name/OpenForTesting.kt
package com.your.package.name
#Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass
#OpenClass
#Target(AnnotationTarget.CLASS)
annotation class OpenForTesting
3. Add the corresponding annotation in the release flavor. For example: app/src/release/java/com/your/package/name/OpenForTesting.kt
package com.your.package.name
#Target(AnnotationTarget.CLASS)
annotation class OpenForTesting
4. Add the #OpenForTesting annotation to the class that needs to be mocked
package com.your.package.name
#OpenForTesting
class JniClassOfVictory {
...
external fun nativeInit()
...
companion object {
init {
System.loadLibrary("victoryeveryday")
}
}
}
The result is a flexible way to mark classes as open without actually making them open in release builds. Of course, this is because the #OpenForTesting annotation that we created in release is not marked with #OpenClass but the same annotation in debug is marked with #OpenClass. In build.gradle we designated that annotation as the signal to the kotlin-allopen plugin. So any class annotated with #OpenForTesting will be made open at compile-time but only on Debug builds.

Categories

Resources