First Post. Absolute Beginner. Be kind
I am playing arround with quarkus and kotlin.
I have this kotlin entity class:
#Entity
data class Fruit (
var name: String = "",
var description: String = ""
) : PanacheEntity()
I have this Resource Class based on tutorials in Java:
#Path("/fruits")
#ApplicationScoped
public class FruitJResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Fruit> getAll() {
return Fruit.listAll();
}
}
Everything fine here, Fruit inherits from PanacheEntityBase, i can access listAll()
However,
Same Class in Kotlin does not:
#Path("/fruits")
#ApplicationScoped
class FruitResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getAll(): List<Fruit> = Fruit.listAll()
}
Now i learned allready, that this is probably due kotlin not beeing able to inherit static methods from Super Class.
I read, that i should call the static method direct from the superclass, but this won't work here.
So I need a suggestion for a possible workaround.
The only solution for kotlin and scala languages for now (1.4.1) is to use the Repository pattern :
see documentation: https://quarkus.io/guides/hibernate-orm-panache#solution-2-using-the-repository-pattern
This is due to referenced issue github.com/quarkusio/quarkus/issues/4394.
So if using Kotlin, you simply have to define a new FruitRepository
#ApplicationScoped
class FruitRepository: PanacheRepository<Fruit> {
fun all(): List<Fruit> = findAll(Sort.by("name")).list<Fruit>()
}
Quarkus has released an extension which brings Kotlin support to panache (I think it is still in preview).
in Gradle (if you're using Gradle for your project) you'll need to add the dependencie implementation 'io.quarkus:quarkus-hibernate-orm-panache-kotlin'
To define your "static" methods (Kotlin uses Companion objects to do stuff with static methods) you'll need to define a companion object like this:
#Entity
open class Category : PanacheEntityBase {
#Id
#GeneratedValue
lateinit var id: UUID
// ...
companion object : PanacheCompanion<Category, UUID> {
fun findByName(name: String) = find("name", name).firstResult()
fun findActive() = list("active", true)
fun deleteInactive() = delete("active", false)
}
}
for more information you can check out the official docs:
https://quarkus.io/guides/hibernate-orm-panache-kotlin
Be warned if you use unit tests: at least for me the panache-mock extension is not working with the kotlin version of panache.
Related
New to DI and guice..
I want to use a service (StoreLevelClient) This is a class defined by other team.
I inject this class in my main file like this:
class ClientAccessor {
companion object {
private val LOGGER = KotlinLogging.logger { }
}
private val myStoreLevelClient: StoreLevelClient =
Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)
And made a module file for the StoreLevelClient like below:
class ServiceModule : AbstractModule() {
#Provides
#Singleton
fun getClient(myServiceClient : KasServiceClient): StoreLevelClient {
return StoreLevelClient(myServiceClient, AppConfigObject.trackedDocument, AppConfigObject.appConfigFallback)
}
It gave me errors:
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:
3
2022-05-20T18:27:50.800-07:00
1) No implementation for com.kasservice.KasServiceClient was bound.
4
2022-05-20T18:27:50.800-07:00
while locating com.kasservice.KasServiceClient
5
2022-05-20T18:27:50.800-07:00
for the 1st parameter of com.myservice.dependency.ServiceModule.getClient
The KasServiceClient is also from other's
So I #Provides it in the ServiceModule as well:
#Provides
#Singleton
fun getService(
cloudAuthCredentialVisitor: CloudAuthDefaultCredentialsVisitor,
metricsAwareCallVisitor: MetricsAwareCallVisitor,
#Named(BINGBONG_SERVICE_CLIENT_RETRY_STRATEGY)
retryStrategy: RetryStrategy<*>
): KasServiceClient {
val domain = AppConfig.findString(DOMAIN)
val realm = AppConfig.getRealm().name()
val qualifier = "$domain.$realm"
return ClientBuilder()
.remoteOf(KasServiceClient::class.java)
.withConfiguration(qualifier)
.withCallVisitors(cloudAuthCredentialVisitor, metricsAwareCallVisitor, CallAttachmentVisitor(Calls.retry(retryStrategy)))
.newClient()
}
But it gave me errors like below:
Could not find a suitable constructor in com.amazon.coral.client.cloudauth.CloudAuthDefaultCredentialsVisitor. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
Could not find a suitable constructor in com.amazon.metrics.declarative.client.MetricsAwareCallVisitor. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
The CloudAuthDefaultCredentialsVisitor and MetricsAwareCallVisitor are use #Provides and instantiate already.
So I don't know why guice can't find them...??
Any idea for this?? I wonder I have some mistake when using Guice. But I have hard time to debug and find
Solved the problem by changing the way of inject:
Instead use:
class ClientAccessor {
companion object {
private val LOGGER = KotlinLogging.logger { }
}
private val myStoreLevelClient: StoreLevelClient =
Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)
Tried this:
class ClientAccessor #Inject constructor(private val myStoreLevelClient: StoreLevelClient){
companion object {
private val LOGGER = KotlinLogging.logger { }
}
Reason:
use #Inject instead of using the createInjector manually on particular modules, let guice inject it for us. When I tried to directly use createInjector in instantiating in my code, it will only lookup the specified module and not able to load other modules.
i created my useCase and setup input and output and then provide it on module and everything works fine.
class GetProfileUseCase constructor(
private val repository: Repository,
defaultDispatcher: CoroutineDispatcher
) : SuspendUseCase<Request, ResultEntity<Response>>(defaultDispatcher) {
override suspend fun execute(parameters: Request): ResultEntity<Response> {
return repository.getProfile(parameters)
}
}
and here is my data class
sealed class GetProfile : Serializable {
data class Request(
val nothing: Nothing? = null
) : GetProfile()
data class Response(
val user: User? = User()
) : GetProfile()
}
because there is no parameter in Request class i tried changed it to :
object Request
but when build it IDE error :
error: method create in class ViewModel_Factory cannot be applied to given types;
and here is my view model :
class ViewModel #Inject constructor(
private val getProfileUseCase: GetProfileUseCase
) : BaseViewModel()
i don't know why it happen and dagger couldn't generate classes !!!
it works without no error when set Request as data class but crash when make it object class
because there is no parameter in Request class i tried changed it to :
object Request
You seem to think an object is same as a class with a single no parameter constructor, which is not true. For complete understanding I encourage you to read this thread.
However I will try to provide a brief explanation as to why your code doesn't work
When you do object Request, it makes Request a singleton, meaning you can no longer create its instances using val instance = Request(), reason being that constructor of Request is marked private by kotlin compiler.
so when your code(dagger) tries to inject Request using new Request(), it gives you error.
I have a very simple Spring Rest service. Below is my ServiceImpl Class in Java :-
Service Impl Class =>
#AllArgsConstructor(onConstructor = (#__(#Autowired)))
public Class ServiceImpl implements ServiceClass {
private ServiceRepository repo;
private MapstructRequestMapper1 mapper1;
private Optional<Pojo1> pojo1;
#Override
public Optional<Pojo1> getSomething(Pojo2 pojo2, String someString){ return repository.findById(Id)};
#Override
public SomeResponse createSomething(Pojo2 pojo2, String someString){
pojo1 = getSomething(someString); //This Returns an Optional object.
if(pojo1.ifPresent(pojo1-> {
//set something with pojo1's attributes and then do something.
});
)
}
}
Impltest class:- (uses Groovy + Spock Specification)
def ref = Mock(Servicerepo)
def mapperRef1 = Mock(MapstructRequestMapper1)
//This is where I am stuck
def requestTestObj = Mock(Pojo1)
def service = new ServiceImpl(ref,mapperRef1, requestTestObj) //This is where it fails.
def "myTest - some bla" () {
}
}
ERROR => if if I tried def requestTestObj = Mock(Pojo1) as Optional it gives me following 2 errors:-
Class cannot be casted.
if without "as Optional" it gives me "No Matching Constructor in the ImplTestclass".
How do we handle this ? DB used here is Mongo
Thank you.
You cannot mock class Optional directly by conventional means because the class is final and Spock mocks are based on dynamic proxies, i.e. sub-classing. But how about mocking Pojo1 and wrapping it into an Optional?
def requestTestObj = Mock(Pojo1)
def service = new ServiceImpl(ref, mapperRef1, Optional.of(requestTestObj))
As usual, you can stub the POJO's methods, verify that certain methods have been called so many times and so forth.
I convert one of my Java class to Kotlin and the class as below.
class MainApplication : Application() {
companion object {
operator fun get(context: Context): MainApplication {
return context.applicationContext as MainApplication
}
}
}
It has a static function get.
I still have a Java function accessing it.
MainApplication application = MainApplication.get(mContext);
It was good when MainApplication is in Java. But not when MainApplication in Kotlin, the above code error
Error:(27, 54) error: cannot find symbol method get(Context)
How could I access get in my Java code above?
You can add #JvmStatic annotation to the method in companion object to make Kotlin generate a static method.
class MainApplication : Application() {
companion object {
#JvmStatic fun get(context: Context): MainApplication {
return context.applicationContext as MainApplication
}
}
}
you can then access it from Java like before converting to Kotlin:
MainApplication application = MainApplication.get(mContext);
EDIT: I feel obliged to add something I learned recently: #JvmStatic doesn't actually move where the method gets generated. It duplicates it, by generating a static method for Java in addition to the method on the companion object. Personally I think this isn't great and it can have some implications depending on a use case, so something worth knowing.
Ops, I got it. Just use the below.
MainApplication application = MainApplication.Companion.get(mContext);
By omitting the name of your companion object, the name Companion must be used to access the methods.
Example:
class MyClass1 {
companion object Object1 {
fun method1 {
}
}
}
class MyClass2 {
companion object {
fun method2 {
}
}
}
To invoke the first companion object method you would do the following:
MyClass1.method1()
To invoke the second:
MyClass2.Companion.method2()
See the Kotlin docs on Companion Objects for details.
You may encounter a problem where you cannot access the Companion object's method in Java if the new keyword is used in the method call. The new keyword should be omitted. The documentation states:
Companion objects and their members can only be accessed via the containing class name, not via instances of the containing class.
So if you have a class like this:
class MyClass {
companion object {
fun create() {}
}
}
You can call the companion object's method like this:
MyClass.create()
But not like this:
new MyClass.create
Well guys; Here's my problem:
I currently have a class extending another that will have a gameinfo.class annotation on each class (Is there a way I can enforce this with the compiler?)
Ideally I'd like to call the following in the type it is extending:
#GameInfo(name = "Default Minigame", aliases = {"DM"}, pvp = false, authors = {"Tom"},
gameTime = 65, description = "desc")
public class DefaultMinigame extends Minigame {
public DefaultMinigame(Plugin p) {
super(p, DefaultMinigame.class.getAnnotation(GameInfo.class));
}
I'd rather not have to call the above in each class and instead call it in Minigame.class
How would I do this without calling "this" (Due to super type not being initialized)
Thanks for your time!
(And sorry if what I'm saying isn't the most comprehensible of questions)
You can not enforce that a subclass has an annotation. If that is important to you, you might use the template method pattern instead.
But if you need to use annotations, you can read the subclass annotation with
class Minigame {
protected Minigame() {
GameInfo info = getClass().getAnnotation(GameInfo.class);
}
}