Field Variable Not Persisting - kotlin

I am learning Kotlin. I am from a Java background, so some of my coding style may reflect that.
So I have written a Spring Boot application. I have written a class like this:
class CISCODeviceAdapter(private val deviceConnectionDetails: DeviceConnectionDetails) : DeviceAdapterBase {
private val logger = LoggerFactory.getLogger(javaClass)
private val transactionID : String = ""
private var sshConnection : SSHConnection? = null
.
.
.
.
override fun executeCommand(commands: List<String>) : HashMap<String, String> {
val deviceAdapterBase = CISCODeviceAdapter(deviceConnectionDetails)
sshConnection = SSHConnection(deviceConnectionDetails.ipAddress, deviceConnectionDetails.port, deviceConnectionDetails.username, deviceConnectionDetails.password)
deviceAdapterBase.createSession()
val configurations = sshConnection!!.sendToShell(commands)
deviceAdapterBase.endSession()
logger.info("All commands run!")
return configurations
}
.
.
.
override fun createSession() {
try {
logger.debug("Creating a SSH connection to: ${deviceConnectionDetails.ipAddress}")
sshConnection!!.connect("240")
logger.debug("Connection created successfully.")
} catch (e : ProtocolException) {
logger.error("Unable to create the session to ${deviceConnectionDetails.ipAddress}.", e)
throw ProtocolException(e.message)
}
}
.
.
.
.
}
So I initialize the sshConnection object in the executeCommand() function. But when the control comes to createSession() function, the sshConnection object is null.
Is there something extra that I have to do so that object value remains in place? I tried lateinit but even that didn't work. One thing, i did think of is, do I have to declare it in the parent interface DeviceAdapterBase?
Any pointers would be helpful.

This has nothing to do with kotlin. You can try to write your code in Java first. Then when you know it's working correctly, I suggest you let your IDE to convert it to kotlin.

Related

Possible to use Spring AOP (AspectJ) with Kotlin properties?

Is it possible to use Spring AOP (AspectJ) with Kotlin properties? Specifically due to how Kotlin compiles properties to Java:
a getter method, with the name calculated by prepending the get prefix
a setter method, with the name calculated by prepending the set prefix (only for var properties)
a private field, with the same name as the property name (only for properties with backing fields)
Consider the following minimal reproducible example:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
annotation class TestAnnotation
...
#Aspect
class TestAspect {
#Around("#annotation(annotation)")
fun throwingAround(joinPoint: ProceedingJoinPoint, annotation: TestAnnotation): Any? {
throw RuntimeException()
}
}
...
internal class MinimalReproducibleExample {
open class TestProperties {
#TestAnnotation
val sampleProperty: String = "sample property"
#TestAnnotation
fun sampleFunction(): String = "sample function"
}
private lateinit var testProperties: TestProperties
#BeforeEach
fun setUp() {
val aspectJProxyFactory = AspectJProxyFactory(TestProperties())
aspectJProxyFactory.addAspect(TestAspect())
val aopProxyFactory = DefaultAopProxyFactory()
val aopProxy = aopProxyFactory.createAopProxy(aspectJProxyFactory)
testProperties = aopProxy.proxy as TestProperties
}
#Test
fun test() {
println(testProperties.sampleProperty)
println(testProperties.sampleFunction())
}
}
Running the test yields:
null
sample function
When debugging I can see that the generated proxy is a cglib-backed proxy, which should be able to proxy to a concrete class, but it does not seem to invoke the configured aspect. Is there something wrong with my #Around definition, or is this a limitation of Kotlin properties and/or proxying concrete classes?
Was able to trigger the aspect above with the following changes:
Use a "site target" for the getter: #get:TestAnnotation
Make the property/function both open

Issue IDE warning if annotated member is not surrounded with a particular block

