简体   繁体   中英

Issues using a Java interface in Clojure

I've been following this tutorial on distributed RMI using clojure, but it seems to be outdated and I can't get it to work:

http://nakkaya.com/2009/12/05/distributed-clojure-using-rmi/

I was getting a java.lang.ClassNotFoundException: stub.sayName when I followed the tutorial precisely, so I tried using reify instead of proxy, but the error is still there.

As of now my code is as follows:

for the interface: package stub;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface sayName extends Remote {
      String Name() throws RemoteException;
}

For my main clojure class:

(ns immutability.core
  (:gen-class))

(defn -main
  [& args]
  (println "Hello, World!"))
(def rmi-registry
  (java.rmi.registry.LocateRegistry/createRegistry 1099))

(defn name-server []
  (reify stub.sayName Name
    (Name [personname] "Hello, " + personname)))

(defn register-server []
  (.bind
   (java.rmi.registry.LocateRegistry/getRegistry)
   "Hello"
   (java.rmi.server.UnicastRemote/exportObject
    (name-server) 0)))
(register-server)

I'm sure it's something silly and small, but I just can figure it out

Okay, these are your issues:

  1. You need to add a package name to the java interface for it to work properly . On reading your post again, I can see the package name, it didn't get added to the code block... see below for where to put the SayName.class file.
  2. You need to change the java signature to take a name, in accordance with your code
  3. You need to change the reify signature, the one that you had previously just won't work. First the structure is wrong, and second, the method constructions are different in reify than proxy. With the reify forms, the arguments all need a reference to an anaphoric 'this', which I have replaced with _, since it is not in use in your code. The input argument is second in the form, and that is what you can use to set the name in "Hallo, Welt".
  4. The class is java.rmi.server.UnicastRemoteObject, not java.rmi.server.UnicastRemote, as you have it in your original code.
  5. Not really an error, but good to have, is to define the server symbol in clojure as defonce, so you don't end up with port conflicts when you try to re-evaluate the file, since the server is already defined.

Post these changes, the code compiles, and appears to be doing what it should. I stopped at invoking the RPI call.

interface code:

package stub;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SayName extends Remote {
      String name(String n) throws RemoteException;
}

I compiled and stored the stub.SayName interface in target/classes/stub/SayName.class. This is on the classpath and was found on the repl startup (relative to your project.clj file).

clojure code:

(ns immutability.core
  (:gen-class))

(defn -main
  [& args]
  (println "Hello, World!"))

(defonce rmi-registry (java.rmi.registry.LocateRegistry/createRegistry 1099))

(defn name-server []
  (reify stub.SayName
    (name [_ n] (str "Hello " n))))

(defn register-server []
  (.bind
   (java.rmi.registry.LocateRegistry/getRegistry)
   "Hello"
   (java.rmi.server.UnicastRemoteObject/exportObject
    (name-server) 0)))

(register-server)

One last thing, as a note, you don't want to leave a call like (register-server) directly in your code, since that will get called on compilation. commenting that out, or reserving it for the REPL is a better approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM