In order to upload photos to Facebook from the server-side, the Graph API requires you to make a POST request with the image attached as multipart/form-data. Since I couldn’t find any native Scala libraries that make this task easy or intuitive, I went with the battle-tested Apache HttpComponents library.
Since the app was going to upload photos to Facebook in an on-demand fashion, I went with an HTTPClient that could be reused and was able handle multiple requests concurrently.
In this case source is the name of the form field whose value is the image file. The next step is to create a POST request and execute it using our httpClient.
importjava.io.Fileimportjava.net.URIimportorg.apache.http.client.methods.{HttpPost,CloseableHttpResponse}importorg.apache.http.client.protocol.HttpClientContextimportorg.apache.http.entity.mime.MultipartEntityBuilderimportorg.apache.http.entity.mime.content.{StringBody,FileBody}importorg.apache.http.entity.ContentTypeimportorg.apache.http.util.EntityUtilsimportorg.apache.http.impl.conn.PoolingHttpClientConnectionManagerimportorg.apache.http.impl.client.HttpClientsimportscala.util.TryobjectUploader{lazyvalhttpClient={valconnManager=newPoolingHttpClientConnectionManager()HttpClients.custom().setConnectionManager(connManager).build()}defuploadToFacebook(file:File,accessToken:String):Try[String]={valuri=newURI("https://graph.facebook.com/me/photos")Try({// Create the entityvalreqEntity=MultipartEntityBuilder.create()// Attach the filereqEntity.addPart("source",newFileBody(file))// Attach the access token as plain textvaltokenBody=newStringBody(accessToken,ContentType.TEXT_PLAIN)reqEntity.addPart("access_token",tokenBody)// Create POST requestvalhttpPost=newHttpPost(uri)httpPost.setEntity(reqEntity.build())// Execute the request in a new HttpContextvalctx=HttpClientContext.create()valresponse:CloseableHttpResponse=httpClient.execute(httpPost,ctx)// Read the responsevalentity=response.getEntityvalresult=EntityUtils.toString(entity)// Close the responseif(response!=null)response.close()result})}}
Two steps are needed in order to correctly apply back-pressure in an Akka system:
Step 1: Bounded Mailboxes and Push Timeouts
The default mailbox for an actor is an UnboundedMailbox backed by Java’s ConcurrentLinkedQueue. As the name indicates, this mailbox grows without bound and will end up crashing the JVM with an OutOfMemoryError if the consumer significantly slower than the producer. If we want to be able to signal the producer to slow down, the first step is to switch to a BoundedMailbox backed by Java’s LinkedBlockingQueue that will block the producer if the mailbox is full. More info about different types of mailboxes can be found here.
Blocking the producer forever is not a good solution because: Rule #1 of Akka => don't block inside actors. The solution to this problem is provided to us by Akka in the form of a push timeout for an Actor’s mailbox. A push timeout is exactly what it sounds like: when you try to push a message to an actor’s mailbox, if the mailbox is full, the action will timeout and the message will get routed to the DeadLetterActorRef.
Configuring an actor to use a bounded mailbox with a 1000 message capacity and a push timeout of 100ms requires the following addition to the application.conf:
When an actor’s mailbox is full and sent messages start timing out, they get routed to the DeadLetterActorRef via the Event Stream of the actor system. Akka allows actors to subscribe to event streams and listen in on all, or a filtered subset of, the messages flying around in the actor system. Since the dead letters service also utilizes the event stream infrastructure, we can subscribe to all DeadLetter messages being published in the stream and signal the producer to slow down.
The following snipped can be used to get an actor subscribed to all the DeadLetter messages in a system
The FastSender waits for a kickoff message and then sends 15 messages to the SlowReceiver and a PoisonPill to itself. After terminating itself, the actor’s postStop hook schedules a PoisonPill to be sent to the SlowReceiver 3 seconds after the FastSender has been terminated.
The Watcher watches for and prints DeadLetters being sent to the SlowReceiver. It also context.watches the SlowReceiver and terminates the actor system when the SlowReceiver is killed.
12345678910111213141516171819
importakka.actor._classWatcher(target:ActorRef)extendsActorwithActorLogging{privatevaltargetPath=target.pathoverridedefpreStart(){context.watch(target)}defreceive:Actor.Receive={cased:DeadLetter=>if(d.recipient.path.equals(targetPath)){log.info(s"Timed out message: ${d.message.toString}")}caseTerminated(`target`)=>context.system.shutdown()}}
While this example doesn’t actually implement back-pressure, it provides the infrastructure for applying a back-pressure strategy. A possible strategy would be to send FastSender a SlowDown message from within the Watcher for each dead letter received. The SlowDown case class could be defined as
1
caseclassSlowDown(dl:DeadLetter)
When FastSender receives a SlowDown message, it could either throttle itself or tell its upstream systems to slow down. The SlowDown message also encapsulates the relevant DeadLetter object to allow for retry logic.
Rule #1 of Akka: don’t block inside actors. If you do have blocking / high latency calls, wrap them in a future and toss them into a different execution context specifically meant for high latency tasks.
12345678910111213141516171819202122
importjava.util.concurrent.ExecutorsclassClassyActorextendsActor{valnumThreads=10valpool=Executors.newFixedThreadPool(numThreads)valctx=ExecutionContext.fromExecutorService(pool)defreceive:Actor.Receive={// An message that needs some high latency work donecasem:Message=>valfuture=Future{// do something with m: Message}(ctx)future.onComplete({caseSuccess(s)=>// do something when the task successfully completedcaseFailure(f)=>// do something when the task failed})(ctx)}}
Attempting to use the Java driver for Titan-0.3.2 (the current stable Titan release) with Cassandra v1.2.6 in a Scala project throws the following Astyanax error:
123
java.lang.NoSuchMethodError:
org.apache.cassandra.thrift.TBinaryProtocol:
method (Lorg/apache/thrift/transport/TTransport;)V not found
This can be traced back to a bug in Astyanax v1.56.37 which was fixed in v1.56.43.
This can be fixed by ensuring that this dependency is listed above the Titan driver in your pom.xml or sbt.build.
A simple search for “facebook like button tracking you” turns up a bunch of articles and blog posts about how Facebook is creeping on you wherever you go; even if you’re not signed in or actively clicking their ubiquitous like button (example). I’ve come up with a simple solution to the problem.
Caveat: This will prevent you from being able to use Facebook.
Step 1: Add the following entries to your hosts file
When trying to use Joda-Time in a Scala project, I encountered a rather cryptic error:
12
scala: error while loading Instant, class file '/Users/asdf/.m2/repository/joda-time/joda-time/2.3/joda-time-2.3.jar(org/joda/time/Instant.class)' is broken
(class java.lang.RuntimeException/bad constant pool tag 9 at byte 48)
This can be solved by adding the joda-convert dependency into Maven/SBT.