August 09, 2013

Following the error: lein-cljsbuild and :foreign-libs

Everyday we stumble across cryptical error messages and complicated stack traces. Some of them requires you to go to different websites and documentation, and if it keeps getting in the way, check the source code to finally understand the Why.

I want to document my line of thoughts when I come across those little problems that really make you sad.

Todays problem was when I was trying out Clojurescript. I started out with a simple `lein new project` and added the cljsbuild < https://github.com/emezeske/lein-cljsbuild > plugin to start the fun.

Some context before the error happens

I wanted to play around with some graphics and I just start drawing squares on a <canvas> tag. No libraries required so far, just some old js functions to grab elements and draw on the canvas.

I realised that I would need another way to draw on the browser. The canvas won’t get mouse events unless I keep track of it and that was too cumbersome for me.

SVG sounded better, but the api to work with it in the browser is not so fun. A quick google search and raphaeljs.com is on the way.

Investigating the Clojurescript integration with different libraries

"Raphael.js is an awesome graphical library for JavaScript. Let’s see how it integrates with clojurescript".

The first search for clojurescript and raphael takes me to the blogpost < http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html >

It is a very complete blog post that gives you different examples of how to integrate cljs with other js libs.

"Wow. it is just one line, let me add this :foreign-libs"

Let the error hunt begin

"But I followed all the instructions" said the baffled developer.

Going to raphaeljs website there is a big button to download the code. Right-click and copy url. Paste on the proper space on the project.clj and we should be good. Right?!

After that, no code was downloaded. “Well, I forgot the require”. Ok, added on the file and we have an error on the webpage.

"goog.require raphael.core is not found"

That lead me to understand better the integration of Clojurecsript and the Google Closure framework.

On the blog post of Luke VanderHart, there is a comment about the version where the :foreign-libs was introduced. The code is not so complicated and reading the changeset gives us a nice insight < https://github.com/clojure/clojurescript/commit/96b38e2c951ef07b397e9d >

"Well, the code should inject that remote file when compiling everything together".  `ack raphael` and no files are reported on the directory, but the project.clj and the require’s statements.

Approaching the light

'That is super weird. I need more help.'

I thought something was configured wrong. Reading the sources, documentations and everything seems to be on the right place. But what was wrong? At this point, asking other people is the best way to get better ideas.

IRC app. /j #clojure. HALP!

dnolen suggested to try using a local file. ‘IT WORKS!’. But it works is not enough. Why it didn’t work before?

First step: Is the error related to using a remote file?

`python -m SimpleHTTPServer` and changing the project.clj to point to http://localhost:8000/raphael-min.js

Works. Yay! But why the other one does not work?

 

Second step: What is the status of the file I am pointing to?

`wget <raphael-url>` gave me some redirects. This might be related. First I get a 301. Moved permanently. To the HTTPS version.

Interesting… redirects…

Third step: Why a redirect does not work?

Reading the source, cljsbuild generate a InputStream for that content. InputStream is part of java. This is not in the plugin anymore.

With some search, we end up on this bug report http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571

Quoting the reply of java’s engineers:

After discussion among Java Networking engineers, it is felt that we shouldn’t automatically follow redirect from one protocol to another, for instance, from http to https and vise versa, doing so may have serious security consequences. Thus the fix is to return the server responses for redirect. Check response code and Location header field value for redirect information. It’s the application’s responsibility to follow the redirect

 

The error happened because of the protocol change not being followed, as we are used to have in the browser.

Ah, now I can sleep.