Akka actor for http request Java - akka

Hello I am trying to look for a simple example in AKKA - Java to create a HTTP Client un an Actor. So far I am able to create a request and get the response Http Entity. I need to migrate it to an actor , so I can call multiple actors in parallel with a time out.
final ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
final List<HttpRequest> httpRequests = Arrays.asList(
HttpRequest.create(url) // Content-Encoding: gzip in respons
);
Unmarshaller<ByteString, BitTweet> unmarshal = Jackson.byteStringUnmarshaller(BitTweet.class);
JsonEntityStreamingSupport support = EntityStreamingSupport.json();
final Http http = Http.get(system);
final Function<HttpResponse, HttpResponse> decodeResponse = response -> {
// Pick the right coder
final Coder coder;
if (HttpEncodings.gzip().equals(response.encoding())) {
coder = Coder.Gzip;
} else if (HttpEncodings.deflate().equals(response.encoding())) {
coder = Coder.Deflate;
} else {
coder = Coder.NoCoding;
}
// Decode the entity
return coder.decodeMessage(response);
};
List<CompletableFuture<HttpResponse>> futureResponses = httpRequests.stream()
.map(req -> http.singleRequest(req, materializer)
.thenApply(decodeResponse))
.map(CompletionStage::toCompletableFuture)
.collect(Collectors.toList());
for (CompletableFuture<HttpResponse> futureResponse : futureResponses) {
final HttpResponse httpResponse = futureResponse.get();
system.log().info("response is: " + httpResponse.entity()
.toStrict(1, materializer)
.toCompletableFuture()
.get());
HttpEntity.Strict entity_ = HttpEntities.create(ContentTypes.APPLICATION_JSON, httpResponse.entity().toString());
Source<BitTweet, Object> BitTweet =
entity_.getDataBytes()
.via(support.framingDecoder()) // apply JSON framing
.mapAsync(1, // unmarshal each element
bs -> unmarshal.unmarshal(bs, materializer)
);

Related

how do i process Flux<Object> returned by webflux at client side in plain java code

I have written a webservice using spring webflux and reactive mongodb connectors, but my client side could be non spring based client.
So, how do I write a plain java code to consume flex at client side?
ServerSide code:
#GetMapping(value = "/findAll")
public Flux<Security> findAll() {
Flux<Security> flux = service.findAll();
return flux;
}
Client side code:
public static void sendRequest() {
try {
long start = System.currentTimeMillis();
for (int i = 0; i <= 100; i++) {
long start1 = System.currentTimeMillis();
URL url = new URL("http://localhost:8080/findAll/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/stream+json");
if (conn.getResponseCode() == 200) {
// url = new URL("http://localhost:8182/status/");
String json = "";
try (BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())))) {
json = br.lines().collect(Collectors.joining());
}
conn.disconnect();
System.out.println("size of each Security: " + json.length());
ArrayList<Security> list = getListOfsecurities(json);
System.out.println(list.get(0).getIsin());
}
}
The above client side gives me an empty array.
I don't think it is possible. It's an asynchronous response.At least you have to use Java 5 Futures to invoke asynchronous response.

How to send Akka broadcast message

