Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

+war profile: clojure.lang.ArityException: Wrong number of args (1) passed to: myapp.handler/app #280

Open
nilskassube opened this issue Mar 15, 2021 · 21 comments

Comments

@nilskassube
Copy link

I created a new app by using

lein new luminus myapp +war

and created a WAR file using

lein uberwar

After deployment in a Tomcat 9.0.43 instance you get a 500 Internal Server Error with this error message:

clojure.lang.ArityException: Wrong number of args (1) passed to: myapp.handler/app
	clojure.lang.AFn.throwArity(AFn.java:429)
	clojure.lang.AFn.invoke(AFn.java:32)
	clojure.lang.Var.invoke(Var.java:384)
	myapp.listener$_contextInitialized$fn__11.invoke(listener.clj:1)
	ring.util.servlet$make_blocking_service_method$fn__98.invoke(servlet.clj:113)
	myapp.servlet$_service.invokeStatic(servlet.clj:1)
	myapp.servlet$_service.invoke(servlet.clj:1)
	myapp.servlet.service(Unknown Source)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
@yogthos
Copy link
Member

yogthos commented Mar 15, 2021

Looks like Tomcat is passing an argument to myapp.handler/app when trying to run the app. Could you try updating the handler to:

(defn app [_]
  (middleware/wrap-base #'app-routes))

and see if it runs?

@nilskassube
Copy link
Author

Adding the _ argument prevents the 500 and the stack strace and yields a 200 with, unfortunately, empty content.

@yogthos
Copy link
Member

yogthos commented Mar 16, 2021

I'm seeing the same behavior locally. Looks like there is a bug here with the setup. The argument to the app handler is the request, and that's getting discarded.

@yogthos
Copy link
Member

yogthos commented Mar 16, 2021

To get the app to display the page you'll need to update the handler namespace as follows:

(mount/defstate app-handler :start (middleware/wrap-base #'app-routes))

(defn app []
  app-handler)

then in project.clj, you'll need to use the app-handler instead of app as the handler for the war:

:uberwar
  {:handler warapp.handler/app-handler
   :init warapp.handler/init
   :destroy warapp.handler/destroy
   :name "warapp.war"}

Finally, another problem is that if you're running with the servlet context then it ends up being injected in the route URI. So, if the app is deployed to tomcat at /myapp context that's part of the URI. That means you have to prefix all the context, .eg:

(defn home-routes []
  ["/myapp"
   {:middleware [middleware/wrap-csrf
                 middleware/wrap-formats]}
   ["/" {:get home-page}]
   ["/about" {:get about-page}]])

@simon-brooke
Copy link

Aye, I was just about to report the same thing. Is this an issue I could submit a pull request on, or is it too dark and dirty for me to mess with? I can tell you that the arg that's passed in is a hashmap, and it's possible that it contains the config information I'm looking for but I haven't yet established that.

@simon-brooke
Copy link

OK, I can see a problem here. I'm frequently deploying the exact same war file on different paths on the same server. If we have to prefix the path-name, I'm going to have either to pass it in as config or else to pull it out of the context somehow. It's quite likely that others will face the same issue.

@simon-brooke
Copy link

...further investigation...

OK, what's passed in appears to be a Clojure map of the (first) request; it contains the key :servlet-context-path which in my case is bound to the token "/wartest", which is precisely the path name we need to prefix into home-routes. The key context i bound to the same token. Other interesting key bindings include:

  • :servlet-context #object[org.apache.catalina.core.ApplicationContextFacade 0x2a66fc6 "org.apache.catalina.core.ApplicationContextFacade@2a66fc6"]
  • :servlet #object[wartest.servlet 0x37f97b5b "wartest.servlet@37f97b5b"]
  • :servlet-request #object[org.apache.catalina.connector.RequestFacade 0x212a87b8 "org.apache.catalina.connector.RequestFacade@212a87b8"]

It looks as if we don't get this until the first request is made to the servlet. The fact that there's also a ServletResponse object bound in the map doesn't bother me since the javax.servlet.Servlet API passes both a request and a response object to the service method (and thus to the doGet, doPost, etc methods of HTTPServlet), so I'm assuming that at this stage the response object is not populated. I'm assuming that the :servlet-context-path in the first request will be identical to that in all subsequent requests, but I might be wrong there.

@bigfoote
Copy link

I made the changes you have suggested here manually, but I can't verify they work because of the other error you linked here that I submitted.

@yogthos
Copy link
Member

yogthos commented Mar 24, 2021

I've got some fixes I was testing that I could push out. I got the app working with Tomcat locally, so I'll try releasing that today. @simon-brooke I'll ping you once I get the changes pushed, and if you have other fixes or ideas PRs are always welcome. :)

@bigfoote
Copy link

I would be happy to try and contribute a PR, it's just that I'm still figuring this stuff out as I'm reading through the beta of the new edition of your book.

@bigfoote
Copy link

first try: copied servlet-api.jar from the Tomcat /lib directory into the app's WEB-INF/lib dir. Doesn't seem to make a difference

@yogthos
Copy link
Member

yogthos commented Mar 24, 2021

Generally, most people tend to run an embedded HTTP server nowadays, so the war options hasn't received much attention for a while.

@yogthos
Copy link
Member

yogthos commented Mar 24, 2021

ok, so I pushed some fixes for the +war profile here, you can try it locally by cloning the repo and running lein install from it to publish the template on your machine and then running lein new myapp +war to generate the project.

I tested it locally with Tomcat 9 and seems to be working here.

@bigfoote
Copy link

These changes do seem to address the arity problem, but they immediately lead me to the next set of issues: the paths to the scripts are absolute. I manually changed those so they load, but they contain links that also don't understand they are not at the root of the server. Right now, that is turning into 404s when asking for base.js, deps.js, and cljs_deps.js. Not sure how many layers of the onion there are to peel back here.

@yogthos
Copy link
Member

yogthos commented Mar 31, 2021

There's no good way to handle servlet context aside from manually specifying it unfortunately. It's a hack that Java app servers use and not part of HTTP spec. Any paths requested from the server by the browser need to contain the context.

@bigfoote
Copy link

bigfoote commented Apr 3, 2021

Fair enough, but how would I find all the places that need to be manually changed?

@yogthos
Copy link
Member

yogthos commented Apr 3, 2021

That would be anything that's defined on the HTML template. However, you shouldn't be seeing requests for base.js, deps.js, and cljs_deps.js if you're compiling ClojureScript with advanced minification. That should produce a single Js artifact that you request. I wouldn't recommend running the app on Tomcat in development mode, and instead just use lein run to start it with its own embedded server.

@bigfoote
Copy link

bigfoote commented Apr 3, 2021

Good point! I set ':optimizations :advanced', but now I'm getting sidetracked by an error that appears to be because I'm on Windows:

java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Users/myuser/src/myproject/target/cljsbuild/public/js/cljs/core.js

Any idea if that slash being put there by code in lein-uberwar or something else?

@yogthos
Copy link
Member

yogthos commented Apr 3, 2021

unfortunately can't help there as I don't have windows here :)

@bigfoote
Copy link

Looks like this works for me: I got the code on to a Linux machine, compiled with advanced optimizations, and it loads in Tomcat now. So thank you very much! I seem to have a new issue where Luminus is using ring-proxy to pass requests back and for between the browser and a server but something very strange is happening where it's a chunked response, but the browser also sees a Content-Length header set. But I haven't been able to track down whether that has anything to do with Luminus or is a Tomcat issue.

@yogthos
Copy link
Member

yogthos commented Apr 16, 2021

Good to hear things are mostly working, and that particular issue sounds like it might be specific to ring-proxy. The headers that get set would be independent of Tomcat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants