Akka Camel and ActiveMQ: How to set delivery mode - scala

As far as I know, ActiveMQ sets delivery mode to PERSISTENT by default... so how do I set delivery mode to NON_PERSISTENT for a specific topic when using Akka-Camel? Here below is my sample code:
import akka.actor._
import akka.camel._
import org.apache.activemq.camel.component.ActiveMQComponent
case class MyMessage(body: String)
class MyProducer() extends Actor with Producer with Oneway {
def endpointUri: String = "activemq:MyTopic"
}
class SimpleConsumer() extends Actor with Consumer {
def endpointUri: String = "activemq:MyTopic"
def receive = {
case msg: CamelMessage => println(msg)
}
}
object MyApp extends App {
val actorSystem = ActorSystem("MyApp")
val system = CamelExtension(actorSystem)
system.context.addComponent(
"activemq",
ActiveMQComponent.activeMQComponent("nio://localhost:61616")
)
val consumer = actorSystem.actorOf(Props[MyConsumer])
val producer = actorSystem.actorOf(Props[MyProducer])
...
producer ! MyMessage("hello")
...
actorSystem.shutdown()
}

The options are set on the endpoint URI.
"activemq:MyTopic?deliveryPersistent=false"

Related

How to use loan-fixture method with AsyncFeatureSpec?

How can I use loan-fixture method with AsyncFeatureSpec?
I have tried as the following:
import akka.actor._
import akka.testkit._
import com.sweetsoft._
import org.scalatest._
import scala.concurrent.duration._
class SapOnlineKafkaOnlineSpec extends fixture.AsyncFeatureSpec with Matchers
with GivenWhenThen
with BeforeAndAfter
with BeforeAndAfterAll {
private val listener1 = TestProbe()
private val detector = system.actorOf(DetectorSupervisor.props)
def withKafkaAndSap(testCode: (ActorRef) => Any) {
}
feature("Detect Kafka and SAP availability") {
info("As a technical user, I want to be notified in real time, if Kafka and SAP is up and running or not.")
scenario("SAP and Kafka are available") in withKafkaAndSap { (listener) =>
Given("I am waiting for the current state message")
detector ! AddNewListener(listener1.ref)
When("I am receive the state message")
val res1 = listener1.expectMsgPF[Assertion](6.second) _
Then("it should contain `SAP and Kafka are online`")
res1 {
case status: ServerStatus =>
status.health should be(ServersOnline)
}
}
}
}
I do not know, how to inject the loan method.
Try the following
class SapOnlineKafkaOnlineSpec extends AsyncFeatureSpec with Matchers
with GivenWhenThen
with BeforeAndAfter
with BeforeAndAfterAll {
def withKafkaAndSap(testCode: ActorRef => Future[Assertion]) = {
val actor = ...
testCode(actor)
}
feature("Detect Kafka and SAP availability") {
info("As a technical user, I want to be notified in real time, if Kafka and SAP is up and running or not.")
scenario("SAP and Kafka are available") {
withKafkaAndSap { listner =>
Given("I am waiting for the current state message")
When("I am receive the state message")
Then("it should contain `SAP and Kafka are online`")
succeed
}
}
}
}
Note the loan fixture parameter type should be
testCode: ActorRef => Future[Assertion]
instead of ActorRef => Any, and we extend just AsyncFeatureSpec instead of fixture.AsyncFeatureSpec

Remote actor not responding to Identify?

I'm just messing around with some basics to learn akka remoting, and I'm running into an issue where my proxy class sends Identify messages to my backend, but never receives a response back.
I've verified that the backend receives messages sent to the ActorSelection, and I've seen a logging message where the backend actor says it will use xxx for serialization of the ActorIdentity messages. Not sure where I'm getting it wrong.
Here is my proxy class:
package remoting
import akka.actor.{Actor, ActorIdentity, ActorRef, Identify, ReceiveTimeout, Terminated}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
class BackendProxyActor extends Actor {
context.setReceiveTimeout(3.seconds)
val path = createPath
val backendSelection = context.actorSelection(path)
println(f"BackendSelection is $backendSelection")
override def preStart(): Unit = backendSelection ! Identify(1)
override def receive: Receive = {
case ActorIdentity(1, Some(actor)) =>
context.setReceiveTimeout(Duration.Undefined)
context.watch(actor)
context.become(active(actor))
case ActorIdentity(1, None) =>
println("Backend actor not available")
case ReceiveTimeout =>
backendSelection ! Identify(1)
case msg: Any => println(f"Received $msg while identifying backend")
}
def active(backend: ActorRef): Receive = {
case msg: Any => backend ! msg
case Terminated(backend) =>
println("backend terminated, going to identifying state")
context.setReceiveTimeout(3.seconds)
context.become(receive)
}
def createPath: String = {
val config = ConfigFactory.load("frontend").getConfig("backend")
val name = config.getString("name")
val host = config.getString("host")
val port = config.getString("port")
val system = config.getString("system")
val protocol = config.getString("protocol")
f"$protocol://$system#$host:$port/$name"
}
}
Here is my backend class:
package remoting
import akka.actor.{Actor, Identify, PoisonPill}
import com.typesafe.config.ConfigFactory
class BackendActor extends Actor {
val config = ConfigFactory.load("backend")
override def receive: Receive = {
case "stop" => self ! PoisonPill
case msg: Any => println(f"Received $msg")
}
}
My frontend class:
package remoting
import akka.actor.{Actor, Props}
class FrontendActor extends Actor {
val proxy = context.actorOf(Props[BackendProxyActor], "backendProxy")
override def receive: Receive = {
case msg: Any => proxy ! msg
}
}
and finally my App class:
package remoting
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Main extends App {
val frontendConfig = ConfigFactory.load("frontend")
val frontend = ActorSystem("frontend", frontendConfig)
val frontendActor = frontend.actorOf(Props[FrontendActor], "FrontendActor")
(1 to 20).foreach(i => frontendActor ! f"Msg #$i")
frontendActor ! "stop"
}
My backend is being started in another process, and is run on port 2551, while my frontend is on port 2552.

