2012-02-09 81 views
1

我试图通过RMI调用返回一个普通的类。我的服务器拥有一个名为GameState的类的实例,我想通过它的方法从客户端应用程序执行操作。所以如果一个返回一个int或者某个东西,RMI工作的很好,但是当试图返回GameState(GameServer java文件中定义的一个类)时,会发生以下错误(游戏状态声明不是public,protected或private):螺纹通过RMI调用返回类实例

异常 “主要” java.lang.IllegalAccessError:试图从类$ Proxy0 在$ Proxy0.getGameState(来源不明) 在GameClient.login(GameClient.java:204) 在GameClient访问类的游戏状态.main(GameClient.java:168)

所以,我猜客户端应用程序知道如何GameState看起来,但没有任何访问它?

我试图让GameState在它自己的文件中成为一个公共类,但不同的连接客户端应用程序获取它们自己的GameState,所以它看起来好像不从服务器获取它。

下面是一些代码,我认为这是相关的:

远程接口:

import java.rmi.*; 

public interface ServerInterface extends Remote 
{ 
    public GameState getGameState() throws RemoteException; 
} 

一些,如果服务器代码:

public class GameServer extends UnicastRemoteObject implements ServerInterface { 
    /** 
    * 
    */ 
    private static final long serialVersionUID = -6633456258968168102L; 

    private final static int DEFAULT_NAMING_PORT = 9955; // TODO: IMPORTANT - change this to a group-specific number, 
    // e.g., 2000 + group number. The number should be the same 
    // as in GameClient.java. 

    private final GameState m_state; 

    public static void main(String[] args) { 

      //the variables: port and host etc it configurated here, but has nothing to do with the RMI problem. 

     try { 
      GameServer instance = new GameServer(players); 
      System.out.print("Setting up registry on "+host+":"+port+" ... "); 

      //Set up an unrestricted security manager. 
      if (System.getSecurityManager() == null) { 
       // Set security manager to an instance of a dynamically created 
       // subclass of RMISecurityManager with the checkPermission() method overloaded 
       System.setSecurityManager(
         new RMISecurityManager() { 
          @Override 
          public void checkPermission(Permission permission) { 
          } 
         } 
       ); 
      } 

      // Create a registry for binding names (name server) 
      Registry naming = LocateRegistry.createRegistry(port); 
      System.out.println("done."); 

      String rmiObjectName = "GeschenktServer"; 
      System.out.print("Binding name "+rmiObjectName+" ... "); 
      naming.rebind(rmiObjectName, instance); 
      System.out.println("done."); 
     } catch(RemoteException e) { 
       System.err.println("Could not start server: "+e); 
       System.exit(-1); 
      } 
     } 

    //the rest of the server code.... 

    //the GameState declared in the same file 

class GameState implements java.io.Serializable { 

    private static final long serialVersionUID = 545671487061859760L; 

//the rest of the Game state code. 

下面是一些客户端的代码:

private void login() { 
     try { 
      System.out.println("Connecting to server on host "+m_host+"."); 

      // Set up an unrestricted security manager. In the server we trust. 
      // See GameServer.java for code explanation. 
      if (System.getSecurityManager() == null) { 
       System.setSecurityManager(
         new RMISecurityManager() { 
          @Override 
          public void checkPermission(Permission permission) { 
          } 
         } 
       ); 
      } 

      System.out.print("Locating registry on "+m_host+":"+m_port+" ... "); 
      Registry naming = LocateRegistry.getRegistry(m_host, m_port); 
      System.out.println("done."); 
      String name = "GeschenktServer"; 
      System.out.print("Looking up name "+name+" ... "); 
      m_server = (ServerInterface) naming.lookup(name); 
      System.out.println("done."); 

      // TODO: Connect the player, i.e., register the player with the server. 
      // Make sure that the player cannot register if there are already enough players. 

      m_Id = m_server.getGameState().loginPlayer(m_name); //this line is causing the error... 

      if(m_Id < 0) 
       System.exit(0); 

      System.out.println("Server connection successful.");   
      m_window = new GameWindow(m_server, m_name, m_Id); 
      m_window.run(); 
     } catch (Exception e) { 
      System.out.println("Connection failed - "+e); 
      System.exit(1); 
     } 
    } 
} 

我使用eclipse来完成所有这些工作,并且基于我在eclipse中有关RMI的红色,rmic和那些东西不再需要,我是对的?

那么任何人有任何想法?

在此先感谢!

回答

0

我不认为这是一个权限问题。我无法从上面的代码中确定,但我会认为这是一个代码库问题。您是否也在客户端配置了代码库?

要反序列化GameState类,客户端需要能够加载类定义。这个定义位于Server实现中,而不是接口。通常,服务器实现不应编译到客户端的类路径,只有接口应该。我不完全确定,因为在你的解决方案中,由于GameState这个接口似乎对实现有依赖性,所以这不是一个好主意。无论如何,尝试添加一个代码库配置到你的虚拟机参数。假设你执行本地主机上的一切,应该是这样的:

-Djava.rmi.server.codebase=file:${workspace_loc}/PROJECT-NAME/bin/ 

哪里${workspace_loc}是您的工作空间和PROJECT-NAME的绝对路径是服务器项目的名称。 Eclipse会自动解决${workspace_loc},所以你只需要设置你的PROJECT-NAME

作为一个方面说明:如果要实现这样的说法,该GameState对象传输到客户端和客户端上执行,有没有影响无论在服务器的执行上。这真的是你想要的吗?如果您希望GameState实例在服务器端执行,则GameState还需要实现Remote而不是Serializable,并且在将其存根发送到客户端时需要将其导出。

最后,当你正确地指出,你并不需要使用RMIC自从Java 1.5

+0

这不会帮助。它不是ClassNotFoundException或NoClassDefFoundError。这根本不是一个代码库问题。 – EJP 2012-02-10 00:12:46

+0

问题是GameState是封装私有的,错误非常明显。 – bestsss 2012-02-10 00:25:22

+0

@bestsss:你说得对,那是我错过的一个明显问题。感谢您的更正! – joergl 2012-02-10 09:04:14

0

try to return GameState, which is a class defined inside the GameServer java file, the following error occurs (game state is declared neither public, protected or private)

这就是问题所在。只有同一个包中的GameServer类和类才能创建GameState的实例。您的RMI代理对象(存根)在其自己的文件中将其作为公共类。

I have tried to make GameState a public class in it's own file, but then the different connecting client applications get each their own GameState, so it's seems like that dont get it from the server

这是正确的。它被序列化到每个客户端。如果你想共享一个GameState并保留在服务器上,它必须是一个导出的远程对象本身,其中一个叫做GameState的远程接口

0

IllegalAccessError原因很简单:

    游戏状态是不公开的

然而,有一个更大的问题:

你也明白,loginPlayer不会做你喜欢它...的游戏状态是原始状态的拷贝。你想GameState为Remote不可序列化,所以你可以在服务器上执行操作,而不是每个客户端都得到一个无用的副本。