I have my main class set up like this:
class MyView : View() {
val controller: PollController by inject()
etc
}
I want to pass in a variable (like a string for a pathfile)
class PollController : Controller() {
val currentData = SimpleStringProperty()
val stopped = SimpleBooleanProperty(true)
val scheduledService = object : ScheduledService<DataResult>() {
init {
period = Duration.seconds(1.0)
}
override fun createTask() : Task<DataResult> = FetchDataTask()
}
fun start() {
scheduledService.restart()
stopped.value = false
}
inner class FetchDataTask : Task<DataResult>() {
override fun call() : DataResult {
return DataResult(SimpleStringProperty(File(**path**).readText()))
}
override fun succeeded() {
this#PollController.currentData.value = value.data.value // Here is the value of the test file
}
}
}
[DataResult is just a SimpleStringProperty data class]
so that the functions within class PollController can refer to a pathfile. I can't figure out how the injection works; #Inject always stays red and adding constructors throws out the Controller() object return
This is a good use case for Scopes. A Scope isolates Controllers and ViewModels so that you can have different scopes with different versions of your resources. If you also add a ViewModel to hold your context, you can do something like this:
class MyView : View() {
val pc1: PollController by inject(Scope(PollContext("somePath")))
val pc2: PollController by inject(Scope(PollContext("someOtherPath")))
}
Now add the context object to your Controller, so that you can access it from any function within the controller instance.
class PollController : Controller() {
val context : PollContext by inject()
}
The context object could contain both input/output variables. In this example it takes the input path as a parameter. Be aware that such a ViewModel cannot be instantiated by the framework, so you have to put one of these into the Scope manually like I showed above.
class PollContext(path: String) : ViewModel() {
val pathProperty = SimpleStringProperty(path)
var path by pathProperty
val currentDataProperty = SimpleStringProperty()
var currentData by currentDataProperty
}
You could do something like:
Main app
class MyApp: App(MainView::class)
MainView
class MainView : View() {
override val root = hbox {
add(FileView("C:\\passedTestFile.txt"))
}
}
FileView
class FileView(filePath: String = "C:\\test.txt") : View() {
private val controller : FileController by inject(params = mapOf("pathFile" to filePath))
override val root = hbox {
label(controller.pathFile)
}
}
FileController
class FileController: Controller() {
val pathFile : String by param()
}
The controller acceps the path by a parameter using by param(), the view expects this variable by a constructor parameter and using it when injection the controller (The inject delegate has an optional params argument). The only thing left when you are using this view (in MainView) that you pass the file path on instance creation.
Ends up with:
However this works I would create 3 layers rather than two, the classic model-view-controller (or any derivate) layers and I would store the file path in the model.
Related
I have an authorizaton Aspect that checks specific conditions based on method annotation.
This specific example shows annotation to mark a method that is only accessible by customer service. But unfortunately this isn't the only condition.
We have to confirm the customerServiceId that is also passed as one of method parameters. The parameter containing customerServiceId is pretty nested, so I was wondering if it's possible to get parameter value by some kind of a "path".
So let's say we have this method:
fun updateRemoteConfig(val remoteConfig: RemoteConfig) { doSomething() }
RemoteConfig class is pretty nested, so the path to customerServiceId would be something like: remoteConfig.customerService.id
What I would like to achieve is mark the method with annotation:
#CustomerServiceAccess(customerServiceIdPath = "remoteConfig.customerService.id")
And the value would then be fetched inside Aspect method. But I have no idea how to get to the specified value by path. Is it even possible?
The unknown is where arrows are in the code. Here's rest of the aspect:
#Aspect
class AuthorizationAspect {
#Pointcut("#annotation(com.my.project.annotations.CustomerServiceAccess)")
fun customerServiceAccess() = Unit
#Before("customerServiceAccess()")
fun checkAccess(joinPoint: JoinPoint) {
val methodSignature = joinPoint.signature as MethodSignature
val method = methodSignature.method
val canAccess = mutableListOf<() -> Boolean>()
.apply {
addAll(method.getAnnotationsByType(CustomerServiceAccess::class.java).map { it.canAccess(method) })
}
.any { it() }
if (!canAccess) {
throw UnauthorizedException(message = "User cannot perform this action")
}
}
private fun CustomerServiceAccess.canAccess(val method: Method): () -> Boolean = {
->> val customerServiceIdParam = method.getParameterByPath(getCustomerServiceIdPath())
SecurityContext.isCustomerService && SecurityContext.customerServiceId == customerServiceIdParam
}
private fun CustomerServiceAccess.getCustomerServiceIdPath(): String = this.customerServiceIdPath
}
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class CustomerServiceAccess(val customerServiceIdPath: String)
I hava a class Packet.java(can't modify) in a package.
public class Packet implements java.io.Serializable, Cloneable {
private static final AtomicLong ID_ATOMICLONG = new AtomicLong();
private Long id = ID_ATOMICLONG.incrementAndGet();
}
I use own class LoginPacket.kt (can modify)
class LoginPacket : Packet () {
var id = "" ( this name must be id )
fun parsePacket(input: String): Boolean {
val map = HashMap<String,Any>()
map["id"] = "5d6ff3433354b4d43076419"
var wrapper: BeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(this)
wrapper.isAutoGrowNestedPaths = true
// question is here , I can not set id as String use BeanWrapper, Only can set id as Long
// and also I can replace id's getter and setter method
val pd = wrapper.getPropertyDescriptor("id")
pd.readMethod = LoginPacket::id.getter.javaMethod
pd.writeMethod = LoginPacket::id.setter.javaMethod
wrapper.setPropertyValues(map)
}
}
So what I can do next?
Thanks very much for sharing!
Beanwrapper link
It is not possible to override the type of a field.
What you can do instead depends on what you are trying to do, and which libraries you are using.
I can think of one way that may work, assuming your library does not need an instance nor subclass of Packet.
And that is creating your own class that only implements the interfaces:
class LoginPacket(): java.io.Serializable, Cloneable {
// You may or may not need this.
// Since the original version uses it to generate the ID,
// I think you can skip this part.
companion object {
#JvmStatic
private val ID_ATOMICLONG = AtomicLong()
}
var id : String = ""
fun parsePacket(input: String): Boolean {
val map = HashMap<String,Any>()
map["id"] = "5d6ff3433354b4d43076419"
var wrapper: BeanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(this)
wrapper.isAutoGrowNestedPaths = true
val pd = wrapper.getPropertyDescriptor("id")
pd.readMethod = LoginPacket::id.getter.javaMethod
pd.writeMethod = LoginPacket::id.setter.javaMethod
wrapper.setPropertyValues(map)
}
}
It is hard to provide better answers without more context.
I'm currently designing database for a mobile application. Recently I found very useful function to access database in background:
private val IO_EXECUTOR = Executors.newSingleThreadExecutor()
fun ioThread(f : () -> Unit) {
IO_EXECUTOR.execute(f)
}
Besides that I figured out that don't need synchronization code as the database will be accessed only in one thread (i.e the thread used by SingleThreadExecutor).
The only issue is that the following methods have to be restricted to be invoked only through ioThread function (or using IO_EXECUTOR).
abstract class MyDatabase : RoomDatabase() {
companion object {
fun init(context: Context) { ... }
fun getInstance() { ... }
}
Is it possible to achieve this in Kotlin/Java?
UPDATE: for now I have this implementation but think there should be better ones
// App.kt file
private val IO_EXECUTOR = Executors.newSingleThreadExecutor()
private var IO_THREAD_ID: Long = -1L
private fun getIOThreadId(): Long {
if (IO_THREAD_ID == -1L)
IO_THREAD_ID = IO_EXECUTOR.submit(Callable<Long> { Thread.currentThread().id }).get()
return IO_THREAD_ID
}
fun notInIOThread() = Thread.currentThread().id != getIOThreadId()
fun ioThread(f : () -> Unit) {
IO_EXECUTOR.execute(f)
}
and then use notInIOThread() in init() and getInstance() functions
If you absolutely need to make sure that the code is running on the correct thread, you could make use of a custom thread and then checking Thread.currentThread() for the interface.
private interface MarkedIOThread // Marker interface
private val IO_EXECUTOR = Executors.newSingleThreadExecutor { r ->
return object : Thread(r), MarkedIOThread
}
fun notInIOThread(): Boolean = Thread.currentThread() !is MarkedIOThread
Yes, you can use android annotations`s Worker Thread annotation.
When you annotate a method or class with #WorkerThread, android will give you lint errors if you call it from the UI thread.
You can read more about the #WorkerThread here: https://developer.android.com/reference/android/support/annotation/WorkerThread
And more about android annotations here: https://developer.android.com/studio/write/annotations
I would suggest that you should check room library: https://developer.android.com/topic/libraries/architecture/room
It is very powerful, if you don't have any specific reason to create a database library, room is your best bet.
I assume you want the functions to be called only inside ioThread code block, otherwise there'd be a type error. First make them member functions of a class with user-code-inaccessible constructor so others cannot call it directly:
class MyDslClass internal constructor() {
fun init(context: Context) { ... }
fun getInstance() { ... }
}
And ioThread should be:
fun ioThread(f : MyDslClass.() -> Unit) {
val dsl = MyDslClass()
IO_EXECUTOR.execute { dsl.f() }
}
Then you can restrict calls to those functions only inside ioThread block.
fun main(args: Array<String>) {
ioThread {
getInstance() // Ok
}
// cannot call `getInstance` since I cannot construct a `MyDslClass`
}
Is there any way to bind with provider which interprets target's annotation value in Google Guice?
Example:
bind(Resource.class)
.annotatedWith(MyAnnotation.class)
.toProvider(new MyProvider<MyAnnotation, Resource>{
public Resource get(MyAnnotation anno){
return resolveResourceByAnnoValue(anno.value());
}
});
I want to initialize field of an Android Activity class by annotated binding.
It should have to take multiple resources by it's unique Id.
Original Way:
public class TestActivity extends Activity{
private TextView textView;
private Button testButton;
public void onAfterCreate(...){
// set UI declaration resource.
setContentView(R.layout.activity_test);
// initialize fields, it must be done after setting ui definition.
textView = (TextView) findViewById(R.id.textView);
.... initialize other fields, hook them...
...
}
I want to bind UI and it's field in declarative way, not pragmatically likes above:
#ResourceID(R.layout.activity_test)
public class TestActivity extends InjectiveActivity{
#ResourceID(R.id.textView) // Auto generated static resource id constant
private TextView textView;
#ResourceID(R.id.testButton)
private Button testButton;
...
}
This isn't possible as such.
If #MyAnnotation is a binding annotation, it will be compared using its equals method. #MyAnnotation(5) Resource will be bound to #MyAnnotation(5) Resource, and that will not match at all compared to #MyAnnotation(6) Resource. Check out this SO answer for more. As in that answer, you could loop through your possible annotation values and bind each one individually, if you feel like it.
If #MyAnnotation isn't a binding annotation, you won't be able to access it at all from your provider. As mentioned in this SO answer, it is a rejected feature to add injection-site information to the provider or dependency itself.
Your best bet is to create an #Assisted injection (or manual factory) to accept the parameter:
class MyConsumer {
final Resource resource;
#Inject MyConsumer(Resource.Factory resourceFactory) {
int previouslyAnnotatedValue = 5;
this.resource = resourceFactory.createWithValue(previouslyAnnotatedValue);
}
}
You may also consider using Custom Injections, which will let you use an arbitrary annotation other than #Inject, which may use runtime annotation values however you'd like.
Here is an example in Scala (I like using Scala for prototyping, it's Java in a different dress after all) which I came up with after wondering about it myself in Dynamic Google Juice injection depending on value of an annotation
import java.lang.reflect.{Constructor, Parameter}
import java.util.concurrent.atomic.AtomicReference
import javax.inject.{Inject, Named, Provider}
import com.google.inject.matcher.Matchers
import com.google.inject.spi.ProvisionListener.ProvisionInvocation
import com.google.inject.{AbstractModule, Binder, Guice}
import com.google.inject.spi.{DependencyAndSource, ProviderInstanceBinding, ProvisionListener}
import com.typesafe.config.ConfigFactory
import net.codingwell.scalaguice.InjectorExtensions._
import net.codingwell.scalaguice.ScalaModule
import scala.collection.JavaConverters._
object GuiceExperiments extends App {
val injector = Guice.createInjector(new MyModule())
val some = injector.instance[Some]
println(some)
some.go()
}
trait Some {
def go(): Unit
}
class Impl #Inject()(
#Named("a.a.a") hello: String,
#Named("a.a.b") bello: String,
#Named("a.b.a") kello: String
) extends Some {
override def go() = {
println(hello)
println(bello)
println(kello)
}
}
abstract class DynamicProvider[T >: Null](binder: Binder) extends Provider[T] {
private[this] val nextValue = new AtomicReference[T]
binder.bindListener(Matchers.any(), new ProvisionListener {
private[this] def tryProvide(target: DependencyAndSource): Unit = {
val dependency = target.getDependency
val injectionPoint = dependency.getInjectionPoint
val parameterIndex = dependency.getParameterIndex
injectionPoint.getMember match {
case constructor: Constructor[_] =>
val parameter = constructor.getParameters()(parameterIndex)
nextValue.set(getFor(parameter))
}
}
override def onProvision[V](provision: ProvisionInvocation[V]): Unit = {
provision.getBinding match {
case binding: ProviderInstanceBinding[_] if binding.getUserSuppliedProvider eq DynamicProvider.this =>
provision.getDependencyChain.asScala.lastOption.foreach(tryProvide)
case _ => ()
}
}
})
final override def get(): T = nextValue.getAndSet(null)
def getFor(parameter: Parameter): T
}
class MyModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[Some].to[Impl]
bind[String].annotatedWith[Named].toProvider(new DynamicProvider[String](binder) {
override def getFor(parameter: Parameter): String = {
if (parameter.isAnnotationPresent(classOf[Named])) {
parameter.getAnnotation(classOf[Named]).value()
} else {
null
}
}
})
}
}
this only inserts the value of the #Named, but looks like it pretty damn works. so much for not possible.
I am very new to Gson and Json. I have simple Events that I want to serialize through Json with the help of Gson.
Note: Code in Kotlin.
public abstract class Event() {
}
public class Move : Event() {
var from: Point? = null
var to: Point? = null
}
public class Fire : Event() {
var damage: Int = 0
var area: ArrayList<Point> = ArrayList(0)
}
public class Build : Event() {
var to: Point? = null
var type: String = ""
var owner: String = ""
}
I am persisting bunch of these via this way:
val list: ArrayList<Event>() = ArrayList()
list.add(move)
list.add(fire)
val str = gson.toJson(events)
And unpersisting:
val type = object : TypeToken<ArrayList<Event>>(){}.getType()
val eventStr = obj.getString("events")
val events: ArrayList<Event> = gson.fromJson(eventStr, type)
I have tried both creating a serializer & deserializer for Event-class, and registering it via registerTypeAdapter, and I have also tried the RuntimeTypeAdapterFactory, but neither will persist the information required to unpersist the correct type.
For example, the RuntimeTypeAdapterFactory says:
"cannot deserialize Event because it does not define a field named type"
EDIT: Here's the code for the "Adapter", which was.. well, adapted from another StackOverflow post:
public class Adapter :
JsonSerializer<Event>,
JsonDeserializer<Event> {
final val CLASSNAME = "CLASSNAME"
final val INSTANCE = "INSTANCE"
override fun serialize(src: Event?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement? {
val obj = JsonObject()
val className = (src as Event).javaClass.getCanonicalName()
obj.addProperty(CLASSNAME, className)
val elem = context!!.serialize(src)
obj.add(INSTANCE, elem)
return obj
}
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Event? {
val jsonObject = json!!.getAsJsonObject()
val prim = jsonObject.get(CLASSNAME)
val className = prim.getAsString()
val klass = Class.forName(className)
return context!!.deserialize(jsonObject.get(INSTANCE), klass)
}
}
This code fails with NullPointerException on line:
val className = prim.getAsString()
You can't do it this way.
The example you are referring is not targeted to your case. It works in only one case: if you register base type (not type hierarchy) and serialize using gson.toJson(obj, javaClass<Event>()). It will never work for array except you write custom serializer for you events container object too
Generally you need another approach: use TypeAdapterFactory and delegate adapters: GSON: serialize/deserialize object of class, that have registered type hierarchy adapter, using ReflectiveTypeAdapterFactory.Adapter and https://code.google.com/p/google-gson/issues/detail?id=43#c15
I believe this approach is overcomplicated so if you have few types the easiest solution is two serialize these types by hand, field by field via custom serializer and forget about attempts to delegate to default