I´m trying to use a Configuration in an Annotation, like so:
#ConfigurationProperties("scheduling")
interface SchedulingConfiguration {
val initialDelay: String
val fixedDelay: String
}
#Singleton
class Worker(
private val configuration: SchedulingConfiguration,
) {
private val log = LoggerFactory.getLogger(javaClass)
#Scheduled(initialDelay = configuration.initialDelay, fixedDelay = configuration.fixedDelay)
fun fetchQueueEntry() {
log.info("Fetching entry")
}
}
I´m getting the warning An annotation argument must be a compile-time constant.
Is there some way to get this working with Micronaut?
I managed to get it running by browsing the Micronaut documentation and accidentally stumbling across Property Placeholders. This will work fine, even though not feeling 'optimal'.
#Singleton
class Worker {
private val log = LoggerFactory.getLogger(javaClass)
#Scheduled(
initialDelay = "\${scheduling.initialDelay}",
fixedDelay = "\${scheduling.fixedDelay}"
)
fun fetchQueueEntry() {
log.info("Fetching entry")
}
}
It´s also possible to define default values that will be used if the keys are not present in config files or env variables:
#Scheduled(
initialDelay = "\${scheduling.initialDelay:0s}",
fixedDelay = "\${scheduling.fixedDelay:10s}"
)
With no default values and absent configuration for used property placeholders, an Exception will be thrown at runtime and the application will shut down.
Related
we received a crash on Firebase for a kotlin method:
Fatal Exception: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter code
at [redacted].DeliveryMethod.<init>(:2)
at [redacted].DeliveryMethodsUpdater$addSingleDMInAd$clientCall$1.invokeSuspend(DeliveryMethodsUpdater.kt:121)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
the model is this one:
class DeliveryMethod() {
lateinit var code: String
lateinit var name: String
lateinit var description: String
var isAddressRequired: Boolean? = null
var image: JSONObject? = null
var isDefault: Boolean = false
constructor(code: String) : this() {
this.code = code
}
constructor(code: String, name: String, description: String, image: JSONObject? = null) : this() {
this.code = code
this.name = name
this.description = description
this.image = image
}
}
and the method:
private suspend fun addSingleDMInAd(
adId: Long,
deliveryMethodCode: String
): JoinAdDeliveryMethod? {
var addedDeliveryMethod: JoinAdDeliveryMethod? = null
val clientCall = GlobalScope.async(Dispatchers.IO) {
val cd = CountDownLatch(1)
Client.getInstance().addDeliveryMethodInAd(
adId,
DeliveryMethod(deliveryMethodCode),
object : NetworkCallback<JoinAdDeliveryMethod> {
override fun onSuccess(result: JoinAdDeliveryMethod) {
addedDeliveryMethod = result
cd.countDown()
}
override fun onFailure(err: NetworkError?) {
addedDeliveryMethod = null
cd.countDown()
}
}
)
cd.await()
}
clientCall.await()
return addedDeliveryMethod
}
now, I understand that that the constructor for DeliveryMethod is being called with a null value for code, but I don't understand why the exception only come up at this point. As you can see, the method param is also marked as non-null, and so are previous methods. Shouldn't the exception be thrown way before getting to the constructor call for DeliveryMethod?
EDIT:
this is the caller of addSingleDMinAd():
fun addDeliveryMethodsInAd(
adId: Long,
deliveryMethodCodesToAdd: List<String>,
completionListener: (List<JoinAdDeliveryMethod?>) -> Unit
) {
GlobalScope.launch {
val updatedDms: MutableList<JoinAdDeliveryMethod?> = mutableListOf()
for (deliveryCode in deliveryMethodCodesToAdd) {
addSingleDMInAd(adId = adId, deliveryMethodCode = deliveryCode).run {
updatedDms.add(this)
}
}
completionListener.invoke(updatedDms)
}
}
this is the java caller of the addDeliveryMethodsInAd() (this is inside an Android Service):
new DeliveryMethodsUpdater().addDeliveryMethodsInAd(
result.getId(),
deliveryMethodCodesToAdd,
updatedDMs -> {
// on failed delivery method request
for (JoinAdDeliveryMethod updatedDm : updatedDMs) {
if (updatedDm == null) {
//show error
break;
}
}
AdDetailUpdater
.getInstance()
.updateSubscribersWithDeliveryMethods(result.getId(), updatedDMs);
return null;
}
);
Shouldn't the exception be thrown way before getting to the constructor call for DeliveryMethod?
Within Kotlin, it's not possible for a non-null parameter to be given a null value at runtime accidentally (because the code wouldn't have compiled in the first place). However, this can happen if the value is passed from Java. This is why the Kotlin compiler tries to protect you from Java's null unsafety by generating null-checks at the beginning of some methods (with the intrinsic checkNotNullParameter you're seeing fail here).
However, there is no point in doing that in private or suspend methods since they can only be called from Kotlin (usually), and it would add some overhead that might not be acceptable in performance-sensitive code. That is why these checks are only generated for non-suspend public/protected/internal methods (because their goal is to prevent misuse from Java).
This is why, if you manage to call addSingleDMInAd with a null argument, it doesn't fail with this error. That said, it would be interesting to see how you're getting the null here, because usually the checks at the public API surface are enough. Is some reflection or unsafe cast involved here?
EDIT: with the addition of the calling code, this clears up the problem. You're calling a method that takes a List<String> from Java, with a list that contains nulls. Unfortunately Kotlin only checks the parameters themselves (in this case, it checks that the list itself is not null), it doesn't iterate your list to check for nulls inside. This is why it didn't fail at the public API surface in this case.
Also, the way your model is setup is quite strange. It seems the lateinit is lying because depending on which constructor is used, the properties may actually not be set at all. It would be safer to mark them as nullable to account for when users of that class don't set the value of these properties. Doing this, you won't even need all secondary constructors, and you can just use default values:
class DeliveryMethod() {
var code: String? = null,
var name: String? = null,
var description: String? = null,
var image: JSONObject? = null,
) {
var isAddressRequired: Boolean? = null
var isDefault: Boolean = false
}
Other things of note about addSingleDMInAd:
don't use GlobalScope in this case. If you need to run short-lived coroutines, provide them with a smaller scope that is cancelled when the work is not needed anymore - it ensures no coroutines are leaked. You can read more about the potential pitfalls of GlobalScope and possible replacements in its own doc. That said, you probably shouldn't be starting a coroutine at all anyway here, see next point.
don't use async {} if you use await() right after - it's pointless to start something asynchronous if you wait for it right there. If you want to switch the context to IO, use withContext(Dispatchers.IO) { ... } instead. That said, you shouldn't even need to use the IO dispatcher here, see next point.
don't use CountDownLatch for this purpose. The proper way to encapsulate an async API as a suspend function for coroutines is to use suspendCancellableCoroutine (check its doc, it provides an example on how to use it). Once you use this, there is no need anymore for Dispatchers.IO because it will not be blocking the current thread anymore.
Preface: Google+SO+docu search did not appear to give relevant information.
Domain Model:
My domain model tries to picture an ProductionPlan, containing a List of Machines.
Each Machine has a list of chained jobs, thus a job which has a getNextEntry():Job Method, creating a list of jobs.
I tried to solve this problem with chained PlanningVariables, but apparently do not understand the concept of chainedVariables/shadowVariables/anchorVariables.
With my understanding, all jobs are getting chained and the anchorShadowVariable points to the beginning of the list, thus the machine.
To implement chaining, Job and Machine need to implement an interface or extend superclass, thus i created ChainSuperClass. Additionally i do not know if i have to overwrite getter/setter to set Annotations in Machine-Class, my guess was that because Machine extends ChainSuperClass, those Annotations carry over.
Edit: Kotlin specific improvements are also appreciated.
The full error log of my code execution is now:
Exception in thread "main" java.lang.IllegalArgumentException: The entityClass (class optaplanner.productionPlan.domain.ChainSuperClass) has a InverseRelationShadowVariable annotated property (nextEntry) which does not return a Collection with sourceVariableName (machine) which is not chained. Only a chained variable supports a singleton inverse.
ChainSuperClass:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
#InverseRelationShadowVariable(sourceVariableName = "machine")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
Job:
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
#AnchorShadowVariable(sourceVariableName = "machine")
var machine: Machine? = null
private var nextEntry: Job? = null
#PlanningVariable(
valueRangeProviderRefs = ["jobList"],
graphType = PlanningVariableGraphType.CHAINED
)
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Machine:
class Machine(override val id: String, val jobTypes: List<JobType>) : ChainSuperClass() {
private var nextEntry: Job? = null
override fun setNextEntry(job: Job) {
this.nextEntry = job
}
override fun getNextEntry(): Job? {
return nextEntry!!
}
}
I think the most important thing to realize with chained variables is this: When you have an entity, say Job A and solver assigns a value (job/machine) to its variable, it's not like the chain is being built forward starting from Job A. It's the other way around. By assigning a value to Job A's planning variable, Job A gets connected at the end of an existing chain.
Please take a look at the documentation to find more details about chaining and examples of valid chains.
By having understood this it should be clear that Job's planning variable name should be something like previousJobOrMachine (you'll probably want something simpler, for example previousStep) whereas the nextJob property is an inverse relation shadow variable derived from that (so when Job X gets connected to an existing chain ending with Job C by assigning Job X.previousStep=Job C, an inverse relation is established automatically: Job C.nextJob=Job X).
Based on that information, your model should look more like this:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
// changed sourceVariableName to point to the planning variable
#InverseRelationShadowVariable(sourceVariableName = "previousStep")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
// changed sourceVariableName to point to the planning variable
#AnchorShadowVariable(sourceVariableName = "previousStep")
var machine: Machine? = null
// added planning variable
private var previousStep: ChainSuperClass? = null
private var nextEntry: Job? = null
#PlanningVariable(
// added machineList value range provider
valueRangeProviderRefs = ["jobList", "machineList"],
graphType = PlanningVariableGraphType.CHAINED
)
// getter for the new planning variable
fun getPreviousStep(): ChainSuperClass {
return previousStep
}
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Notice that I added machineList as the source of possible values of the previousStep planning variable as previous step might be either a Job at the end of a non-empty chain or a Machine representing an empty chain.
Your domain is very similar to the vehicle routing domain, where you can see a working example of chaining. With good understanding of chaining principles you should be able to replicate it on your domain.
I have some testcases that share a common setup. They all need two fields which can be initialized in the same way. So I thought I can extract them into lateinit var fields and create them in an test-case-interceptor.
But when I try to access them in my testcases they always throw an exception because they are not initialized.
Is there a way to create the fields before every testcase?
Here is my code so far:
class ElasticsearchFieldImplTest : WordSpec() {
// These 2 are needed for every test
lateinit var mockDocument: ElasticsearchDocument
lateinit var mockProperty: KProperty<*>
override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
// Before Each
mockDocument = mock()
mockProperty = mock {
on {name} doReturn Gen.string().generate()
}
// Execute Test
test()
// After Each
}
init {
"ElasticsearchFields" should {
"behave like normal var properties" {
val target = ElasticsearchFieldImpl<Any>()
// Here the exception is thrown
target.getValue(mockDocument, mockProperty) shouldBe null
val testValue = Gen.string().generate()
target.setValue(mockDocument, mockProperty, testValue)
target.getValue(mockDocument, mockProperty) shouldBe testValue
}
}
}
}
When I step through it with a debugger and set a breakpoint in the interceptTestCase methods I see that it is executed before the test and that the properties are initialized. Then I step forward to the test and in it the properties are not initialized anymore.
Клаус Шварц's answer is incorrect. This is not how kotlintest works - in init lambdas are only created, not run. So you are not accessing your lateinit vars in init block. They just never have any value assigned.
This doesn't work because of bug in kotlintest, described (and actually almost resolved) here: https://github.com/kotlintest/kotlintest/issues/174
In short - interceptTestCase is called on different instance of class than real tests. So it has no influence on your tests at all.
Workaround is to override property:
override val oneInstancePerTest = false
Then there is only one instance and interceptTestCase works correctly, but you have to remember - there is only one instance for all tests.
Kotlintest 3.0 will be free of this bug. (But possibly might have one instance for all tests by default.)
You should not access lateinit vars before they were initialized.
The problem is that you are accessing your lateinit vars inside init {} block, which is default constructor and it is called before interceptTestCase().
The easiest way here is just to make mockDocument and mockProperty nullable.
var mockDocument: ElasticsearchDocument? = null
var mockProperty: KProperty<*>? = null
and if you want you test to crash if these fields were not initialized add !! modifier:
init {
"ElasticsearchFields" should {
"behave like normal var properties" {
val target = ElasticsearchFieldImpl<Any>()
// Here the exception is thrown
target.getValue(mockDocument!!, mockProperty!!) shouldBe null
val testValue = Gen.string().generate()
target.setValue(mockDocument!!, mockProperty!!, testValue)
target.getValue(mockDocument!!, mockProperty!!) shouldBe testValue
}
}
}
for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.
A previous question shows how to put a static initializer inside a class using its companion object. I'm trying to find a way to add a static initializer at the package level, but it seems packages have no companion object.
// compiler error: Modifier 'companion' is not applicable inside 'file'
companion object { init { println("Loaded!") } }
fun main(args: Array<String>) { println("run!") }
I've tried other variations that might've made sense (init on its own, static), and I know as a workaround I can use a throwaway val as in
val static_init = {
println("ugly workaround")
}()
but is there a clean, official way to achieve the same result?
Edit: As #mfulton26's answer mentions, there is no such thing as a package-level function really in the JVM. Behind the scenes, the kotlin compiler is wrapping any free functions, including main in a class. I'm trying to add a static initializer to that class -- the class being generated by kotlin for the free functions declared in the file.
Currently there is no way to add code to the static constructor generated for Kotlin file classes, only top-level property initializers are getting there. This sounds like a feature request, so now there is an issue to track this: KT-13486 Package-level 'init' blocks
Another workaround is to place initialization in top-level private/internal object and reference that object in those functions that depend on the effect of that initialization. Objects are initialized lazily, when they are referenced first time.
fun dependsOnState(arg: Int) = State.run {
arg + value
}
private object State {
val value: Int
init {
value = 42
println("State was initialized")
}
}
As you mentioned, you need a property with something that would run on initialisation:
val x = run {
println("The package class has loaded")
}
I got around it by using a Backing Property on the top-level, under the Kotlin file. Kotlin Docs: Backing Properties
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // Type parameters are inferred
// .... some other initialising code here
}
return _table ?: throw AssertionError("Set to null by another thread")
}