Возврат экземпляра класса через вызов RMI

Я пытаюсь вернуть нормальный класс через вызов RMI. На моем сервере хранится экземпляр класса GameState, над которым я хочу выполнять действия с помощью его методов из клиентского приложения. Таким образом, RMI отлично работает, если просто вернуть целое число или что-то в этом роде, но при попытке вернуть GameState, который является классом, определенным внутри java-файла GameServer, возникает следующая ошибка (состояние игры не объявляется ни общедоступным, ни защищенным, ни закрытым):

Исключение в потоке "main" java.lang.IllegalAccessError: попытка доступа к классу GameState из класса $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, чтобы делать все это, и, основываясь на том, что у меня есть красное мнение о RMI в eclipse, rmic и другие вещи больше не нужны, я прав?

Итак, у кого-нибудь есть идеи?

Заранее спасибо!


person Mockarutan    schedule 09.02.2012    source источник


Ответы (3)


Я не думаю, что это проблема разрешения. Я не могу точно сказать по приведенному выше коду, но я предполагаю, что это проблема с кодовой базой. Вы настроили кодовую базу также на стороне клиента?

Чтобы десериализовать класс GameState, клиент должен иметь возможность загрузить определение класса. Это определение находится в реализации сервера, а не в интерфейсе. Обычно реализация сервера не должна компилироваться в путь к классам клиента, должен компилироваться только интерфейс. Я не совсем уверен, так как в вашем решении интерфейс, похоже, зависит от реализации из-за GameState, что, кстати, не очень хорошая идея. В любом случае, попробуйте добавить конфигурацию кодовой базы в ваши VM-args. Предполагая, что вы выполняете все на локальном хосте, это должно выглядеть так:

-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.

person joergl    schedule 09.02.2012
comment
Это не поможет. Это не ClassNotFoundException или NoClassDefFoundError. Это вообще не проблема кодовой базы. - person user207421; 10.02.2012
comment
проблема в том, что GameState является частным пакетом, ошибка очень очевидна. - person bestsss; 10.02.2012
comment
@bestsss: Вы правы, это очевидная проблема, которую я пропустил. Cпасибо за поправку! - person joergl; 10.02.2012

попробуйте вернуть GameState, который является классом, определенным внутри java-файла GameServer, возникает следующая ошибка (состояние игры не объявлено ни общедоступным, ни защищенным, ни закрытым)

Это проблема. Только класс GameServer и классы в одном пакете могут создавать экземпляры GameState. Ваш прокси-объект RMI (заглушка) Сделайте его общедоступным классом в собственном файле.

Я попытался сделать GameState общедоступным классом в своем собственном файле, но тогда различные подключающиеся клиентские приложения получают каждое свое собственное GameState, поэтому кажется, что это не получается с сервера.

Это правильно. Он сериализуется для каждого клиента. Если вы хотите поделиться одним GameState и оставить его на сервере, это должен быть сам экспортированный удаленный объект с удаленным интерфейсом под названием GameState.

person user207421    schedule 10.02.2012

Причина IllegalAccessError проста:

    GameState НЕ является общедоступным

Однако есть большая проблема:

вы понимаете, что loginPlayer не будет делать то, что вам нравится... GameState — это копия исходного состояния. Вы хотите, чтобы GameState был Remote несериализуемым, поэтому вы можете выполнять операцию на сервере, а не каждый клиент, чтобы получить бесполезную копию.

person bestsss    schedule 10.02.2012