0
votes

I want to write a clojure lib and exposes generated classes, so that other java projects could use it. I read and followed the gen-class doc, and everything works as my expect, except class annotation with enum parameters.

(ns common.exception.unauthorized
  (:gen-class :name
    ^{org.springframework.web.bind.annotation.ResponseStatus
      org.springframework.http.HttpStatus/UNAUTHORIZED}  ; <- here
              com.lalala.common.exception.Unauthorized
              :extends java.lang.RuntimeException
              :init init
              :constructors {[String] [String]}
              :main false
              :prefix "unauthorized-"))

(defn unauthorized-init [^String message]
  [[message] nil])

This exception class is generated without any error, and it also able be used as an Exception. However, this exception is intended to be used as a http response along with spring web. The spring framework read the annotation, and find that it is HttpStatus/UNAUTHORIZED then response 401. But the spring framework throws exception complaining that java.lang.EnumConstantNotPresentException: org.springframework.http.HttpStatus.

And I had a look at the generated class, it's something like this:

@ResponseStatus(HttpStatus.401)
public class Unauthorized extends RuntimeException {
    private static final Var init__var = Var.internPrivate("common.exception.unauthorized", "unauthorized-init");
    private static final Var getStackTrace__var = Var.internPrivate("common.exception.unauthorized", "unauthorized-getStackTrace");
    // ...... ellipsis
}

As it shown, the HttpStatus/UNAUTHORIZED is compiled into HttpStatus.401 which is invalid.

I also tried with {:code org.springframework.http.HttpStatus/UNAUTHORIZED}, {:value org.springframework.http.HttpStatus/UNAUTHORIZED}, it can be compiled into @ResponseStatus(code/value = HttpStatus.401), but the enum value itself still in invalid form HttpStatus.401.

Am I use class annotation for gen-class in a wrong way? or just Clojure compiler just have this bug?

P.S. tried with Clojure 1.9, 1.10, 1.10.1

1
IMO gen-class is in general a mug's game. If you need to produce Java classes for Java interop, javac is really good at making them, so I write a small Java wrapper for the logic that I implement in Clojure. That Java wrapper can have whatever annotations and other weird stuff Java libraries expect, but it communicates with my Clojure code in a straightforward, data-driven way.amalloy

1 Answers

0
votes

Finally, I use native Java code instead. I realized that writing classes in clojure with only ctor forwarded to super class is making trouble for myself. I embedded java code in the project along with :java-source-paths configured in defproject, got my problem solved.