I've implemented an RMI interface with these current files;
MyClient.java - Clientside code
MyServer.java - Serverside code
Adder.java - Interface
AdderRemote.java - Remote _implements Adder_
DataAccess.java - Contains all the methods to interveen between server and client
I have a vps which contains all files except the Client file in the directory
vps:~/rmi#
When testing this on it's own, ie: compiling on the server, doing
rmic AdderRemote
rmiregistry 5000&
(the port I have chosen)
java -classpath .:mysql-connector... MyServer
and then locally doing the same process but running the MyClient java instead, it works. The problem I am facing is now implementing this into a project I have running in eclipse as a part of this;
I have an instance of MyClient in the main file which is then passed as parameters to the certain classes (This project implements the MVC pattern and is passed to fellow model class'), and I am now getting the error
java.rmi.NotBoundException: xox
after googling, the only response I could find was "Attempt to look up a name that is not bound.", But I'm not really sure what this means? I'll attach my code, and any help would be much appreciated.
MyClient.java
public class MyClient
{
public Adder stub;
public MyClient ()
{
try
{
stub = (Adder)Naming.lookup("rmi://92.222.2.96:5000/xox");
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
public static void connect(Adder stub) throws RemoteException
{
System.out.println(stub.connect());
}
Adder.java
public interface Adder extends Remote
{
public String connect() throws RemoteException;
}
AdderRemote.java
public class AdderRemote extends UnicastRemoteObject implements Adder
{
public AdderRemote() throws RemoteException
{
super();
da = new DataAccess();
}
DataAccess da;
public String connect() throws RemoteException
{
return da.connect();
}
DataAccess.java
public class DataAccess
{
// Connection info
static final String URL = "jdbc:mysql://92.222.2.96:3306/groupproj";
static final String DRIVER = "com.mysql.jdbc.Driver";
static final String USERNAME = "root";
static final String PASSWORD = "*****";
Connection c = null;
public String connect()
{
try
{
Class.forName(DRIVER).newInstance(); //Load DB driver
c = DriverManager.getConnection(URL, USERNAME, PASSWORD); //Establish connection to DB2
return "Connected.";
}
catch(Exception e)
{
return e.toString();
}
}
MyServer.java
public class MyServer
{
public static void main ( String args[] )
{
try
{
Adder stub = new AdderRemote();
Naming.rebind("rmi://92.222.2.96:5000/xox", stub);
}
catch ( Exception e )
{
System.out.println(e);
}
}
public static void connect(Adder stub) throws RemoteException
{
try
{
stub.connect();
}
catch(Exception e)
{
System.out.println("Could not connect to the DB.");
}
}
I gathered that because the files on the server are located in the directory "rmi" I renamed the xox to this, but this did not solve the problem, so I reverted it back to xox, which it worked before putting it into a java project.
Thank you
You must have got an exception doing the bind.
If you got a NotBoundException when looking up the same name in the same Registry you're supposed to have bound it to, you didn't bind it at all.
Notes:
You can only bind to a Registry that is running in the same host as yourself. For that reason it is convenient to always use "localhost" as the hostname when calling bind(), rebind(), or unbind().
You'd be better off letting the RemoteException and NotBoundException be thrown from the constructor of MyClient.
MyClient.connect() should not be static. In fact it cannot be static. Ergo this cannot be the real code.
From what we can see so far, your system isn't correctly designed. Your server should get a DBMS connection when it needs one, inside a remote method, on behalf of the client that is calling that method, and release it before exiting the method. Opening a new connection every time a client asks for one explicity and storing it into an instance variable of the remote object (a) will leak connections and (b) won't work when concurrent clients come to execute a query or update on the same connection.
Related
I've been stuck for two days now, trying to resolve a communication problem between my singleton session bean and a standalone (rmi) client.
Here is the point: I have a remote session bean (LoadBalancer) which can be accessed by a client (ChatRoom), and which can also access to the client... in theory.
The remote interface:
public interface ILoadBalancer{
public void addChatRoom( IChatRoom chatRoom );
public void removeChatRoom( IChatRoom chatRoom );
}
The session bean:
#Remote(ILoadBalancer.class)
#Singleton
public class LoadBalancer implements ILoadBalancer {
/**
* List of available chat rooms
*/
private Map<String, IChatRoom> chatRooms;
private static final Logger logger = Logger.getLogger("loadBalancerLogger");
#PostConstruct
public void init() {
chatRooms = new HashMap<String, IChatRoom>();
}
#Override
public void addChatRoom(IChatRoom chatRoom) {
// Adding a new chat room to the list of available chat rooms
try {
chatRooms.put(chatRoom.getName(), chatRoom);
logger.log(Level.INFO, "Enregistrement d'un salon de discussion {0}", chatRoom.getName());
} catch (RemoteException ex) {
Logger.getLogger(LoadBalancer.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void removeChatRoom(IChatRoom chatRoom) {
// Removing a chat room from the list of available chat rooms
try {
chatRooms.remove(chatRoom.getName());
logger.log(Level.INFO, "Suppression du salon de discussion {0}", chatRoom.getName());
} catch (RemoteException ex) {
Logger.getLogger(LoadBalancer.class.getName()).log(Level.SEVERE, null, ex);
}
}
These are deployed in a glassfish server (4.1).
And now the standalone client using RMI:
public interface IChatRoom extends Remote {
public int postMessage( String pseudo, String message) throws RemoteException;
public int subscribe( String pseudo) throws RemoteException;
public List<String> getNewMessages(String pseudo, int sequenceNumber) throws RemoteException;
public String getName() throws RemoteException;
}
And the class implementing it:
public class ChatRoom implements IChatRoom, Serializable {
private String name;
private List<String> users;
private List<String> messages;
private static final Logger logger = Logger.getLogger("chatRoomLogger");
/**
* A reference to the load balancer
*/
private ILoadBalancer loadBalancer;
public ChatRoom(String name) throws RemoteException, NamingException {
this.name = name;
users = new ArrayList<String>();
messages = new ArrayList<String>();
// Making the chat room available via RMI
UnicastRemoteObject.exportObject(this);
// Retrieving a reference to the loadBalancer
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.impl.SerialInitContextFactory");
props.setProperty(Context.STATE_FACTORIES, "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty(Context.URL_PKG_PREFIXES, "com.sun.enterprise.naming");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
Context ctx = new InitialContext(props);
loadBalancer = (ILoadBalancer) ctx.lookup("java:global/loadBalancer/LoadBalancer!sn.esp.dgi.core.ILoadBalancer");
loadBalancer.addChatRoom(this);
}
#Override
public int postMessage(String pseudo, String message) throws RemoteException {
// Not relevant to the problem
}
#Override
public int subscribe(String pseudo) throws RemoteException {
// Not relevant to the problem
}
#Override
public List<String> getNewMessages(String pseudo, int sequenceNumber) throws RemoteException {
// Not relevant to the problem
}
#Override
public String getName() throws RemoteException {
// Not Relevant to the problem
}
}
As you have seen, the client has a remote reference to the session bean, and the session bean has also a remote reference to all the clients that have subscribed to it.
The client is launched with this code:
public class ChatRoomLauncher {
public static void main( String[] args ) {
if( args.length < 1 ) {
System.out.println("Usage: java ChatRoomLauncher <nomChatRoom>");
System.exit(-1);
}
String chatRoomName = args[0];
try {
IChatRoom chatRoom = new ChatRoom(chatRoomName);
System.out.println("Salon de discussion "+ chatRoomName +" démarré ");
} catch (RemoteException ex) {
Logger.getLogger(ChatRoomLauncher.class.getName()).log(Level.SEVERE, null, ex);
} catch (NamingException ex) {
Logger.getLogger(ChatRoomLauncher.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
After generating the stub and skeleton of respectively IChatRoom and ChatRoom, and placing the stub of IChatRoom in the directory where is deployed the LoadBalancer, I get the following exceptions which are my nightmares since two days:
Exception in thread "main" javax.ejb.NoSuchEJBException
at sn.esp.dgi.core._ILoadBalancer_Wrapper.addChatRoom(sn/esp/dgi/core/_ILoadBalancer_Wrapper.java)
at sn.esp.dgi.server.ChatRoom.<init>(ChatRoom.java:76)
at sn.esp.dgi.server.ChatRoomLauncher.main(ChatRoomLauncher.java:29)
Caused by: java.rmi.NoSuchObjectException: CORBA INV_OBJREF 1330447539 No; nested exception is:
org.omg.CORBA.INV_OBJREF: WARNING: 00511203: Class sn.esp.dgi.server.ChatRoom not exported, or else is actually a JRMP stub vmcid: OMG minor code: 1203 completed: No
at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.mapSystemException(Util.java:260)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:211)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
at sn.esp.dgi.core.__ILoadBalancer_Remote_DynamicStub.addChatRoom(sn/esp/dgi/core/__ILoadBalancer_Remote_DynamicStub.java)
... 3 more
Caused by: org.omg.CORBA.INV_OBJREF: WARNING: 00511203: Class sn.esp.dgi.server.ChatRoom not exported, or else is actually a JRMP stub vmcid: OMG minor code: 1203 completed: No
at com.sun.proxy.$Proxy54.objectNotExported(Unknown Source)
at com.sun.corba.ee.impl.util.Utility.autoConnect(Utility.java:160)
at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.writeRemoteObject(Util.java:473)
at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl$12.write(DynamicMethodMarshallerImpl.java:356)
at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl.writeArguments(DynamicMethodMarshallerImpl.java:465)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:196)
... 6 more
The exception tells me that I have not exported the chatRoom, though I have generated and placed its stub in the directory where I think it should be.
I've tried to change the instruction UnicastRemoteObject.exportObject(this) by PortableRemoteObject.exportObject(this), but I still get an error, different from this one.
Any help would be greatly appreciated.
Thanks
I had the exact same issues (EJB running on Glassfish 4 server, client with RMI callback object) and finally found a solution! The tricks were to:
Make the client implementation inherit from PortableRemoteObject, i.e. in your example:
public class ChatRoom extends PortableRemoteObject implements IChatRoom, Serializable
Note:
There were 3 choices for PortableRemoteObject offered by Eclipse:
javax.rmi.PortableRemoteObject -> did not work, wanted stubs which I did not generate for the client object (error message: java.rmi.StubNotFoundException: Stub class not found)
com.sun.corba.se.impl.javax.rmi.PortableRemoteObject -> same issue
com.sun.corba.ee.impl.javax.rmi.PortableRemoteObject -> WORKED, without generating stubs!!
Export the object in the constructor (in the same place where you have UnicastRemoteObject.exportObject(this);). Extending the class from com.sun.corba.ee.impl.javax.rmi.PortableRemoteObject allowed simply to use:
exportObject(this); // i.e. non-static reference is required
Note: if extending from javax.rmi.PortableRemoteObject then static reference to PortableRemoteObject.exportObject(this) is needed but then one needs the stub as I've found. You could try that, too, since you have the stubs.
Hope this helps!
I want to build system so that :
1. Client connects to Server
2. Client asks for a port to server him
3. Server creates a remote object to serve the Client and binds it to a port
4. return the port number to client
5. Client connects to the port
My session/connection manager
public class ConnectionManager extends UnicastRemoteObject implements Server
{
public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
public ConnectionManager() throws RemoteException
{
super();
}
public static final String RMI_ID = "Server";
#Override
public boolean checkConnection() throws RemoteException
{
return true;
}
#Override
public int getPort(String ip) throws RemoteException
{
int i = 10000+clients.size()*2;
clients.add(new ClientHandler(ip, i));
return i;
}
}
Session implementation
public class ClientHandler extends UnicastRemoteObject implements Transfer
{
Registry rmi;
Registry reg;
PrintWriter log;
public Client client;
public ClientHandler(String ip, int port) throws RemoteException
{
super();
try
{
File f = new File(ip+" "+new Date()+".txt");
log = new PrintWriter(f);
}catch (Exception e)
{
e.printStackTrace();
}
rmi = LocateRegistry.createRegistry(port);
try
{
rmi.bind(String.valueOf(port),this);
}catch(Exception e)
{
e.printStackTrace();
}
}
The problem that if the object is created in a remote call , it is considered of remote origin and so unless you are on the same host it is not allowed to bind one to the LocalRegistry. and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.
The problem that if the object is created in a remote call , it is considered of remote origin
Untrue. This is simply not correct. You've just made this up. Don't do that.
and so unless you are on the same host it is not allowed to bind one to the LocalRegistry.
But you are on the same host. The constructor ClientHandler runs in the same host as the ConnectionManager and it creates a Registry, also in the same host. Indeed all this takes place within the same JVM.
and server throws java.rmi.AccessException Registry.Registry.bind disallowed : origin IP address is non-local host.
No it doesn't. I tested your code. After fixing a compilation error it worked. Cannot reproduce.
HOWEVER
You don't need the part about the port, or the extra bind. All remote objects can share the same port. The server only needs to return a new instance of the remote session object to the client, directly.
A simple implementation of your requirement, using your classnames, looks like this, with a certain amount of guesswork as you didn't provide the Server interface:
public interface Server extends Remote
{
boolean checkConnection() throws RemoteException;
Transfer getSession(String ip) throws RemoteException;
}
public class ConnectionManager extends UnicastRemoteObject implements Server
{
public List<ClientHandler> clients = Collections.synchronizedList(new ArrayList<>());
public ConnectionManager() throws RemoteException
{
super();
}
public static final String RMI_ID = "Server";
#Override
public boolean checkConnection() throws RemoteException
{
return true;
}
#Override
public Transfer getSession(String ip) throws RemoteException
{
ClientHandler ch = new ClientHandler(ip);
clients.add(ch);
return ch;
}
}
public class ClientHandler extends UnicastRemoteObject implements Transfer
{
PrintWriter log;
public Client client;
public ClientHandler(String ip) throws RemoteException
{
super();
try
{
File f = new File(ip+" "+new Date()+".txt");
log = new PrintWriter(f);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
EDIT
It is clear from your post and your comment below that you are suffering from a number of major misconceptions about Java RMI:
if a remote object is created in a remote call , it is not 'considered of remote origin'
The port on which an object is exported has nothing to do with the Registry.
It is determined when you construct an object that extends UnicastRemoteObject, via the super(), call, or when you call UnicastRemoteObject.exportObject() for other objects. If you supply a non-zero port number, that port is used, and can normally shared with other remote objects. Otherwise if there is already an RMI port in use it is shared with this object, otherwise a system-allocated port number is obtained.
Note that the export step precedes the bind step, so it is quite impossible for your misconception to be correct.
You don't need multiple Registries running in the same host. You can use a single one and bind multiple names to it.
If you first call LocateRegistry.createRegistry(), all other remote objects you export from that JVM can share the port with the Registry.
Remote methods can return remote objects.
The Registry is an example: it is actually very little more than a remote hash-map. Your methods can do it too. The objects are replaced by their stubs during the return process.
For those reasons, your intended design is completely fallacious and your comment below complete nonsense.
Further notes:
A do-nothing method doesn't really check a connection. It is just as likely to create a new connection and check that. Connections don't really exist in RMI, or at least they are very well hidden from you, pooled, expired, etc.
You don't need to pass the client's IP address. You can get it at the server from RemoteServer.getClientHost().
The constructor for ClientHandler should not catch IOExceptions internally: it should throw them to the caller, so the caller can be aware of the problem.
While performing a client-server communication with various forums, I am unable to perform Remote-object's lookup on the client machine.
The errors which I receive are ConnectIOException(NoRouteToHostException), and sometimes ConnectException and sometimes someother.
This is not what I want to ask. But, the main concern is how should I setup client platform and server platform --- talking about networking details --- this is what I doubt interferes with my connection.
My questions :-
How should I edit my /etc/hosts file on both client-side and server-side? Server's IP- 192.168.1.8 & Client's IP-192.168.1.100. Means, should I add the system name in both the files:
192.168.1.8 SERVER-1 # on the server side
192.168.1.100 CLIENT-1 # on the client side
Should I edit like this? Can this be one of the possible concerns? I just want to remove any doubts left over to perform the rmi-communication!
Also, I am also setting Server's hostname property using System.setProperty("java.rmi.server.hostname",192.168.1.8); on the server side. Should I do the same on the client-side too?
I've read about setting classpath while running the java program on both server-side as well as the client-side. I did this too,but,again the same exceptions. No difference at all. I've read that since Java update 6u45, classpaths aren't necessary to include! Please throw some light on this too...
If I am missing something, Please enlighten about the same too. A brief idea/link to resources are most preferred.
You don't need any of this unless you have a problem. The most usual problem is the one described in the RMI FAQ #A.1, and editing the hosts file of the server or setting java.rmi.server.hostname in the server JVM is the solution to that.
'No route to host' is a network connectivity problem, not an RMI problem, and not one you'll solve with code or system property settings.
Setting the classpath has nothing to do with network problems.
Here is server example of which transfers an concrete class. This class must be exist in server and client classpath with same structure
Message:
public class MyMessage implements Serializable {
private static final long serialVersionUID = -696658756914311143L;
public String Title;
public String Body;
public MyMessage(String strTitle) {
Title = strTitle;
Body = "";
}
public MyMessage() {
Title = "";
Body = "";
}
}
And here is the server code that gets an message and returns another message:
public class SimpleServer {
public String ServerName;
ServerRemoteObject mRemoteObject;
public SimpleServer(String pServerName) {
ServerName = pServerName;
}
public void bindYourself() {
try {
mRemoteObject = new ServerRemoteObject(this);
java.rmi.registry.Registry iRegistry = LocateRegistry.getRegistry(RegistryContstants.RMIPort);
iRegistry.rebind(RegistryContstants.CMName, mRemoteObject);
} catch (Exception e) {
e.printStackTrace();
mRemoteObject = null;
}
}
public MyMessage handleEvent(MyMessage mMessage) {
MyMessage iMessage = new MyMessage();
iMessage.Body = "Response body";
iMessage.Title = "Response title";
return iMessage;
}
public static void main(String[] server) {
SimpleServer iServer = new SimpleServer("SERVER1");
iServer.bindYourself();
while (true) {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
and here is the remote interface of server remote object:
public interface ISimpleServer extends java.rmi.Remote{
public MyMessage doaction(MyMessage message) throws java.rmi.RemoteException;
}
all you need is adding MyMessage class both in server and client classpath.
I'm doing an agenda on server and everything was going fine, I started the RMI registry and the server was working fine, I test the code and I was able to login.
now I want to start putting the codes in methods and calling them in the client from the server,and when I put the Naming.rebind("server", i); in the server to call methods the code breaks.
My project is contain three classes: server, client and interface that calls the methods from the server to the client. This is the code of the server:
public class ServerAgendas extends UnicastRemoteObject
implements InterfaceServer {
private static final long serialVersionUID = 1L;
protected ServerAgendas() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
static String Clientusername;
static String Clientpassword;
public static void main (String args[]) throws NotBoundException,
IOException {
System.out.println("1");
try{
InterfaceServer i = new ServerAgendas();
System.out.println("2");
Naming.rebind("server",i);
System.out.println("Serveur agendas pret");
}
catch (Exception e){
System.err.println("Error "+e.getMessage());
}
ServerSocket ASocket = new ServerSocket(portNumber);
Socket connectionSocket = ASocket.accept();
}
public boolean login(String ClientUsername, String ClientPassword)
throws IOException, SQLException {
}
in the client side I use this to call the methods:
InterfaceServer i = (InterfaceServer)Naming.lookup("server");
System.out.println(i.login(username, password));
and the interface contain:
public interface InterfaceServer extends Remote{
public boolean login(String Clientusername, String Clientpassword)
throws RemoteException, IOException, SQLException;
I run the registry at first then I run the server, the console shows:
1
2
Error RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: InterfaceServer
I used the 1 and 2 to see where it is breaking, it is breaking on:
Naming.rebind("server",i);
What do I need to do to avoid this exception occurring?
The class mentioned in the message isn't available to the Registry on its CLASSPATH.
The simplest solution to this and several other problems is to run the Registry inside the server JVM, with LocateRegistry.createRegistry().
Otherwise you will have to figure out how to start the rmiregistry command with a CLASSPATH argument, or else use the codebase feature, which is another kettle of fish altogether.
I have a scenario where I have to connect to virtual center and get data. I have implemented a singleton class so that two threads cannot access the VC at the same time, as it has give concurrent access issue. My code is as follows:
public class Connector {
private static Connector instance ;
private Connector(String urlStr, String username, String password) {
connect(urlStr, username, password);
}
public static synchronized Connector getInstance(String urlStr, String username, String password) {
if (instance == null){
instance = new Connector(urlStr,username,password);
System.out.println("creating instance");
}
return instance ;
}
public void connect(String urlStr, String username, String password) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
//code to connect to VC
}
} catch (RuntimeException e) {
connectException = e;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public void disconnect() throws RuntimeFault, RemoteException {
//code for disconnect
}
}
}
I called this from another class in the following way:
Connector c = Connector.getInstance(dburl, dbuser, dbpass);
c.connect(dburl, dbuser, dbpass);
//code for getting data
c.disconnect();
Now if I have 2 simultaneous requests to get data from viruatal center, one of them fails saying "session is not authenticated".
Can you help we with a better way to deal with the issue.
And as the same instance is used always, how can we differentiate if its a different Virtual Center.
Your sequence
connect
do stuff
disconnect
is not atomic which means it is possible that
Thread 1 connects
Thread 2 connects
Thread 1 does stuff
Thread 1 disconnects
Thread 2 tries to do stuff and fails.
Structure your class so that clients cannot fail to connect, fail to disconnect, or interleave operations with another.
Your API could allow code that wants to use a connection to pass in an object that uses a connection while it is connected, and returns a result of using that connection:
public interface ConnectionTask<T> {
public T useConnection(Connection c);
}
and then instead of getInstance write a useConnection that does
public synchronized <T> T useConnection(ConnectTask<T> c) {
connect();
try {
return c.useConnection(this);
} finally {
disconnect();
}
}
and make connect and disconnect private so that clients cannot misuse them.
You are only retaining exclusive access to the connection in the first method.
I suggest you add a Lock like ReentrantLock which you lock in the first method and unlock when you release the connection.
Another approach is to use a visitor pattern which may be safer.
interface UsesConnector {
public void useConnector(Connector connector);
}
public class Connector {
private static final Connector connector = new Connector();
public static synchronized void useConnector(String urlStr, String username, String password, UsesConnector usesConnector) {
connector.connect(urlStr, username, password);
try {
usesConnector.useConnector(connector);
} finally {
connector.disconnect();
}
}
}