5

EDIT: There are some other discussions going on that are related to this question on the Bukkit forums and on Github.

So, I know one or two people have attempted this with no luck.. But I think I'm almost there.

One problem: I don't know Java, so this is a little alien to me. Anyway..

So, I made a simple class in Clojure, as follows:

(ns com.gdude2002.ClojurePlugin.mainclj
  (:gen-class
   :name com.gdude2002.ClojurePlugin.mainclj
   :extends org.bukkit.plugin.java.JavaPlugin)
  (:import org.bukkit.plugin.java.JavaPlugin))

(defn -onEnable [this] (java.util.logging.Logger/getLogger "Loaded clojure plugin!"))
(defn -onDisable [this] (java.util.logging.Logger/getLogger "Unloaded clojure plugin!"))

I use clojure's compile function to compile this to a Java class, as follows..

(set! *compile-path* ".")
(compile 'com.gdude2002.ClojurePlugin.mainclj)

I then put it in the jar manually, under com/gdude2002/ClojurePlugin/mainclj.class (as well as putting the plugin.yml in the root).

So far so good. This method isn't making bukkit bitch about my code (specifically), which I guess is a good thing. Now, on to the problem.

When I try to launch the server with this handmade jar, I get the following output.

21:43:30 [SEVERE] Could not load 'plugins\plugin.jar' in folder 'plugins'
org.bukkit.plugin.InvalidPluginException: java.lang.NoClassDefFoundError: clojure/lang/IFn

"So," I think, "That seems simple enough - it can't find Clojure, right?" So, I put the clojure jar in all sorts of places, with the same error. I also add META-INF/MANIFEST.MF to the jar, containing Class-Path: ../lib. Still nothing.

So, thinking I was being smart, I pulled the clojure folder out of the Clojure jar and shoved it into mine, also putting the folder in ../lib, and other places I thought it might help.

Now, I get this error..

21:51:33 [SEVERE] Could not load 'plugins\plugin.jar' in folder 'plugins'
org.bukkit.plugin.InvalidPluginException: java.lang.ExceptionInInitializerError
...
Caused by: java.io.FileNotFoundException: Could not locate clojure/core__init.class or clojure/core.clj on classpath:
...

The thing is, both of those things exist, as far as I can see..

EDIT: Decided to show the decompiled class code here, in case that helps anyone.

// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available

package com.gdude2002.ClojurePlugin;

public class mainclj extends org.bukkit.plugin.java.JavaPlugin {
    private static final clojure.lang.Var main__var;
    private static final clojure.lang.Var onEnable__var;
    private static final clojure.lang.Var getResource__var;
    private static final clojure.lang.Var onLoad__var;
    private static final clojure.lang.Var getLogger__var;
    private static final clojure.lang.Var saveDefaultConfig__var;
    private static final clojure.lang.Var getDescription__var;
    private static final clojure.lang.Var removeDDL__var;
    private static final clojure.lang.Var onDisable__var;
    private static final clojure.lang.Var isInitialized__var;
    private static final clojure.lang.Var saveResource__var;
    private static final clojure.lang.Var onCommand__var;
    private static final clojure.lang.Var getDefaultWorldGenerator__var;
    private static final clojure.lang.Var toString__var;
    private static final clojure.lang.Var getDataFolder__var;
    private static final clojure.lang.Var installDDL__var;
    private static final clojure.lang.Var getDatabase__var;
    private static final clojure.lang.Var getFile__var;
    private static final clojure.lang.Var getClassLoader__var;
    private static final clojure.lang.Var getCommand__var;
    private static final clojure.lang.Var getDatabaseClasses__var;
    private static final clojure.lang.Var getConfig__var;
    private static final clojure.lang.Var reloadConfig__var;
    private static final clojure.lang.Var clone__var;
    private static final clojure.lang.Var setEnabled__var;
    private static final clojure.lang.Var saveConfig__var;

    public mainclj() { /* compiled code */ }

    public java.io.File getDataFolder() { /* compiled code */ }

    public boolean onCommand(org.bukkit.command.CommandSender p0, org.bukkit.command.Command p1, java.lang.String p2, java.lang.String[] p3) { /* compiled code */ }

    public void reloadConfig() { /* compiled code */ }

    public org.bukkit.configuration.file.FileConfiguration getConfig() { /* compiled code */ }

    public java.io.File getFile() { /* compiled code */ }

    public void saveConfig() { /* compiled code */ }

    public org.bukkit.command.PluginCommand getCommand(java.lang.String p0) { /* compiled code */ }

    public void onEnable() { /* compiled code */ }

    public java.util.logging.Logger getLogger() { /* compiled code */ }

    public void onLoad() { /* compiled code */ }

    public java.lang.ClassLoader getClassLoader() { /* compiled code */ }

    public void saveDefaultConfig() { /* compiled code */ }

    public org.bukkit.plugin.PluginDescriptionFile getDescription() { /* compiled code */ }

    public com.avaje.ebean.EbeanServer getDatabase() { /* compiled code */ }

    public void removeDDL() { /* compiled code */ }

    public void onDisable() { /* compiled code */ }

    public boolean isInitialized() { /* compiled code */ }

    public org.bukkit.generator.ChunkGenerator getDefaultWorldGenerator(java.lang.String p0, java.lang.String p1) { /* compiled code */ }

    public void installDDL() { /* compiled code */ }

    public void saveResource(java.lang.String p0, boolean p1) { /* compiled code */ }

    public java.util.List getDatabaseClasses() { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public java.lang.Object clone() { /* compiled code */ }

    public void setEnabled(boolean p0) { /* compiled code */ }

    public java.io.InputStream getResource(java.lang.String p0) { /* compiled code */ }

    public static void main(java.lang.String[] p0) { /* compiled code */ }
}

EDIT: It was mentioned in the comments that I should post my project.clj file. Thing is, I don't have one! I'm running the compile.clj on mainclj.clj directly, and creating a jarfile by hand.

At this point, I'm entirely stumped. Does anyone have any ideas on this?

spongebob
  • 8,370
  • 15
  • 50
  • 83
gdude2002
  • 197
  • 9
  • This sounds like you might be missing something from your project.clj file? Would you edit your original question, and stick your project.clj file and the head of your core.clj file (or the name of the source file where your Clojure code resides) in the post? What are you using to build? I recommend leiningen https://github.com/technomancy/leiningen/ – octopusgrabbus Jun 30 '12 at 20:59
  • At this point I don't even have a project.clj file. The source file is mainclj.clj (The first code block above), which I compile to a standard Java class using the small two-liner above. I package the jar manually, as if it were a zip file. Note that the packaged jar has no Clojure code in it, just the class that I compiled. – gdude2002 Jul 01 '12 at 00:21
  • Look here, and that might help you. http://stackoverflow.com/questions/9167993/java-interop-with-clojure-getting-exception-java-lang-classformaterror-dupli I tried building a lien project, and got your same error, so the link I've provided might get you further. – octopusgrabbus Jul 01 '12 at 20:43
  • At the person that just removed their comment: Yep, all the sourcecode is there, although I can post a decompilation of the .class file if you like. I'm trying to write a plugin for Bukkit (A Minecraft server that can be extended via Java plugins). – gdude2002 Jul 01 '12 at 20:44
  • @octopusgrabbus Thanks, I'm looking through it now. EDIT: Yeah, I actually had the same issue at one point, I had to remove the :methods from the gen-class function to fix it – gdude2002 Jul 01 '12 at 20:44
  • I removed my earlier comment, and added another one with a link. You've got a very interesting question, because whether you're building it your way, a way I haven't seen yet, or through lien it's the same error. – octopusgrabbus Jul 01 '12 at 20:45
  • @octopusgrabbus What also might be interesting is that I've been talking to the person who created that question (on Github), and they haven't gotten much further with it: https://github.com/jonnay/Watership-Down/issues/1 – gdude2002 Jul 01 '12 at 20:47
  • Since clojure was written in java and uses java internally I strongly recommend you to learn just the basics of Java. Not too much time if you are a programmer but it'll help you in your future endeavours with clojure. – Adam Arold Jul 02 '12 at 21:40
  • I'm thinking of moving right to Java if I can't get this to work anyway. Also, I think we're doing Java next year at University, but that's besides the point. I see your point, and you may be right.. Might look into it this weekend. – gdude2002 Jul 03 '12 at 11:00
  • I'm trying to do the same and seems that Bukkit/Spigot plugin class loader (``org.bukkit.plugin.java.PluginClassLoader``) makes Clojure runtime unable to load. I can instantiate my AOT classes from Groovy shell but not from Spigot plugin loader. I am using ``lein uberjar``, I think it generates jars correctly. – kolen Sep 15 '15 at 21:15

1 Answers1

1

I solved this problem by configuring the class loader of the calling code as described in this post.

My situation might be a bit different as I did that from the calling Java code. In other words, I invoked that code in my CommandExecutor (Java) immediately before invoking a method from a class created using gen-class (Clojure).

spongebob
  • 8,370
  • 15
  • 50
  • 83
Jeb
  • 15,939
  • 6
  • 34
  • 37