5

We have 2 apps sitting on our server. One is a Java app that pulls out important blocks of information from sentences written in normal English. It should only talk to other apps on our server (though we have not yet put up a firewall to protect it). It should not talk to the public (though at the moment that is possible). It has some very simple Java code that opens up a Socket on port 8080 and then listens. It expects to receive a string that it can convert to JSON. It sends back a string that can be converted to JSON. It does not send back any HTTP headers, it is just the simplest Socket code you can imagine, not even 30 full lines of code.

We have another app, written in Clojure, that speaks to the wide world, via a RESTful API.

The basic idea is that requests come to our server from the outside world, and interact with the Clojure app. The Clojure app is suppose to interact with the Java app, and then the Clojure app sends back a response to whoever queried it.

I've had trouble figuring out how to call the Java app from the Clojure app. I tried to use a library such as clj-http, but it relies on the Apache HttpClient, which makes the assumption that the response will contain correct HTTP headers. Apache HttpClient threw exceptions when interacting with the Java app, since the Java app only sends back a string (no HTTP headers). I don't want to write full HTTP code in the Java app, as that gets redundant with the work we did in the Clojure app.

So, I switched tactics, and wrote some raw Socket code from the Clojure side. That code looks like:

(def conn (ref {:in nil :out nil}))

(defn connect []
  (let [socket (Socket. "localhost" 8080)
    in (BufferedReader. (InputStreamReader. (.getInputStream socket)))
    out (PrintWriter. (.getOutputStream socket))]
  (dosync
   (alter conn
        (fn [old-value] (assoc old-value :in in)))
   (alter conn
        (fn [old-value] (assoc old-value :out out))))))

The write function was basic:

(defn write-message [msg]
  (doto (:out @conn)
    (.println (str msg "\r"))
    (.flush)))

This worked once, but most of the time when I try it (from the browser, which uses Javascript to ping the API, which then calls the Java app) I get:

Connection reset stack trace: 
    {:class java.net.SocketException, 
     :message "Connection reset", 
     :trace-elems ({:method "read",          
     :class "java.net.SocketInputStream", 

As I do this, I have several terminal windows open. The terminal for the Java app shows no activity at all, whereas the Clojure app shows the above error.

If I use this curl (which hits the Java app directly), then everything works:

curl --verbose "http://localhost:8080" -d '  { "transaction-id" : "99393920", "report" : "Allie Hanther of IBM responded to our proposal and said her company is ready to move forward. The deal is worth $400,000 annually. I will meet with her tomorrow and we will sign the contract." } '   -X POST   --header "Content-Type:application/json"

I realize that stuff like "POST" and "content type" are meaningless in this context, since we are not talking to an HTTP server. Still, they don't seem to do any harm. This curl request gets back the correct response.

But the Clojure app only gets an error.

I could believe that the 2 apps are having a conflict over port 8080, but I don't understand the nature of that conflict.

Any suggestions?

In the future, we are thinking we will give up on the Socket and instead use either Redis or a jobs queue for communication, but for now we are using the Socket, and I'd like to get it working.

For now I only need the Clojure app to work like curl.

charlottesville
  • 249
  • 3
  • 15
  • There are some ideas here why this exception might be thrown - http://stackoverflow.com/questions/62929/java-net-socketexception-connection-reset ... curl is probably more resilient. My guess is that the browser requires valid http message and therefore it fails. – Viktor K. Jul 06 '15 at 17:47
  • Viktor K, I am sorry if what I wrote is confusing, but, as I wrote above, I gave up on the clj-http and switched to a raw socket. The problem we now face is with the raw socket. There is no HTTP involved in the communication between the Clojure app and the Java app. The web browser (Chrome, FireFox, etc) does get all the normal HTTP headers, because both Apache and Jetty send the correct headers to the outside world. – charlottesville Jul 06 '15 at 19:36
  • The clj-rethinkdb speaks raw JSON over Sockets, it should give some inspiration. – Daniel Compton Jul 06 '15 at 20:34
  • 1
    @charlottesville Could you provide a minimal example that includes the Java and Clojure code that would allow other to replicate that behaviour? In the scenario you are describing it is unclear where the problem is coming from. – juan.facorro Jul 07 '15 at 03:47
  • 1
    Why go to the trouble of running the Java code as a service? Why not just call it as a library from your Clojure service? – dskrvk Jul 08 '15 at 04:13

0 Answers0