RMI - przekazywanie parametrów w oparciu o interfejs

0

Witam, piszę program oparty o RMI. Mam aplikację klienta, servera i projekt szarowany między nimi(przetrzymuje tam interfejsy, np interfejs Action). Interfejsy z szarowanego projektu są implementowane w projekcie serwera. Tu pojawia się problem: Klient ma metodę doOperation(Action action); w serwerze tworze obiekt klasy ActionXXX implementujacej interfejs Action i podaje do metody klienta doOperation. Klient nie ma pojęcia o implementacji, zna tylko interfejs Action. Czy to jest w ogóle możliwe? Dostaje taki stacktrace:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: action.ActionSimpleCMD
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:354)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$256(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:276)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:253)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:162)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:217)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:171)
at com.sun.proxy.$Proxy1.handleAction(Unknown Source)
at server.impl.ServerEngine.acceptClient(ServerEngine.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:323)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$256(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: action.ActionSimpleCMD
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:314)
... 12 more
Caused by: java.lang.ClassNotFoundException: action.ActionSimpleCMD
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.rmi.server.LoaderHandler$Loader.loadClass(LoaderHandler.java:1207)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at sun.rmi.server.LoaderHandler.loadClassForName(LoaderHandler.java:1221)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:453)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:186)
at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:637)
at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:264)
at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:214)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1613)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:326)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:308)
... 12 more

Z góry dzięki za odpowiedź, pozdro :)

0

Ok, teraz działa.

System.setProperty("java.rmi.server.codebase", "file:/C:/Users/xxxx/workspace/KCK/KCKServer/bin/");

Because the remote object's codebase can refer to any URL, not just one that is relative to a known URL, the value of the Java RMI codebase must be an absolute URL to the location of the stub class and any other classes needed by the stub class

Tylko teraz nie wiem gdzie jest sens całego RMI? Program odpalony na innej maszynie już nie zadziała, bo nie odnajdzie tej ścieżki. To ma tak wyglądać czy ja coś źle zrozumiałem?

Edit
dodam kod, bo czuję że czegoś nie rozumiem, klient:

public interface Client extends Remote{
	ActionResult handleAction(Action action) throws RemoteException;
}

public class ClientEngine extends UnicastRemoteObject implements Client {
...
}

public class MainClass{
	
	public static void main(String args[]) {
		System.setProperty("java.security.policy", "clients.policy");
		System.setProperty("java.rmi.server.codebase", "file:/C:/Users/xxxxx/workspace/KCK/KCKServer/bin/");
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new SecurityManager());
		}
		try {
			Registry registry = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
			Server server = (Server) registry.lookup(Server.class.getName());
			Client client = new ClientEngine(server);
		} catch (Exception e) {
			System.err.println("exception:");
			e.printStackTrace();
		}

	}
}

Server:

public interface Server extends Remote{
	public void acceptClient(String clientName, Client client) throws RemoteException;
	public String sayHi() throws RemoteException;
}

public class ServerEngine extends UnicastRemoteObject implements Server {
...
}

public class MainClass {
	
	public static void main(String[] args) {
		System.setProperty("java.security.policy", "clients.policy");
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new SecurityManager());
		}
		try {
			Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
			Server server = new ServerEngine();
			registry.rebind(Server.class.getName(), server);
			System.out.println("server bound");
		} catch (Exception e) {
			System.err.println("server bound exception");
			e.printStackTrace();
		}
	}

}
0

Bardzo dawno już tego nie robiłem, ale wydaje mi się że to może być widoczne "w internecie" za pomocą URLa i wtedy wcale nie potrzebujesz żeby wszystko było na jednej maszynie.

0

Tak, ale będzie problem bo po każdej nowej wersji będzie trzeba na serwer wrzucać skompilowane klasy :/. W moim przypadku i tak mam jeden projekt "szarowany" trzymający interfejsy, chyba dorzucę tam resztę kodu który ma być znany po obu stronach i tyle. Dzięki @Shalom za pomoc. Jeżeli ktoś będzie miał jeszcze jakiś pomysł to piszcie, temat jeszcze otwarty :). Pozdro

1 użytkowników online, w tym zalogowanych: 0, gości: 1