I'm trying to send a single message to several actors. Investigation led me to the code below, but it doesn't work. The message that's "wrapped" in the Broadcast object disappears, and the plain string ends up in the dead letter box.
Can someone tell me what I'm missing? (Edit: I've added the correction below)
import akka.actor.{Actor, ActorSystem, Props}
import akka.routing.{Broadcast, BroadcastRoutingLogic, Router}
object RouterAndBroadcast {
class MyRoutee extends Actor {
override def receive: Receive = {
case x => println(s"MyRoutee $this got message $x")
}
}
def main(args: Array[String]): Unit = {
val system = ActorSystem.create("system")
val mr0 = system.actorOf(Props[MyRoutee])
val mr1 = system.actorOf(Props[MyRoutee])
val mr2 = system.actorOf(Props[MyRoutee])
/* This was the error:
val router = new Router(BroadcastRoutingLogic())
router.addRoutee(mr1)
router.addRoutee(mr2) */
// This is the corrected version:
val router = new Router(BroadcastRoutingLogic())
.addRoutee(mr1)
.addRoutee(mr2)
mr1 ! "Hello number one!"
mr2 ! "Ahoy two, me old mate!"
router.route(new Broadcast("Listen up!"), mr0) // vanishes??
router.route("Listen up!", mr0) // ends up in dead letters
mr1 ! "Number one, are you still there?"
mr2 ! "Two, where's the grog?"
router.route(new Broadcast("Still shouting!"), mr0) // also vanishes
Thread.sleep(5000)
system.terminate()
}
}
Router.addRoutee returns a copy with the routee added, it doesn't modify ther Router in place, see:
https://github.com/akka/akka/blob/b94e064a34a7f6a9d1fea55317d5676731ac0778/akka-actor/src/main/scala/akka/routing/Router.scala#L140
/**
* Create a new instance with one more routee and the same [[RoutingLogic]].
*/
def addRoutee(routee: Routee): Router = copy(routees = routees :+ routee)
so instead try
router = router.addRoutee(mr1).addRoutee(mr2)

Access the messages of actor mailbox

I want to read the messages in ShardRegions mailbox. In previous akka version, we could use the following code to get only the size of mailbox:
getContext().getMailboxSize();
Is there any way to fetch the type of messages in the mailbox?
Here is the solution
Define a wrapper mailbox for shard region like this:
class UnboundedMailboxWrapper extends MailboxType with ProducesMessageQueue[UnboundedMailboxWrapper.MessageQueue] {
def this(settings: ActorSystem.Settings, config: Config) = this()
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
new UnboundedMailboxWrapper.MessageQueue
}
object UnboundedMailboxWrapper {
class MessageQueue extends ConcurrentLinkedQueue[Envelope] with UnboundedQueueBasedMessageQueue {
val runtime = RuntimeManager.runtime
final def queue: Queue[Envelope] = this
override def enqueue(receiver: ActorRef, handle: Envelope): Unit = {
runtime.queue.add(handle)
queue add handle
}
override def dequeue(): Envelope = {
if (!runtime.queue.isEmpty) runtime.mailbox.queue.poll()
queue.poll()
}
}
}
in this mailbox we duplicate the element which added, to another queue, so when calculate size and other operation on it, there is no any impact on performance of mailbox queue
In duplicated queue we can calculate the number of messages and sort them:
def getElemets(): String = {
runtime.queue.asScala.toList.groupBy(_.message.getClass.getName)
.map(e ⇒ (e._1, e._2.length)).toSeq
.sortBy(_._2).foldLeft("") { (a, b) ⇒
b._1 + ":" + b._2 + "\n" + a
}
}
and with JMX or any other way we can call this method in runtime
and finlally assign this mailbox to ShardRegion dispatcher:
monit-dispatcher {
mailbox-type = "im.actor.server.cluster.UnboundedMailboxWrapper"
}
akka.cluster.sharding {
use-dispatcher = "monit-dispatcher"
}

akka stream custom graph stage

