开发者

How do I access JAR classes from within my Clojure program?

I've been stuck for several days on something that I know I've gotten to work once before. I'm probably missing something obvious. Any help would be appreciated.

In my Clojure program, I want to access methods from classes originally written in Java. Let's use a specific example: org.infoml.jaxb.ObjectFactory. These classes are in a .jar file (infoml-classes-1.0.jar) that is a double-clickable Java application. If you open up the jar file, you see the folder for the top-level segment of the Java classes' package:

macscooter:infoml-classes-1.0 folder gw$ find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
.
|____META-INF
| |____MANIFEST.MF
|____org
| |____infoml
| | |____infocardOrganizer  <<<<<< Java application's classes
| | | |____AFileFilter.class

      |  ... many classes omitted here

| | | |____UniqueContentListener.class
| | | |____UniqueContentModel.class
| | |____jaxb
| | | |____AgentContainerLocationType.class
| | | |____AgentType.class

      |  ... many classes omitted here

| | | |____ObjectFactory.class   <<<<<< HERE IT IS

      |  ... many classes omitted here

| | | |____TableRowType.class
| | | |____TableType.class
macscooter:infoml-classes-1.0 folder gw$

In my Clojure program, I import it (from file cardmaker.clj):

(ns infwb.cardmaker
  (:gen-class)
  (:import
   (javax.xml.bind  JAXBContext  JAXBException  Marshaller
            Unmarshaller)
    (org.infoml.jaxb        ContentAgentContainerLocationType
            InfomlFile  InfomlType  ObjectFactory  PType  <<<<<< HERE IT IS
            RichTextWithExactType
            SelectorsType
            SimpleRichTextType)
   (java.io  ByteArrayOutputStream IOException)))

When I print out the classpath that the REPL sees, it's there (as infoml-classes-1.0.jar):

infwb.cardmaker> (doseq [p (.getURLs (java.lang.ClassLoader/getSystemClassLoader))] (println (.getPath p)))
/Users/gw/tech/clojurestuff/cljprojects/infwb/src/
/Users/gw/tech/clojurestuff/cljprojects/infwb/test/
/Users/gw/tech/clojurestuff/cljprojects/infwb/classes/
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/clojure-1.3.0-SNAPSHOT.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/clojure-contrib-1.2.0.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/infoml-classes-1.0.jar  <<<<<< HERE IT IS
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/piccolo2dcore-1.3.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/piccolo2dextras-1.3.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/sxqj-beta2.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/dev/clojure-1.2.0.jar
/Users/gw/tech/clojurestuff/cljprojects/infwb/lib/dev/swank-clojure-1.3.0-20110104.084027-21.jar
nil
infwb.cardmaker>

However, when I try to compile the file cardmaker.clj (by either C-c C-k or (load-file "src/infwb/cardmaker.clj"), I get the following exception:

Could not initialize class org.infoml.jaxb.ObjectFactory
  [Thrown class java.lang.NoClassDefFoundError]

I've gone as far as rebooting my machine to try to eliminate inadverte开发者_JAVA技巧nt crud as a source of the problem. I've researched several things on the Internet--no luck. I've looked at the MANIFEST.MF file for clues, and stripped the jar file down to just the classes I need. I've slept on it overnight twice now--still no solution.

Using existing Java classes from within Clojure is a useful thing to do. I'd appreciate the help that anyone might be able to give me. Thanks.


ADDENDUM: My problem was caused by a number of factors, including a missing jar file. @kotarak's answer was spot on. Thanks.


I am not 100% sure I fully get your question, but if you are simply trying to access your Java classes from a Clojure program, I would use leiningen. Here is roughly what you do once you have installed leiningen:

lein new cardmaker
cd cardmaker
lein deps

Put your jar file in the lib directory.

After this you will get a directory structure that looks like this:

├── README
├── classes
├── lib
│   ├── clojure-1.2.1.jar
│   └── infoml-classes-1.0.jar
├── project.clj
├── src
│   └── cardmaker
│       └── core.clj
└── test
    └── cardmaker
        └── test
            └── core.clj

Now you can import your Java classes in your Clojure program and have access to them via Clojure Java interop. Here is an example

(ns cardmaker.core
  (:import [java.util Blah Blah]))

To run this program, you have a bunch of different options. See the leiningen docs. It looks like you are using emacs so see swank clojure. Using leiningen in this way should resolve your classpath issues.


The way you import the classes is fine. Obviously the ObjectFactory class cannot be initialised because some other class it needs is not on the classpath. Lookup at the full stacktrace to find out which class is missing for ObjectFactory to work. As @mikera suggested, you can use (import 'org.infoml.jaxb.ObjectFactory) in the Repl to do that. (Unless emacs eats your stacktrace...)


I use the following code to import Java classes:

  (ns my.namespace
    (:import [java.io DataInputStream File FileInputStream BufferedInputStream])
    (:import [my.package MyClassOne MyClassTwo])
    ....

This should work providing the relevant Java classes are on the classpath (it's fine if they are in a .jar).

To test whether these are on the classpath you can also do the following at the REPL:

  (import 'my.package.MyClassOne)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