special things to do when using rmi + plugins - java

I have an application FooApplication (code in foo.jar) with a plugin BarPlugin (code in foo-bar-plugin.jar). The application instantiates an instance of the plugin dynamically. It works great.
FooApplication has some functionality accessible via an RMI interface FooRemote. That also works great, except for one thing. FooRemote has a method to access Remote objects exported by plugins, and I get an java.rmi.UnmarshalException when I try to hand out one of those plugins to an RMI client.
public interface FooRemote extends Remote
{
/* other methods */
public RemoteControl getPluginRemoteControl(int i) throws RemoteException;
}
/** just a named Remote object for debugging purposes */
public interface RemoteControl extends Remote
{
public String getName() throws RemoteException;
}
What I am doing in my FooRemoteImpl is the following:
/* just a test object */
private static class RC0 extends UnicastRemoteObject implements RemoteControl
{
public RC0() throws RemoteException { super(); }
#Override public String getName() throws RemoteException { return "RC0"; }
}
#Override public RemoteControl getPluginRemoteControl(int i)
throws RemoteException
{
int j = i;
if (j <= 0)
return new RC0();
Collection<RemoteControl> rclist = this.model.getApplicationPluginRemotes();
for (RemoteControl rc : rclist)
{
if (--j == 0)
return rc;
}
return null;
}
When I call FooRemote.getPluginRemoteControl(0), it hands out an instance of my dummy class RC0, and works fine from a client. When I call FooRemote.getPluginRemoteControl(1), it tries to hand out one of the real plugin remotes, and it fails:
??? Java exception occurred:
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: com.example.plugin.BarPluginRemoteControl (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy5.getPluginRemoteControl(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.example.plugin.BarPluginRemoteControl (no security manager: RMI class loader disabled)
[ more stuff deleted ]
What gives?

You probably need to set up a SecurityManager. Your client cannot find the class com.example.plugin.BarPluginRemoteControl because there is no security manager in place to give it access.
Try the following:
To the client code add:
System.setSecurityManager(new java.rmi.RMISecurityManager());
Create a policy file called client.policy containing:
grant{
permission java.security.AllPermission;
};
Pass the policy file into the client's startup command:
java -Djava.security.policy=client.policy ... <main-class>...

Please, please, don't put the classes on the RMI Registry classpath. That works around the problem, but it is not the correct solution. Doing so means that each time you update your server code, you'll need to synch the classes with your RMI Registry and all your clients. The proper solution is to provide the needed classes on the server's codebase using an http or ftp URI, (not a file URI, please!). Then, the RMI Registry and your clients will be able to access the needed classes dynamically via http or ftp.
The only classes you should include on the codebase would be the Remote interfaces to your server, and any classes that appear as parameters to or return values from methods in those interfaces, as well as any exception classes thrown by those interfaces. I believe (but I'm not sure) that best-practices for this is to create a separate jar with a '-dl' suffix which includes only these class files.

The RMI Registry also needs the classes on it's own classpath, in case you are running it in a separate VM. I remember wasting too much time learning this the hard way.
Glad I could help!
edit: apparently this is not the correct approach, see answer by the commenter.

Related

When creating an RMI application on two different machines Client and Server Where should we Define our Interface Client side or Server side?

I want to write an RMI application using two laptops which adds two numbers? i have made one laptop as my server and another laptop as my client. when we want to define the interface which is extending from Remote interface on which machine should i define this interface the client side or the server side? please help.
i Have made an RMI application using one machine it works fine I have defined the Interface in the same package but when i work on different machines it does not work.
public interface AdditionI extends Remote {
public int add(int x ,int y) throws RemoteException;
}
public class Server extends UnicastRemoteObject implements AdditionI {
public Server() throws RemoteException {}
#Override
public int add(int x, int y) throws RemoteException {
return x+y;
}
public static void main(String ar [])throws RemoteException {
try
{
Registry reg = LocateRegistry.createRegistry(2177);
reg.rebind("Add", new Server());
System.out.println("Server is ready");
}
catch(Exception e)
{
System.out.println("Error "+ e);
}
}
}
public class Client {
public static void main(String ar[])throws RemoteException {
try {
Registry reg = LocateRegistry.getRegistry("localhost",2177);
AdditionI ad = (AdditionI)reg.lookup("Add");
System.out.println("REsult:"+(ad.add(10, 5)));
} catch (Exception e) {
System.out.println("Error"+e);
}
}
}
when i run this code on the same machine it works fine the result of the method add is displayed, but on different machine it displays the following message.
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException:
Where should we Define our Interface Client side or Server side?
Simple: you need the interface on both sides.
The Client knows that interface, and basically it is the "only thing" it knows: there is some interface that defines the behavior (the methods) that the client code can use.
The Server knows that interface and implements it.
That interface is the basic thing that "links" client and server (conceptually). They both know that there is some interface AdditionI. The client will need it so it
first identify a service that supports that interface
when such an service is found, the client knows how to invoke the corresponding add method
The server on the other hand uses the interface to register its implementation as a service ... that clients then can call.
Therefore, you basically have three different parts in your source code:
common: that contains that AdditionI interface
client: the additional code required to identify and later use that addition service
server: the additional code to implement and register the service
And note: that exception java.lang.ClassNotFoundException is really basic. It tells you that the JVM running some code did not find some class.
In other words: your classpath setup is somehow broken. Simply research that exception (you can find endless endless documentation about such basic things, see here for example). Most likely, it boils down to: making sure that some .class files are in the classpath ... where you need them. And the first part already tells you where which classes need to go to!

RMI server interface (dispatcher) methods throwing java.lang.NoClassDefFoundError

I'm trying to implement a 2-player network quiz game using RMI. I have a DispatcherInterface (interface) and Dispatcher (implementing the for the former) respecting RMI architecture.
The server model has User and Theme where a Quiz would be in a specific theme, my idea was that when a User connects into a Theme they'd be added into a Map<User,Theme> waitingList until another User comes along to play against them.
My problem lies in the implementation, while trying to implement a simple setter method for adding users to the waiting list I get the following error:
Error occurred in server thread; nested exception is:
java.lang.NoClassDefFoundError: java/sql/SQLException
Which did not make sense to me since my class path is well defined for the RMI registry and the compile/run commands.
public interface DispatcherInterface extends Remote {
public User login(ReceiverInterface client, String email, String password) throws RemoteException;
public void addToWaitingList(int userID, Theme theme) throws RemoteException;
public void addToWaitingList(User user, Theme theme) throws RemoteException;
}
The first method works perfectly with no problems while the other one doesn't regardless of its implementation (tried a simple System.out.print("test")), however I find it inconvenient as I have to loop over the list of connected users in order to get the User instance I need to add to my waitingList. So idealy I am hoping to be able to pass an instance of User to the method.
public class User implements Serializable {
public ReceiverInterface client;
public int id;
public String pseudo;
...
}
I do not understand why I can't pass the a User object to the method, especially since it's serializable.
For completeness sake, for the client part, I have implemented similarly a ReceiverInterface and Receiver that communicate with the server through a proxy (DispatcherInterface)
public class Receiver implements ReceiverInterface {
private DispatcherInterface = proxy;
private User user;
private String serverIP;
public Receiver(String serverIP) throws RemoteException, NotBoundException {
this.serverIP = serverIP;
Registry registry = LocateRegistry.getRegistry(serverIP);
this.proxy = (DispatcherInterface) registry.lookup("QuizApp");
}
...
}
Any help is appreciated.
EDIT:
I use an IDE (IntelliJ) to run my code, it uses the following command:
/usr/lib/jvm/java-10-openjdk/bin/java -javaagent:/opt/intellij-idea-ultimate-edition/lib/idea_rt.jar=41683:/opt/intellij-idea-ultimate-edition/bin -Dfile.encoding=UTF-8 -classpath /home/rand/gm4/JAVA/QuizApp/out/production/QuizApp:/home/rand/gm4/JAVA/QuizApp/lib/miglayout-swing-5.2.jar:/home/rand/gm4/JAVA/QuizApp/lib/miglayout-core-5.2.jar:/home/rand/gm4/JAVA/QuizApp/lib/sqlite-jdbc-3.23.1.jar server.Run
Which is in better terms (without the .jar dependencies), run from the project path is:
javac -d out/production/[project_name] src/server/Run.java
javac -d out/production/[project_name] src/client/Run.java
rmiregistry -J-Djava.class.path=out/production/[project_name]/
java -cp out/production/[project_name] server.Run
java -cp out/production/[project_name] client.Run
You are using jdk 10 to execute your code. In jdk 9 some of the code base was moved into modules and no longer avaliable by default, java.sql is in one of these non root module. You need to use "--add-modules java.sql" with the java command to make the java.sql package avaliable at runtime.
Since you are using intellij to run the code the following doc might help explain how to setup intellij to include this module when run from the ide. https://www.jetbrains.com/help/idea/getting-started-with-java-9-module-system.html

java RMI is giving java.lang.IllegalArgumentException: argument type mismatch

I am trying to create an simple rmi chat but I coldnt get through this exception.i think I have done it right but I must be missing something
this is where the exception occurs
HelloClient(String name,HelloInterface hello) throws RemoteException{
this.name=name;
this.hello=hello;
try{hello.register(this);}///this is the line generating my exception
catch(Exception es){System.out.println(es);}}
I have the register() method in my server interface and I have also implemented it
public synchronized void register(HelloClient h) throws RemoteException {
//code of my implementation which is not neccerary
}//on my server side passing it the client object
and this is the exception I am getting
java.lang.IllegalArgumentException: argument type mismatch
I have tried to make myclient class implement serizable but it didn't work.
so ca anyone please give me a hand
You've changed your remote interface after deployment. Clean, recompile, and redeploy.

org.apache.felix.ipojo.ComponentFactory cannot be cast to org.apache.felix.ipojo.Factory

I have a bundle component,
package ipojo;
import ipojo.service.Hello;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Validate;
#Component(name="hello-factory")
#Provides
public class HelloImpl implements Hello{
#Override
public void shoutHello() {
System.out.println("HellooOOOOoooOooo!");
}
#Validate
public void start() throws Exception {
System.out.println("Hello started :)");
}
#Invalidate
public void stop() throws Exception {
System.out.println("Hello Stopped :(");
}
}
In my java application, I embedded Apache Felix, and deployed iPOJO APIs. Then, I tried to create an instance of my above component using Factory Service, as the following:
myBundle= context.installBundle("myBundlePath");
myBundle.start();
ServiceReference[] references = myBundle.getBundleContext().getServiceReferences(Factory.class.getName(), "(factory.name=hello-factory)");
if (references == null) {
System.out.println("No references!");
}
else {
System.out.println(references[0].toString());
Factory factory = myBundle.getBundleContext().getService(references[0]);
ComponentInstance instance= factory.createComponentInstance(null);
instance.start();
}
I successfully got the reference to the factory service, but at the following line:
Factory factory = myBundle.getBundleContext().getService(references[0]);
I get the following ClassCastException:
java.lang.ClassCastException: org.apache.felix.ipojo.ComponentFactory cannot be cast to org.apache.felix.ipojo.Factory`
I changed this line to:
Factory factory = (ComponentFactory) myBundle.getBundleContext().getService(references[0]);
then I got:
java.lang.ClassCastException: org.apache.felix.ipojo.ComponentFactory cannot be cast to org.apache.felix.ipojo.ComponentFactory
How can I solve my problem? Thank you.
When embedding Felix (or any other OSGi framework) you create a boundary between the classloaders. The host and the bundles are not using the same classloaders meaning that classes from inside and outside are not compatible. In other words, accessing OSGi services from the host is particularly complex and require using reflection.
For simplicity reasons, you should use the Factory service (and any other services) from a bundle and not from the host.
If you really need to use them from the host, you have to configure the OSGi framework to export all the required packages from bundle 0.
This exception means that there are classpath problems, as there are multiple versions of libraries in the classpath.
A ClassCastException when a class cannot be cast to a class of the same name is caused by an attempt to cast classes accross classloaders: it's not possible to do so, see here.
The classloader which loaded the class makes part of the class unique identifier.
So two classes with the exact same name org.apache.felix.ipojo.ComponentFactory will not be the same if they where loaded in different classloaders.
You need to debug your classpath, find the unwanted version of the library containing that class and remove it.

Throwing exception to client in GWT (Google Web Toolkit)

I'm using GWT (currenly working with google's eclipse plugin), and I'm trying to throw an exception from the server to the client.
My exception is something like
class LoginException extends Exception implements IsSerializable
But I get (upon loading in hosted mode):
[ERROR] Errors in '[...]/src/myPackage/client/services/Session.java'
[ERROR] Line 25: No source code is available for type hugobarrera.gestorAlumnos.server.LoginException; did you forget to inherit a required module?
Session.java is:
[...]
public interface Session extends RemoteService {
[...] (Line 25:)
String newSession(String usr, String pwd) throws LoginException;
[...]
Where am I going wrong? I've read like a MILLION places where people have problems with throwing exceptions, but none of those solutions applied.
All classes that need to be serialized must be in the [...].client package or a sub package.
Apparently they may not have a constructor either.
[edit] You need to have a no-argument constructor in the serializable classes.
skaffman: LoginException was not in the same package as Session.
Hugo: Moving them to the same package solved the problem.

Categories