We are planning to implement some behaviour control in our
CordApp, for testing purposes. Is that possible to create a
M(X)Bean, accessible via JMX, which is going to change some
internal flags in our CordApp ? If this is not a good design
choice, please inform the best practice to follow.
Basically, we have a set of flags, like these:
abstract class BaseFlow() : FlowLogic<SignedTransaction>() {
var flagBehaviourOne : Boolean = true
var flagBehaviourTwo : Boolean = true
var flagBehaviourThree: Boolean = true
var flagBehaviourFour : Boolean = true
...
}
then, in some implementing class, we have something like this:
object SomeFlow {
#InitiatingFlow
class Initiator(private val destinatario: Party,
private val parameter: StateObject,
private val isAnonymous: Boolean = false,
private val pointer: Any) : BaseFlow() {
...
#Suspendable
override fun call(): SignedTransaction {
if (flagBehaviourOne || flagBehaviorTwo) {
// enforce some specific behaviour
}
...
} // end of SomeFlow.Initiator
...
} // end of SomeFlow
I have (partially) solved my problem.
I have added a new object class, along with its jmx interface :
package vfalcao.example.jmx
import java.lang.management.ManagementFactory
import javax.management.MXBean
import javax.management.ObjectName
#MXBean
interface BehaviourControlMXBean {
fun setBehaviourOne(newValue: String)
fun isBehaviourOne() : String
...
// other "behaviours" ommited for brevity
}
object BehaviourControl : BehaviourControlMXBean {
// internal data
...
init {
val objectName = ObjectName("vfalcao.example.jmx:type=BehaviourControl,name=def")
val platformMBeanServer = ManagementFactory.getPlatformMBeanServer()
platformMBeanServer.registerMBean(this, objectName)
}
}
then, in my BaseFlow class:
abstract class BaseFlow() : FlowLogic<SignedTransaction>() {
companion object {
...
init {
println("${BehaviourControl}")
}
...
fun test() {
var behaviour1 = ((BehaviourControl.props["behaviour1"] as String).toBoolean())
if (behaviour1) {
// do something controlled by behaviour1
}
}
}
...
}
Related
I'm trying to create a custom lint rule in order to avoid using a kotlin object class.
object AColors {
val white: Color = Color(0xFFFFFFFF)
}
fun main() {
val myColor = AColors.white //Lint error : Do not use AColors
}
How can I manage to get a lint issue when AColors is used?
In my case, AColors can't be private because I need it for a specific case.
I tried to create a custom lint rule to check the import but this is not a bullet-proof solution as you can use AColors without importing anything
class InvalidImportHandler(private val context: JavaContext) : UElementHandler() {
override fun visitImportStatement(node: UImportStatement) {
//check the import
}
}
For this specific case, you may check for USimpleNameReferenceExpressions and then check if the reference is to the AColors class. Like so:
class AColorsReferenceDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes(): List<Class<out UElement>> {
return listOf(USimpleNameReferenceExpression::class.java)
}
override fun createUastHandler(context: JavaContext): UElementHandler {
return object : UElementHandler() {
override fun visitSimpleNameReferenceExpression(
node: USimpleNameReferenceExpression
) {
val element = node.resolve()?.unwrapped
if (element is KtObjectDeclaration && element.name == "AColors") {
context.report(
ISSUE,
node,
context.getLocation(node.uastParent)
)
}
}
}
}
companion object {
val ISSUE = Issue.create(
AColorsReferenceDetector::class.simpleName.orEmpty(),
"Do not use AColors",
"Do not use AColors",
Category.CORRECTNESS,
10,
Severity.ERROR,
Implementation(
AColorsReferenceDetector::class.java,
EnumSet.of(Scope.JAVA_FILE)
)
)
}
}
Example IssueRegistry:
class IssueRegistry : IssueRegistry() {
override val api = CURRENT_API
override val minApi: Int
get() = 8
override val vendor: Vendor = Vendor()
override val issues
get() = listOf(AColorsReferenceDetector.ISSUE)
}
Example result:
I have a annotation AggregateId that could be set on method params and properties and that I will use to retrieve some id :
#Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
#Retention(AnnotationRetention.RUNTIME)
annotation class AggregateId
I wrote that test case :
data class Example(
#AggregateId
val id: UUID
)
class AggregateIdTests {
private val exUuid = UUID.fromString("bae30706-f949-4eb5-b091-d51a13ddc832")
#Test
fun test() {
val ex = Example(id = exUuid)
val id = resolve(ex)
Assertions.assertThat(id).isEqualTo(exUuid)
}
private fun resolve(target: Any): UUID? {
val prop = target::class.declaredMemberProperties.find {
it.findAnnotation<AggregateId>() != null
}
return prop?.getter?.call(target) as UUID?
}
}
That actually works.
But if I add this class in the code :
class TestClass {
fun aMethod(#AggregateId param: UUID) {
}
}
Suddently the AggregateId changes of target for the other class. Even though I didn't change the rest of the code. What is the explaination of this ?
(using kotlin 1.5)
In reference to https://google.github.io/dagger/multibindings.html, there's this code
#AutoAnnotation
static MyKey createMyKey(String name, Class<?> implementingClass, int[] thresholds) {
return new AutoAnnotation_MyComponentTest_createMyKey(name, implementingClass, thresholds);
}
When converted to Kotlin, I use below
companion object {
#AutoAnnotation
#JvmStatic
fun createMyKey(name: String, implementingClass: Class<*>, thresholds: IntArray): MyKey {
return AutoAnnotation_MainActivity_createMyKey(name, implementingClass, thresholds)
}
}
It still complaints
error: #AutoAnnotation method must be static
public final com.elyeproj.daggermultibinding.MyKey createMyKey(#org.jetbrains.annotations.NotNull()
^
I tried both with and without #JvmStatic still not working. How to resolve this?
The following works for me. Have the create key method as global function.
class HelloAutoAnnotations {
fun execute() {
println("HelloAutoAnnotations...")
DaggerMyComponent.create().myMap().forEach(::println)
}
}
#MapKey(unwrapValue = false)
private annotation class MyKey(val username: String, val password: String)
#Module
private class MyModule {
#Provides
#IntoMap
#MyKey(username = "user1", password = "T0gether")
fun providesUser(): String = "Rooney"
#Provides
#IntoMap
#MyKey(username = "user2", password = "T0gether")
fun provideUser(): String = "Doo"
}
#Component(modules = arrayOf(MyModule::class))
private interface MyComponent {
fun myMap(): Map<MyKey, String>
}
#AutoAnnotation
private fun createMyKey(username: String, password: String): MyKey {
return MyKeyCreator.createMyKey(username, password)
}
According to the docs:
Maps whose keys are not known at compile time
Map multibindings work only if your map’s keys are known at compile
time and can be expressed in an annotation. If your map’s keys don’t
fit in those constraints, then you cannot create a multibound map, but
you can work around that by using set multibindings to bind a set of
objects that you can then transform into a non-multibound map.
#Module
class MyModule {
#Provides #IntoSet
static Map.Entry<Foo, Bar> entryOne(...) {
Foo key = ...;
Bar value = ...;
return new SimpleImmutableEntry(key, value);
}
#Provides #IntoSet
static Map.Entry<Foo, Bar> entryTwo(...) {
Foo key = ...;
Bar value = ...;
return new SimpleImmutableEntry(key, value);
}
}
#Module
class MyMapModule {
#Provides
static Map<Foo, Bar> fooBarMap(Set<Map.Entry<Foo, Bar>> entries) {
Map<Foo, Bar> fooBarMap = new LinkedHashMap<>(entries.size());
for (Map.Entry<Foo, Bar> entry : entries) {
fooBarMap.put(entry.getKey(), entry.getValue());
}
return fooBarMap;
}
}
So you should try this approach perhaps.
I am trying to understand how to hide a base constructor parameter in a subclass in kotlin. How do you put a facade over a base constructor? This doesn't work:
import com.android.volley.Request
import com.android.volley.Response
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private fun hiddenListener() = Response.ErrorListener {
/* super secret listener */
}
...
}
I think I understand the problem:
During construction of a new instance of a derived class, the base
class initialization is done as the first step (preceded only by
evaluation of the arguments for the base class constructor) and thus
happens before the initialization logic of the derived class is run.
I'm trying to solve this problem for Volley, where I need my custom request to be be a Request so that it can be passed into a RequestQueue. It would be easier of RequestQueue took in some kind of interface but since it doesn't I have to subclass. There are other ways I can hide these complexities from the caller, but this limitation has come up for me other times in Kotlin and I'm not sure how to solve it.
I am not familiar with volley but I tried to come up with an example that should give you some insight how to solve your problem. What you can do is use a companion object:
interface MyListener {
fun handleEvent()
}
open class Base<T>(anything: Any, val listener: MyListener) { // this would be your Request class
fun onSomeEvent() {
listener.handleEvent()
}
}
class Derived(anything: Any) : Base<Any>(anything, hiddenListener) { // this would be your MyCustomRequest class
private companion object {
private val hiddenListener = object : MyListener {
override fun handleEvent() {
// do secret stuff here
}
}
}
}
So if you apply this to your problem, the result should look something like this:
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private companion object {
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
}
...
}
A different way would be to use a decorator, create your Request withing that decorator and just delegate the calls to it:
class Decorator(anything: Any) {
private var inner: Base<Any>
private val hiddenListener: MyListener = object : MyListener {
override fun handleEvent() { }
}
init {
inner = Base(anything, hiddenListener)
}
}
And once again for your example that would look like this:
class MyCustomRequest(url: String) {
private var inner: Request<String>
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
init {
inner = Request<String>(Request.Method.POST, url, hiddenListener)
}
...
}
I'm wondering if it's possible in Kotlin to deserialize (restore property values) of a declared object, without having to manually assign the properties or resorting to reflection. The following snippet further explains:
object Foo: Serializable {
var propOne: String = ""
// ...
fun persist() {
serialize(this)
// no problem with serialization
}
fun restore(bytes: ByteArray) {
val fooObj: Foo = deserialize(bytes) as Foo
// It seems Kotlin allows us to use singleton as type!
// obvioulsly either of the following is wrong:
// this = fooObj
// Foo = fooObj
// ... is there a way to 'recover' the singleton (object) other than
// manual assignment of properties (or reflection) ???
}
}
There is no way to reassign the global reference to a singleton with a new instance. At most you can write out the properties during serialization, and then on deserialization directly read the properties and mutate the state in the original object. It will require custom code for you to assign the properties into the object either by direct assignment or reflection. It would be better if you make your own singleton mechanism that holds an instance that you can swap out to be another instance that you deserialize.
have faced with the same issue, and want to share with you my solution:
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import java.io.File
import java.lang.reflect.Modifier
typealias ObjMap = HashMap<String, Any?>
fun <T : Any> T.getInstance() : Any? {
val target = if(this is Class<*>) this else javaClass
return target.getDeclaredField("INSTANCE")?.get(null)
}
class ObjectHelper {
companion object {
val mapper = ObjectMapper().apply {
enable(SerializationFeature.INDENT_OUTPUT)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
fun objectToMap(obj: Any): ObjMap {
var res = ObjMap()
val instance = obj.getInstance()
val o = instance ?: obj
o.javaClass.declaredFields.forEach {
if(it.name != "INSTANCE") {
it.isAccessible = true
val value = if(Modifier.isStatic(it.modifiers)) it.get(null) else it.get(o)
res[it.name] = value
}
}
o.javaClass.classes.forEach {
res[it.simpleName] = objectToMap(it)
}
return res
}
fun saveObject(path: String, obj: Any) {
mapper.writeValue(File(path), objectToMap(obj))
}
fun loadObject(path: String, obj: Any) {
val json = mapper.readValue<HashMap<*,*>>(File(path), HashMap::class.java) as ObjMap
loadObject(obj, json)
}
fun loadObject(obj: Any, props: ObjMap) {
val objectParam = mapper.writeValueAsString(props)
mapper.readValue(objectParam, obj::class.java)
obj.javaClass.classes.forEach {
val instance = it.getInstance()
val map = props[it.simpleName]
if(map != null && instance != null) {
loadObject(instance, map as ObjMap)
}
}
}
}
}
Usage example:
object TestObj {
var f1: String = "f1"
var f2: String = "f2"
object TestObj_2 {
var f1: String = "f1_1"
var f2: String = "f2_2"
}
}
TestObj.f1 = "aaa"
saveObject("out.json", TestObj)
TestObj.f1 = "bbb"
loadObject("out.json", TestObj)
println(TestObj.f1)
Result will be "aaa".