No endpoint could be found for: test, please check your classpath contains the needed Camel component jar

I am trying to send and receive messages using akka-camel and created a sample example for producer and consumer like below :
Producer:
import akka.actor.{Actor, ActorSystem, Props}
import akka.camel.Producer
class CamelJmsProducer extends Actor with Producer {
override def endpointUri = "test"
}
object CamelJmsProducerApp extends App {
val system = ActorSystem("some-system")
val ref = system.actorOf(Props[CamelJmsProducer])
ref ! "HEY"
}
Consumer:
import akka.actor.{Actor, ActorSystem, Props}
import akka.camel.{CamelMessage, Consumer}
class CamelJmsConsumer extends Actor with Consumer {
override def receive = {
case msg: CamelMessage ⇒ println("RECEIVED >>> " + msg)
case _ ⇒ println("RECEIVED NOTHING>>> ")
}
override def endpointUri = "test"
}
object CamelJmsConsumerApp extends App {
val system = ActorSystem("some-system1")
system.actorOf(Props[CamelJmsConsumer])
}
But I am facing issue in both producer and consumer like below. What I am missing?
Producer:
java.lang.IllegalArgumentException: destination must be specified
Consumer :
Caused by: org.apache.camel.NoSuchEndpointException: No endpoint could
be found for: test, please check your classpath contains the needed
Camel component jar.
I believe you need to provide a name to the test mock endpoint, just test might not work. Can you try doing doing test:myMockEndpoint?
You can take a look here: http://camel.apache.org/components.html

Apache Bahir, send stuff to ActorReceiver

