Mashup - laforge49/Asynchronous-Functional-Programming GitHub Wiki
Applications will typically require a mashup of several different types of persistent data. We will explore how to do this by looking at a simple application class which has a title and a list of strings. We will start with the messages/requests supported by the application.
case class Title()
case class SetTitle(transactionContext: TransactionContext, title: String)
case class Strings()
case class AddString(transactionContext: TransactionContext, string: String)
case class GetString(index: Int)
- The result returned for a Title request is the application's title, or null.
- SetTitle is used to set the title. The returned result is null.
- Strings returns the IncDesList[IncDesString] object.
- AddString adds a string to the list of strings.
- GetString returns the selected string.
We implement the application logic as a Component.
class MashupComponent(actor: Actor) extends Component(actor) {
bind(classOf[Title], title)
bind(classOf[SetTitle], setTitle)
bind(classOf[Strings], strings)
bind(classOf[AddString], addString)
bind(classOf[GetString], getString)
def title(msg: Any, rf: Any => Unit) {
actor(GetValue2("title"))(rf)
}
def setTitle(msg: AnyRef, rf: Any => Unit) {
val st = msg.asInstanceOf[SetTitle]
val transactionContext = st.transactionContext
val t = st.title
actor(PutString(transactionContext, "title", t))(rf)
}
def strings(msg: AnyRef, rf: Any => Unit) {
actor(GetValue("strings"))(rf)
}
def addString(msg: AnyRef, rf: Any => Unit) {
val st = msg.asInstanceOf[AddString]
val transactionContext = st.transactionContext
val t = st.string
actor(MakePutMakeSet(transactionContext, "strings", INC_DES_STRING_LIST_FACTORY_ID)) {
r1 => {
val strings = r1.asInstanceOf[IncDesList[IncDesString, String]]
val ids = IncDesString(mailbox)
strings(Add[IncDesString, String](transactionContext, ids)) {
r2 => {
ids(Set(transactionContext, t))(rf)
}
}
}
}
}
def getString(msg: AnyRef, rf: Any => Unit) {
val st = msg.asInstanceOf[GetString]
val index = st.index
actor(GetValue("strings")) {
r1 => {
if (r1 == null) {
rf(null)
return
}
val strings = r1.asInstanceOf[IncDesList[IncDesString, String]]
strings(GetValue(index))(rf)
}
}
}
}
Now all we need is a component factory and a factory.
object MashupComponentFactory extends ComponentFactory {
override def instantiate(actor: Actor) = new MashupComponent(actor)
}
class MashupFactory extends IncDesStringIncDesMapFactory(FactoryId("mashup")) {
include(MashupComponentFactory)
}
Here's the test code.
val systemServices = SystemServices(new IncDesComponentFactory)
val mashupFactory = new MashupFactory
mashupFactory.configure(systemServices)
val mashup1 = mashupFactory.newActor(new ReactorMailbox)
mashup1.setSystemServices(systemServices)
Future(mashup1, Title()) must beNull
Future(mashup1, SetTitle(null, "123"))
Future(mashup1, Title()) must be equalTo ("123")
Future(mashup1, MakePut(null, "nada"))
Future(mashup1, GetString(0)) must beNull
Future(mashup1, AddString(null, "Laundry"))
Future(mashup1, GetString(0)) must be equalTo("Laundry")
val bs1 = Future(mashup1, Bytes()).asInstanceOf[Array[Byte]]
val mashup2 = mashupFactory.newActor(new ReactorMailbox).asInstanceOf[IncDes]
mashup2.setSystemServices(systemServices)
mashup2.load(bs1)
Future(mashup2, Title()) must be equalTo ("123")
Future(mashup2, SetTitle(null, "42"))
Future(mashup2, Title()) must be equalTo ("42")
Future(mashup2, AddString(null, null))
Future(mashup2, AddString(null, "Dishes"))
Future(mashup2, GetString(0)) must be equalTo("Laundry")
Future(mashup2, GetString(1)) must beNull
Future(mashup2, GetString(2)) must be equalTo("Dishes")
val bs2 = Future(mashup2, Bytes()).asInstanceOf[Array[Byte]]
val mashup3 = mashupFactory.newActor(new ReactorMailbox).asInstanceOf[IncDes]
mashup3.setSystemServices(systemServices)
mashup3.load(bs2)
Future(mashup3, Title()) must be equalTo ("42")
Future(mashup3, GetString(2)) must be equalTo("Dishes")
Future(mashup3, GetString(1)) must beNull
Future(mashup3, GetString(0)) must be equalTo("Laundry")
println("")
val mashupSeq = Future(mashup3, Seq()).asInstanceOf[Sequence[String, IncDesIncDes]]
println("mashupSeq:")
Future(mashupSeq, Loop((key: String, item: IncDesIncDes) => println(key+" "+item)))
println("")
val mashupValuesSeq = Future(mashup3, ValuesSeq()).asInstanceOf[Sequence[String, IncDes]]
println("mashupValuesSeq:")
Future(mashupValuesSeq, Loop((key: String, value: IncDes) => println(key+" "+value)))
println("")
val flatMashupValuesSeq = Future(mashup3, FlatValuesSeq()).asInstanceOf[Sequence[String, IncDes]]
println("flatMashupValuesSeq:")
Future(flatMashupValuesSeq, Loop((key: String, value: IncDes) => println(key+" "+value)))
println("")
val mashupStrings = Future(mashup3, Strings()).asInstanceOf[IncDesList[IncDesString, String]]
val stringsSeq = Future(mashupStrings, Seq()).asInstanceOf[Sequence[Int, IncDesString]]
println("stringsSeq:")
Future(stringsSeq, Loop((key: Int, item: IncDesString) => println(key+" "+item)))
println("")
val stringValuesSeq = Future(mashupStrings, ValuesSeq()).asInstanceOf[Sequence[Int, String]]
println("stringValuesSeq:")
Future(stringValuesSeq, Loop((key: Int, value: String) => println(key+" "+value)))
println("")
val flatStringValuesSeq = Future(mashupStrings, FlatValuesSeq()).asInstanceOf[Sequence[Int, String]]
println("flatStringValuesSeq:")
Future(flatStringValuesSeq, Loop((key: Int, value: String) => println(key+" "+value)))
println("")
And the output.
mashupSeq:
nada org.agilewiki.incDes.IncDesIncDes@722d95b3
strings org.agilewiki.incDes.IncDesIncDes@337b4703
title org.agilewiki.incDes.IncDesIncDes@6273305c
mashupValuesSeq:
nada null
strings org.agilewiki.incDes.IncDesList@45c3987
title org.agilewiki.incDes.IncDesString@42c4d04d
flatMashupValuesSeq:
strings org.agilewiki.incDes.IncDesList@45c3987
title org.agilewiki.incDes.IncDesString@42c4d04d
stringsSeq:
0 org.agilewiki.incDes.IncDesString@1fbbdd48
1 org.agilewiki.incDes.IncDesString@66bcb23e
2 org.agilewiki.incDes.IncDesString@6e5a3923
stringValuesSeq:
0 Laundry
1 null
2 Dishes
flatStringValuesSeq:
0 Laundry
2 Dishes