2
votes

I am developing an application in clojure using compojure, ring-json, and using lein-ring to test while developing (using the "lein ring server" command) and to package the app into a war file to deploy on Tomcat 7.0.42. I have a page that uses jQuery to do an ajax post request to a url that responds data from an updated database record. While developing, running under the "lein ring server" command, the javascript works perfectly. It collects the inputs, sends the post data, and the server responds with the data I'm looking for.

When I package up the app and deploy it in the "webapps" directory of Tomcat, my app runs correctly, let's me log in, pulls data from the database, and handles form post requests correctly (the login, for example, sends the data via the login form to the server and everything works). But when I navigate to the page with the javascript ajax request, however, I get the following error:

SEVERE: Servlet.service() for servlet [performancecenter.routes/app servlet] in context with path [/performance] threw exception
java.io.IOException: Stream closed
    at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:312)
    at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:200)
    at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
    at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
    at sun.nio.cs.StreamDecoder.read(Unknown Source)
    at java.io.InputStreamReader.read(Unknown Source)
    at java.io.BufferedReader.fill(Unknown Source)
    at java.io.BufferedReader.read(Unknown Source)
    at clojure.core$slurp.doInvoke(core.clj:6279)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at ring.middleware.json$read_json.doInvoke(json.clj:12)
    at clojure.lang.RestFn.invoke(RestFn.java:423)
    at ring.middleware.json$wrap_json_body$fn__3427.invoke(json.clj:19)
    at ring.middleware.json$wrap_json_params$fn__3431.invoke(json.clj:31)
    at ring.middleware.json$wrap_json_response$fn__3438.invoke(json.clj:42)
    at performancecenter.servlet$_service$fn__706.invoke(servlet.clj:1)
    at ring.util.servlet$make_service_method$fn__50.invoke(servlet.clj:126)
    at performancecenter.servlet$_service.invoke(servlet.clj:1)
    at performancecenter.servlet.service(Unknown Source)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

Here is the compojure routes function for the post request url and the app function that wraps the requests with the ring-json functions:

(defroutes page-routes
  (POST "/log/performance/api" {session :session params :params} 
        (enter-production-record (:user session) params)))

(def app
  (-> (routes login-routes main-routes)
      (auth/with-user)
      (handler/site :session)
      (middleware/wrap-json-body)
      (middleware/wrap-json-params)
      (middleware/wrap-json-response)))

And here is the function that returns the json data (and which works in development):

(defn enter-production-record [user params]
  (let [m (data/insert-producer-metric (:id user))
        r (data/get-productivity m)]
    {:body {:productivity (float (:productivity_factor r))}}))

I admittedly know very little about Tomcat, but reading around I found examples of this error and it usually was related to a function in the application closing a stream early or something. I don't understand exactly what's going on here, or how my function would be doing that. Any help would be appreciated, thanks!

2

2 Answers

1
votes

I often encounter such situations, where something works in development and fails in production, when a call to the DB is returning a lazy sequence of records, which is being printed to the repl or logger in development and not being realized on the production system. It could be that something in the call to get-productivity is missing a call to doall around the returned rows.

Another case is where the exception handler is wrapping around a call to with-connection so when the exception happens the db connection is not longer available, though this seems less likely than a missing doall based on my guess of what could be in get-productivity

if you include get-productivity and log an example of it's return value during the failure we will be able to make better guesses.

1
votes

I had a similar problem and it was caused by a request made without passing the header "Content-Type: application/json". If you don't put the correct content type the json wrapper may not work properly and, therefore, they may result in a IO Error (in my case I didn't use a middleware, but I directly slurped {request :body}, which failed giving the IO Error).