Expressing Java Annotation Class with Members in Kotlin - java

I'm new to Kotlin and currently using v1.3.31 in an OSGI bundle development POC. I'm currently using annotation processors to generate the OSGI manifest declarations and I am trying to find the Kotlin equivalent of the following:
#ObjectClassDefinition(name="Config", description = "Sample Config")
public static #interface Config {
#AttributeDefinition(name = "A parameter", description = "Configurable param")
String myParameter() default "";
}
Given that these OSGI annotations are created as:
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.TYPE)
public #interface ObjectClassDefinition
/*....*/
#Retention(RetentionPolicy.CLASS)
#Target(ElementType.METHOD)
public #interface AttributeDefinition
The closest I can get in Kotlin is using the #ObjectClassDefinition annotation on a public annotation class but since Kotlin annotation classes do not support members, I cannot use the #AttributeDefinition on a class member.
I've tried to only use a Kotlin class or interface but the annotation processor will not allow any non-annotation class.
I've also tried to create a java.lang.Annotation in Kotlin to bypass this behavior, but the compiler will not allow it.
Current partially working implementation and here's is the latest working GitHub source.:
#ObjectClassDefinition(name = "Sample Kotlin servlet",
description = "Simple Kotlin servlet with configurable properties")
public annotation class Config (
val value : String = "hello"
)
I've noticed that annotations can be particularly tricky in Kotlin. Any insight is greatly appreciated!

#ObjectClassDefinition(
name = "Sample Kotlin servlet",
description = "Simple Kotlin servlet with configurable properties")
annotation class Config (
#get:AttributeDefinition(name = "A parameter", description = "Configurable param")
val value : String = "hello")
("public" does not seem to have any effect).
See this documentation for details annotation-use-site-targets

Related

MapStruct Custom EnumTransformationStrategy

I am a bit confused looking at the documentation on how to use a custom transformation strategy for enums. I am doing this in Kotlin, but a Java solution is fine too.
I am using 14.2
I have this
package org.mapstruct.custom.spi
import org.mapstruct.ap.spi.EnumTransformationStrategy
class StripLowerCaseEnumTransformationStrategy : EnumTransformationStrategy {
companion object {
const val NAME = "stripLowerCase"
}
override fun getStrategyName(): String {
return NAME
}
override fun transform(value: String, configuration: String): String {
return value.removePrefix(configuration).toLowerCase()
}
}
But when I try to use it within my EnumMappers, it says it cannot be found. What step am I missing?
#Mapper
interface EnumMapper {
#ValueMappings(
ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL),
ValueMapping(source = "PERSON_STATUS_INVALID", target = MappingConstants.NULL)
)
#EnumMapping(
nameTransformationStrategy = StripLowerCaseEnumTransformationStrategy.NAME,
configuration = "PERSON_STATUS_"
)
fun fromProto(status: PersonStatusProto): PersonStatus
#InheritInverseConfiguration
fun toProto(status: PersonStatus): PersonStatusProto
}
Here is the error: error: There is no registered EnumTransformationStrategy for 'stripLowerCase'. Registered strategies are: prefix, stripPrefix, stripSuffix, suffix.
I also have created the file resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy with the value org.mapstruct.custom.spi.StripLowerCaseEnumTransformationStrategy.
Here is also a picture of directory in case it is organized incorrectly:
The reason why the configured EnumTransformationStrategy is not used is because SPIs which are available only in the same compilation unit are not used.
In order for this to work you will need to move the implementation in a different module (not package) and make it available during the compilationl
An example for how this can be done with maven can be seen in this integration test.
Note: I see that you are placing the StripLowerCaseEnumTransformationStrategy under the org.mapstruct package. I would advise not to use packages that you don't own for placing your code, otherwise you might have problems with split packages when running on the Java module path.

junit 5 custom parametrized tests

I'm using Junit 5 parametrized tests with custom name as follow
#ParameterizedTest(name = PARAMETERIZED_TESTS_NAME_PLACEHOLDER)
where PARAMETERIZED_TESTS_NAME_PLACEHOLDER is defined in its own utility class
public static final String PARAMETERIZED_TESTS_NAME_PLACEHOLDER = "#{index} [{argumentsWithNames}]";
the problem I'm facing is that as I'm using extensively the parametrized tests, my code is cluttered by these #ParameterizedTest(name = PARAMETERIZED_TESTS_NAME_PLACEHOLDER).
so I created a custom annotation to fix this
import java.lang.annotation.*;
import org.junit.jupiter.params.*;
#ParameterizedTest(name = PARAMETERIZED_TESTS_NAME_PLACEHOLDER)
#Inherited
public #interface CustomParametrizedTest {
}
but this annotation is ignored when I use it in the test cases
any help would be appreciated
The #ParamterizedTest annotation appears to have a retention policy of runtime suggesting it's needed and processed at runtime. Try this config
#ParameterizedTest(name = PARAMETERIZED_TESTS_NAME_PLACEHOLDER)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface CustomParametrizedTest {
}
It seems odd to me that this is not the default retention policy for custom annotations, see more from this post.

Spring Boot: How to include a configuration class that is not in my base package?

I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.

Converting package level Java annotation to Kotlin

Is it possible to convert a package level Java annotation to Kotlin?
Java annotation (MyAnnotation.java):
package com.myexample.annotation;
#Retention(RUNTIME) #Target(PACKAGE)
public #interface MyAnnotation {
}
Application of annotation (package-info.java)
#MyAnnotation
package com.myexample
The following does not seem to work (although it does compile) - my annotation processor does not detected any of the classes in the package com.myexample:
MyAnnotation.kt
package com.myexample.annotation
#Target(allowedTargets = [AnnotationTarget.CLASS, AnnotationTarget.FILE])
#Retention(AnnotationRetention.SOURCE)
annotation class MyAnnotation
package-info.kt
#file:MyAnnotation
package com.myexample
import com.myexample.annotation.MyAnnotation
No, it's not currently possible. You can simply leave package-info.java as a Java file.

How annotation internally works in java

I know how to create custom annotation. But i am unable to understand how does it work internally. If i take example of spring annontation.
#PropertySource(value = { "classpath:database.properties" }).
if we see internal details of #PropertySource annotation
#Target({ java.lang.annotation.ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Repeatable(PropertySources.class)
public #interface PropertySource {
public abstract String name();
public abstract String[] value();
public abstract boolean ignoreResourceNotFound();
}
We do not have provided any implementation here for loading property file.
Then How is it loading property file from classpath. Who is working behind the scene ?
Really simple: framework. That is. All 'custom' annotations processed by frameworks using reflection. Only small scope of annotations are processed by compiler, such as #Override, #SuppressWarnings, #Retention and so on

Categories

Resources