I have the folloving RMI server:
public static void main(String[] args) {
try{
ChatSystemImpl chatSystem = new ChatSystemImpl();
ChatSystem chatSystem_stub = (ChatSystem) UnicastRemoteObject.exportObject(chatSystem, 6001);
Registry registry = LocateRegistry.getRegistry("localhost", 6001);
registry.bind("ChatSystem1", chatSystem_stub);
System.out.println("Server up.");
}
catch(Exception ex){
ex.printStackTrace();
}
}
When I run it I get:
ava.rmi.NoSuchObjectException: no such object in table
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:273)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:251)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:377)
at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
at fr.inp.ensimag.examples.chatsystem.Main.main(Main.java:30)
Well I have no idea whats wrong... Staring on it for more then 2 hours right now.
This is the interface (if needed):
public interface ChatSystem extends Remote{
void registerUser(UserInfo newUser) throws RemoteException;
void unregisterUser(UserInfo user) throws RemoteException;
boolean userExists(UserInfo user) throws RemoteException;
void send(MessageInfo message) throws RemoteException;
}
The implementation has following header, the body just contains methods that are not really implemented, they just thow UnsupportedOperationException :
public class ChatSystemImpl implements ChatSystem
The ChatSystem interface is in other project then the rest of source code if it is of any importance.
Thank you.
Your Registry has been unexported. This usually means that you are running it in a JVM via LocateRegistry.createRegistry() and you have let it become garbage-collected. The result of that method should be stored in a static variable. It may also mean that you never started the Registry in the first place.
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 control bandwidth for data transfer.
According to Netty document, they suggest:
In your handler, you should consider to use the channel.isWritable() and channelWritabilityChanged(ctx) to handle writability, or through future.addListener(new GenericFutureListener()) on the future returned by ctx.write().
Here is my channel initializer source code:
public class MyChannelInitializer extends ChannelInitializer<Channel>
{
private int mode;
private Server server;
private String fileName;
public MyChannelInitializer(Server server, int mode,String fileName)
{
this.mode=mode;
this.server=server;
this.fileName=fileName;
}
#Override
protected void initChannel(Channel ch) throws Exception
{
if (this.mode==MyFtpServer.RECEIVEFILE)
{
ch.pipeline().addLast(new ChannelTrafficShapingHandler(0L,10240L));
ch.pipeline().addLast(new ReceiveFileHandler(this.fileName,server));
}
else
{
ch.pipeline().addLast(new ChannelTrafficShapingHandler(10240L,0L));
ch.pipeline().addLast("streamer", new ChunkedWriteHandler());
ch.pipeline().addLast("handler",new SendFileHandler(this.fileName,server));
}
}
}
Here is Send File Handler source code:
public class SendFileHandler extends SimpleChannelInboundHandler<ByteBuf>
{
String fileName;
PassiveServer txServer=null;
public SendFileHandler(String fileName, PassiveServer txServer)
{
this.fileName=fileName;
this.txServer=txServer;
}
public void channelWritabilityChanged(ChannelHandlerContext ctx)throws IOException
{
System.out.println("isWritable="+ctx.channel().isWritable());
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws IOException
{
System.out.println("SendFileHandler active");
}
}
Would you tell me why "channelWritabilityChanged" method in my SendFileHandler is never trigger, only channelActive method is triggered.?
After I changed SendFileHandler.channelActive method as the following:
public void channelActive(ChannelHandlerContext ctx) throws IOException
{
Calendar startTime;
System.out.println("SendFileHandler active");
startTime=Calendar.getInstance();
ctx.fireChannelWritabilityChanged();
}
The SendFileHandler.channelWritabilityChanged method still not triggered.
Would anyone tell me why?
I believe because ChunkedWriteHandler does the job, taking into account writability directly. However, since its last code in channelWritabilityChanged is:
ctx.fireChannelWritabilityChanged();
You should have the signal of writability changed.
But do you send any file? Because in your code, not, and therefore no channelWritabilityChanged shall be raized since there seems to be no write at all... (of course, writability only occurs on write part, not on read part).
You could also have a look at this thread regarding traffic shaping.
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.
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.
When you design an a client that is going to connect to a lot of servers, like a crawler.
You will code something like that :
// the pipeline
public class CrawlerPipelineFactory implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new CrawlerHandler());
}
}
// the channel handler
public class CrawlerHandler extends SimpleChannelHandler {
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// ...
}
}
// the main :
public static void main(){
ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool());
ClientBootstrap scannerBootstrap = new ClientBootstrap(factory);
scannerBootstrap.setPipelineFactory(new CrawlerPipelineFactory());
while(true){
MyURL url = stack.pop();
ChannelFuture connect = scannerBootstrap.connect(url.getSocketAddress());
}
}
Now when you are in your ApplicationHandler, the stuff that implements your SimpleChannelHandler or WhatEverStreamHandler, (CrawlerHander in the example) the only piece of information you get is the socketAdress you are connecting to that you can recover in "public void channelConnected()" function.
Ok but what if I want to recover some user data, like the MyURL object you see in my code example ?
I use a dirty hack, I use a Map<"ip:port",MyURL> so I can retrieve the associated data in channelConnected because I know ip:port i'm connected on.
This hack is really dirty, it won't work if you are connecting simultaneously to the same server (or you'll have to bind to a local port and use a key like "localport:ip:remoteport" but it's so dirty).
So I'm seeking what is the good way to pass data the the CrawlerHander ?
It would be cool if we could pass this data via the connect() method of the bootstrap. I know I can pass argument in my ChannelPipelineFactory.getPipeline() because it's invoked via connect(). But now we can't, so here is another dirty hack I use :
EDIT:
// the main
while(!targets.isEmpty()){
client.connect("localhost",111); // we will never connect to localhost, it's a hack
}
// the pipleline
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new CrawlerHandler(targets.pop()) // I specify each new host to connect here
);
}
// in my channel handler
// Now I have the data I want in the constructor, so I m sure I get them before everything is called
public class CrawlerHandler extends SimpleChannelHandler {
ExtraParameter target;
public CrawlerHandler(ExtraParameter target) {
this.target = target;
// but, and it's the most dirty part, I have to abort the connection to localhost, and reinit a new connection to the real target
boolean bFirstConnect=true;
#Override
public void connectRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
if(bFirstConnect){
bFirstConnect = false;
ctx.getChannel().connect(target.getSocketAddr());
}
You can pass variables to Channel via Bootstrap.
Netty.io 4.1 & SO - Adding an attribute to a Channel before creation
Update to this answer while very late.
You can pass the data to the newly connected channel/channel handler using ChannelLocal or in ChannelHandlerContext (or in the Channel it self in latest Netty 3.x) using a connect future listener. In below example, ChannelLocal is used.
public class ChannelDataHolder {
public final static ChannelLocal<String> CHANNEL_URL = new ChannelLocal<String>(true);
}
// for each url in bootstrap
MyURL url = ....;
ChannelFuture cf = scannerBootstrap.connect(url.getSocketAddress());
final String urlString = url.getUrl();
cf.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
ChannelDataHolder.CHANNEL_URL.set(future.getChannel(), urlString);
}
});
//In the handler
public class CrawlerHandler extends SimpleChannelHandler {
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
String urlString = ChannelDataHolder.CHANNEL_URL.get(ctx.getChannel());
// ...use the data here
}
}
Note: instead of ChannelLocal, you can set and get the data using
ChannelHandlerContext.setAttachment()/getAttachment()
Channel.setAttachment()/getAttachment() in latest 3.x version of Netty
but both approaches does not support type safety.