I have a data structure which has members that are not thread safe and the caller needs to lock the resource for reading and writing as appropriate. Here's a minimal code sample:
class ExampleResource : LockableProjectItem {
override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()
#RequiresReadLock
val nonThreadSafeMember: String = ""
}
interface LockableProjectItem {
val readWriteLock: ReadWriteLock
}
fun <T : LockableProjectItem, Out> T.readLock(block: T.() -> Out): Out {
try {
readWriteLock.readLock().lock()
return block(this)
} finally {
readWriteLock.readLock().unlock()
}
}
fun <T : LockableProjectItem, Out> T.writeLock(block: T.() -> Out): Out {
try {
readWriteLock.writeLock().lock()
return block(this)
} finally {
readWriteLock.writeLock().unlock()
}
}
annotation class RequiresReadLock
A call ExampleResource.nonThreadSafeMember might then look like this:
val resource = ExampleResource()
val readResult = resource.readLock { nonThreadSafeMember }
To make sure that the caller is aware that the resource needs to be locked, I would like the IDE to issue a warning for any members that are annotated with #RequiresReadLock and are not surrounded with a readLock block. Is there any way to do this in IntelliJ without writing a custom plugin for the IDE?
I think this is sort of a hack, but using context receivers might work. I don't think they are intended to be used in this way though.
You can declare a dummy object to act as the context receiver, and add that as a context receiver to the property:
object ReadLock
class ExampleResource : LockableProjectItem {
override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()
// properties with context receivers cannot have a backing field, so we need to explicitly declare this
private val nonThreadSafeMemberField: String = ""
context(ReadLock)
val nonThreadSafeMember: String
get() = nonThreadSafeMemberField
}
Then in readLock, you pass the object:
fun <T : LockableProjectItem, Out> T.readLock(block: context(ReadLock) T.() -> Out): Out {
try {
readWriteLock.readLock().lock()
return block(ReadLock, this)
} finally {
readWriteLock.readLock().unlock()
}
}
Notes:
This will give you an error if you try to access nonThreadSafeMember without the context receiver:
val resource = ExampleResource()
val readResult = resource.nonThreadSafeMember //error
You can still access nonThreadSafeMember without acquiring a read lock by doing e.g.
with(ReadLock) { // with(ReadLock) doesn't acquire the lock, just gets the context receiver
resource.nonThreadSafeMember // no error
}
But it's way harder to accidentally write something like this, which I think is what you are trying to prevent.
If you call another function inside readLock, and you want to access nonThreadSafeMember inside that function, you should mark that function with context(ReadLock) too. e.g.
fun main() {
val resource = ExampleResource()
val readResult = resource.readLock {
foo(this)
}
}
context(ReadLock)
fun foo(x: ExampleResource) {
x.nonThreadSafeMember
}
The context receiver is propagated through.

What to send as Content