I have an akka stream from a web-socket like akka stream consume web socket and would like to build a reusable graph stage (inlet: the stream, FlowShape: add an additional field to the JSON specifying origin i.e.
{
...,
"origin":"blockchain.info"
}
and an outlet to kafka.
I face the following 3 problems:
unable to wrap my head around creating a custom Inlet from the web socket flow
unable to integrate kafka directly into the stream (see the code below)
not sure if the transformer to add the additional field would be required to deserialize the json to add the origin
The sample Project (flow only) looks like:
import system.dispatcher
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
val incoming: Sink[Message, Future[Done]] =
Flow[Message].mapAsync(4) {
case message: TextMessage.Strict =>
println(message.text)
Future.successful(Done)
case message: TextMessage.Streamed =>
message.textStream.runForeach(println)
case message: BinaryMessage =>
message.dataStream.runWith(Sink.ignore)
}.toMat(Sink.last)(Keep.right)
val producerSettings = ProducerSettings(system, new ByteArraySerializer, new StringSerializer)
.withBootstrapServers("localhost:9092")
val outgoing = Source.single(TextMessage("{\"op\":\"unconfirmed_sub\"}")).concatMat(Source.maybe)(Keep.right)
val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("wss://ws.blockchain.info/inv"))
val ((completionPromise, upgradeResponse), closed) =
outgoing
.viaMat(webSocketFlow)(Keep.both)
.toMat(incoming)(Keep.both)
// TODO not working integrating kafka here
// .map(_.toString)
// .map { elem =>
// println(s"PlainSinkProducer produce: ${elem}")
// new ProducerRecord[Array[Byte], String]("topic1", elem)
// }
// .runWith(Producer.plainSink(producerSettings))
.run()
val connected = upgradeResponse.flatMap { upgrade =>
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
Future.successful(Done)
} else {
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
system.terminate
}
}
// kafka that works / writes dummy data
val done1 = Source(1 to 100)
.map(_.toString)
.map { elem =>
println(s"PlainSinkProducer produce: ${elem}")
new ProducerRecord[Array[Byte], String]("topic1", elem)
}
.runWith(Producer.plainSink(producerSettings))
One issue is around the incoming stage, which is modelled as a Sink. where it should be modelled as a Flow. to subsequently feed messages into Kafka.
Because incoming text messages can be Streamed. you can use flatMapMerge combinator as follows to avoid the need to store entire (potentially big) messages in memory:
val incoming: Flow[Message, String, NotUsed] = Flow[Message].mapAsync(4) {
case msg: BinaryMessage =>
msg.dataStream.runWith(Sink.ignore)
Future.successful(None)
case TextMessage.Streamed(src) =>
src.runFold("")(_ + _).map { msg => Some(msg) }
}.collect {
case Some(msg) => msg
}
At this point you got something that produces strings, and can be connected to Kafka:
val addOrigin: Flow[String, String, NotUsed] = ???
val ((completionPromise, upgradeResponse), closed) =
outgoing
.viaMat(webSocketFlow)(Keep.both)
.via(incoming)
.via(addOrigin)
.map { elem =>
println(s"PlainSinkProducer produce: ${elem}")
new ProducerRecord[Array[Byte], String]("topic1", elem)
}
.toMat(Producer.plainSink(producerSettings))(Keep.both)
.run()

java.io.IOException: Unable to write on channel java.nio.channels.SocketChannel in Play Framework

I'm trying to upload a byte array to a remote WS with multipart/form-data in play! framework using Scala. My code is:
//create byte array from file
val myFile = new File(pathName)
val in = new FileInputStream(myFile)
val myByteArray = new Array[Byte](audioFile.length.toInt)
in.read(audioByteArray)
in.close()
// create parts
val langPart = new StringPart("lang", "pt")
val taskPart = new StringPart("task","echo")
val audioPart = new ByteArrayPart("sbytes", "myFilename", myByteArray, "default/binary", "UTF-8")
val client: AsyncHttpClient = WS.client
val request = client.preparePost(RemoteWS)
.addHeader("Content-Type", "multipart/form-data")
.addBodyPart(audioPart)
.addBodyPart(taskPart)
.addBodyPart(langPart).build()
val result = Promise[Response]()
// execute request
client.executeRequest(request, new AsyncCompletionHandler[AHCResponse]{
override def onCompleted(response: AHCResponse): AHCResponse = {
result.success(Response(response))
response
}
override def onThrowable(t: Throwable) {
result.failure(t)
}
})
// handle async response
result.future.map(result =>{
Logger.debug("response: " + result.getAHCResponse.getResponseBody("UTF-8"))
})
Every time I execute this request, it throws this exception:
java.io.IOException: Unable to write on channel java.nio.channels.SocketChannel
The remote server is OK, I can make sucessfull requests using for example Postman.
I'm using Play Framework:
play 2.2.3 built with Scala 2.10.3 (running Java 1.8.0_05)
Version of AsyncHttpClient:
com.ning:async-http-client:1.7.18
Any help is welcome!