I am trying to setup a simple process with Spark Streaming, using Apache Bahir to connect to Akka. I tried to follow their example together with this older one. I have a simple forwarder actor
class ForwarderActor extends ActorReceiver {
def receive = {
case data: MyData => store(data)
}
}
and I create a stream with
val stream = AkkaUtils.createStream[RSVP](ssc, Props[ForwarderActor], actorName)
the configuration looks like this:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "localhost"
port = 7777
}
}
}
and my problem is: how do I send messages to the Forwarder actor? Maybe I don't understand how Akka Remote is used in this case. When the app starts, I see a log
[akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://test#localhost:7777]
and later on I see
[akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://streaming-actor-system-0#192.168.192.7:52369]
Which seems to remind to the description in the ScalaDoc:
/**
* A default ActorSystem creator. It will use a unique system name
* (streaming-actor-system-<spark-task-attempt-id>) to start an ActorSystem that supports remote
* communication.
*/
All in all I am not sure how I am supposed to send messages to the Forwarder actor. Thanks for any help!
Akka actors can send messages to other Akka actors running on a remote JVM. So... when the sender actor needs to know the address of the intended receiver actor.
AkkaUtil (Bahir) enables you to create a spark-stream from the messages that a ReceiverActor receives. But, where is is going to receive messages from ? Well... some remote actor. And to send messages this remote actor is going to need the address of your ReceiverActor which is running in your spark-application.
In general, you can not be too sure about the ip which will be running your spark application. So, we will make it so that the actor running with spark will tell the producer actor its reference and request it to send its things.
Just make sure that both applications are written using same version of Scala and are running the same JRE.
Now... lets first write the actor who will be the data source,
import akka.actor.{Actor, ActorRef, ActorLogging, ActorSystem, Props}
import akka.actor.Actor.Receive
import com.typesafe.config.{Config, ConfigFactory}
case class SendMeYourStringsRequest(requesterRef: ActorRef)
case class RequestedString(s: String)
class MyActor extends Actor with ActorLogging {
val theListOfMyStrings = List("one", "two", "three")
override def receive: Receive = {
case SendMeYourStringsRequest(requesterRef) => {
theListOfMyStrings.foreach(s => {
requesterRef ! RequestedString(s)
})
}
}
}
object MyApplication extends App {
val config = ConfigFactory.parseString(
"""
|akka{
| actor {
| provider = remote
| }
| remote {
| enabled-transports = ["akka.remote.netty.tcp"]
| untrusted-mode = off
| netty.tcp {
| hostname="my-ip-address"
| port=18000
| }
| }
|}
""".stripMargin
)
val actorSystem = ActorSystem("my-actor-system", config)
var myActor = actorSystem.actorOf(Props(classOf[MyActor]), "my-actor")
}
Now... lets write our simple spark app,
import akka.actor.{Actor, ActorRef, ActorLogging, ActorSystem, Props}
import akka.actor.Actor.Receive
import com.typesafe.config.{Config, ConfigFactory}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.akka.{ActorReceiver, AkkaUtils}
case class SendMeYourStringsRequest(requesterRef: ActorRef)
case class RequestedString(s: String)
class YourStringRequesterActor extends ActorReceiver {
def receive = {
case RequestedString(s) => store(s)
}
override def preStart(): Unit = {
val myActorPath = ActorPath.fromString("akka.tcp://my-actor-system#my-ip-address:18000/user/my-actor")
val myActorSelection = context.actorSelection(myActorPath)
myActorSelection ! SendMeYourStringsRequest(self)
}
}
object YourSparkApp {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setAppName("ActorWordCount")
if (!sparkConf.contains("spark.master")) {
sparkConf.setMaster("local[2]")
}
val ssc = new StreamingContext(sparkConf, Seconds(2))
val stringStream = AkkaUtils.createStream[String](
ssc,
Props(classOf[YourStringRequesterActor]),
"your-string-requester-actor"
)
stringStream.foreach(println)
}
}
Note :: Just take care of my-ip-address. If there are any other problems, please let me know in comments.

Play Scala Akka WebSockets change actor path

I've followed the example for creating Web Sockets with Scala Play and Akka actors:
https://www.playframework.com/documentation/2.5.x/ScalaWebSockets#Handling-WebSockets-with-Akka-Streams-and-actors
On resume, the controller:
import play.api.mvc._
import play.api.libs.streams._
class Controller1 #Inject() (implicit system: ActorSystem, materializer: Materializer) {
def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}
And the Actor:
import akka.actor._
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
class MyWebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
The actors created (one per websocket connection) are child of /user actor. I've created 3 connections and the actor created were:
/user/$b
/user/$c
/user/$d
I want to change the actors' name based in a field of the web socket message. How could i do this?.
You can set the name of the actor as follows:
Create a file BetterActorFlow.scala
package your.package
import akka.actor._
import akka.stream.scaladsl.{Keep, Sink, Source, Flow}
import akka.stream.{Materializer, OverflowStrategy}
object BetterActorFlow {
def actorRef[In, Out](props: ActorRef => Props, bufferSize: Int = 16, overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew, maybeName: Option[String] = None)(implicit factory: ActorRefFactory, mat: Materializer): Flow[In, Out, _] = {
val (outActor, publisher) = Source.actorRef[Out](bufferSize, overflowStrategy)
.toMat(Sink.asPublisher(false))(Keep.both).run()
def flowActorProps: Props = {
Props(new Actor {
val flowActor = context.watch(context.actorOf(props(outActor), "flowActor"))
def receive = {
case Status.Success(_) | Status.Failure(_) => flowActor ! PoisonPill
case Terminated(_) => context.stop(self)
case other => flowActor ! other
}
override def supervisorStrategy = OneForOneStrategy() { case _ => SupervisorStrategy.Stop }
})
}
def actorRefForSink =
maybeName.fold(factory.actorOf(flowActorProps)) { name => factory.actorOf(flowActorProps, name) }
Flow.fromSinkAndSource(Sink.actorRef(actorRefForSink, Status.Success(())), Source.fromPublisher(publisher))
}
}
Use BetterActorFlow instead of ActorFlow:
BetterActorFlow.actorRef(out =>
ChatActor.props(out), 16, OverflowStrategy.dropNew, Some("alicebob"))
This worked for me. The created actor is at user/alicebob (use this with context.system.actorSelection("user/alicebob"))
According to the source code of ActorFlow, it is currently not possible to thange the name of the actual actor spawned for a connection (line 38):
Sink.actorRef(factory.actorOf(Props(new Actor { ... }) /*, name parameter is omitted */)
However, ActorFlow.actorRef accepts an implicit ActorRefFactory, which is implicit system: ActorSystem in all cases in your code. As we know, there are 2 most common ActorRefFactories: ActorSystem and ActorContext. You can modify your code in such a way that each time a connection is accepted another dummy actor would spawn with your preferred name (e.g. myActor1), and pass this new actor's context to ActorFlow.actorRef instead. In return, actors for connections would be named as follows:
/user/myActor1/$a
/user/myActor2/$a
etc