In my application, I save data to Firebase and to local storage using the Room library. With Firebase, everything is clear to me. But with Rom I had questions. I can't figure out what to pass to the class parameter.
PacketsLocalDataSource.kt
class PacketsLocalDataSource(val context: Context) {
lateinit var db: PacketsDatabase
lateinit var dao: PacketDao
fun saveLocal(packet: Packet) {
db = PacketsDatabase.getInstance(context)
dao = db.packetDao()
dao.add(packet)
}
}
And further, in the code below, I want to save the data. But there is an error on the fifth line of the code: No value passed for parameter 'context'. Please let me know what I need to send here.
class PocketScoutContainer {
private val firebaseRealtimeDatabase = Firebase.database
private val packetsRemoteDataSource = PacketsRemoteDataSource(firebaseRealtimeDatabase)
private val packetsLocalDataSource = PacketsLocalDataSource()
val packetsRepository = PacketsRepository(packetsRemoteDataSource, packetsLocalDataSource)
}
You need to pass Context to PacketsLocalDataSource constructor. In turn, the context must be passed when creating an instance of the class PocketScoutContainer. So:
class PocketScoutContainer(context: Context) {
//...
private val packetsLocalDataSource = PacketsLocalDataSource(context)
//...
And when creating PocketScoutContainer instanse in some Activity:
val pocketScoutContainer = PocketScoutContainer(this.applicationContext)
If PocketScoutContainer instantiated somewhere outside an activity or a fragment, you will need to pass Context there.
This may help further: Dependency Injection

Mock private property with mockk throws an excpetion

I'm using mockk for my testing in kotlin. But I can't seem to override a private property in a spy object.
I have this object
private val driverMapSnapshotMap: MutableMap<Int, SnapshotImage> = mutableMapOf()
in a class that I spy on using
viewModel = spyk(DriverListViewModel(), recordPrivateCalls = true)
But when I try to make it fill up with mock values I get an error
every {
viewModel getProperty "driverMapSnapshotMap"
} returns(mapOf(1 to mockkClass(SnapshotImage::class)))
The error I get
io.mockk.MockKException: Missing calls inside every { ... } block.
Any thoughts?
Here is a solution to access private fields in Mockk for classes( for objects it is even simpler )
class SaySomething {
private val prefix by lazy { "Here is what I have to say: "}
fun say( phrase : String ) : String {
return prefix+phrase;
}
}
#Before
fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true)
#Test
fun SaySomething_test() {
mockkConstructor(SaySomething::class)
every { anyConstructed<SaySomething>() getProperty "prefix" } propertyType String::class returns "I don't want to say anything, but still: "
val ss = SaySomething()
assertThat( ss.say("Life is short, make most of it"), containsString( "I don't want to say anything"))
}
It is nearly impossible to mock private properties as they don't have getter methods attached. This is kind of Kotlin optimization and solution is major change.
Here is issue opened for that with the same problem:
https://github.com/mockk/mockk/issues/263
It should be
every {
viewModel getProperty "driverMapSnapshotMap"
} returns mock(DriverRemoteModel::class)

Infinite recursion in Getter in Kotlin

I am familiar with Java, but I am having difficulty working with Kotlin.
To illustrate my question, here is some Java Code. If the getter finds the field to be NULL, it initializes the field, before returning the field.
package test;
public class InitFieldJava {
private final static String SECRET = "secret";
private String mySecret;
public String getMySecret() {
if(mySecret == null) initMySecret();
return mySecret;
}
private void initMySecret() {
System.out.println("Initializing Secret ....");
mySecret = SECRET;
}
public static void main(String[] args) {
InitFieldJava field = new InitFieldJava();
System.out.println(field.getMySecret());
}
}
Can I do something like the above in Kotlin. My attempt in Kotlin looks like this:
package test
class InitFieldKotlin {
private val SECRET = "secret"
private var mySecret: String? = null
get() {
if (mySecret == null) initMySecret() //Infinite Recursion!!!
return mySecret
}
private fun initMySecret() {
println("Initializing Secret ....")
mySecret = SECRET
}
companion object {
#JvmStatic
fun main(args: Array<String>) {
val field = InitFieldKotlin()
println(field.mySecret)
}
}
}
My problem is that this results in infinite recursion:
Exception in thread "main" java.lang.StackOverflowError
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
at test.InitFieldKotlin.getMySecret(InitFieldKotlin.kt:7)
I’d appreciate knowing what I’m doing wrong.
Try to use field keyword inside get():
private var mySecret: String? = null
get() {
if (field == null) initMySecret()
return field
}
Generally speaking, field allows to access your value directly without calling get, almost in the same way as in your Java example. More information can be found in documentation.
The problem you're facing is that when you call your property this way, the getter will be called again. And when you call getter, another getter is called, and so on until an StackOverflow.
You can fix this as shown by #Google, and using field inside the getter, instead of the property name:
if (field == null)initMySecret()
This way you won't access the property using its getter.
But more importantly: why don't you use a lazy initialization? If the variable is final, and it seems to be, you could use a lazy val
This way, the field won't be nullable anymore, so you won't have to safe-call it. And you'll not use boilerplate code, Kotlin can do this lazy initialization for you!
val mySecret: String by lazy {
println("Initializing Secret. This print will be executed only once!")
"SECRETE" //This value will be returned on further calls
}
More examples on Lazy can be seen at Kotlin Docs