Как будто вы пытаетесь получить доступ к объекту, который является null
. Рассмотрим ниже пример:
TypeA objA;
. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException
, что имеет смысл.
См. Также этот пример:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Можно сделать это с пользовательской Фабрикой Сокета RMI.
Фабрики сокета создают сокеты для RMI для использования в обоих конца клиента и сервера поэтому, если Вы пишете свое собственное, у Вас есть полный контроль над используемыми портами. Клиентские фабрики создаются на сервере, Сериализированном и затем отправленном вниз клиенту, который крут.
Сводка длинного ответа ниже: для решения проблемы, которую я имел (ограничение сервера и портов обратного вызова с обоих концов соединения RMI) я должен был создать двух пар фабрик сокета клиента и сервера.
Более длинный ответ следует:
Наше решение проблемы обратного вызова имело по существу три части. Первым было обертывание объекта, для которого была нужна способность указать, что это привыкло для клиента к серверному соединению по сравнению с тем, чтобы быть используемым для сервера к клиентскому обратному вызову. Используя расширение UnicastRemoteObject
дал нам способность указать фабрики сокета клиента и сервера, которые мы хотели использовать. Однако лучшее место для блокировки вниз фабрик сокета находится в конструкторе удаленного объекта.
public class RemoteObjectWrapped extends UnicastRemoteObject {
// ....
private RemoteObjectWrapped(final boolean callback) throws RemoteException {
super((callback ? RemoteConnectionParameters.getCallbackPort() : RemoteConnectionParameters.getServerSidePort()),
(callback ? CALLBACK_CLIENT_SOCKET_FACTORY : CLIENT_SOCKET_FACTORY),
(callback ? CALLBACK_SERVER_SOCKET_FACTORY : SERVER_SOCKET_FACTORY));
}
// ....
}
Так, первый аргумент указывает часть, на которой объект ожидает запросы, тогда как второе и третье указывают фабрики сокета, которые будут использоваться с обоих концов соединения, управляющего этим удаленным объектом.
Так как мы хотели ограничить порты, используемые соединением, мы должны были расширить фабрики сокета RMI и заблокировать вниз порты. Вот некоторые эскизы нашего сервера и клиентских фабрик:
public class SpecifiedServerSocketFactory implements RMIServerSocketFactory {
/** Always use this port when specified. */
private int serverPort;
/**
* @param ignoredPort This port is ignored.
* @return a {@link ServerSocket} if we managed to create one on the correct port.
* @throws java.io.IOException
*/
@Override
public ServerSocket createServerSocket(final int ignoredPort) throws IOException {
try {
final ServerSocket serverSocket = new ServerSocket(this.serverPort);
return serverSocket;
} catch (IOException ioe) {
throw new IOException("Failed to open server socket on port " + serverPort, ioe);
}
}
// ....
}
Обратите внимание, что фабрика сокета сервера выше гарантирует, что только порт, который Вы ранее указали, будет когда-либо использоваться этой фабрикой. Клиентская фабрика сокета должна быть соединена с соответствующей фабрикой сокета (или Вы никогда не будете соединяться).
public class SpecifiedClientSocketFactory implements RMIClientSocketFactory, Serializable {
/** Serialization hint */
public static final long serialVersionUID = 1L;
/** This is the remote port to which we will always connect. */
private int remotePort;
/** Storing the host just for reference. */
private String remoteHost = "HOST NOT YET SET";
// ....
/**
* @param host The host to which we are trying to connect
* @param ignoredPort This port is ignored.
* @return A new Socket if we managed to create one to the host.
* @throws java.io.IOException
*/
@Override
public Socket createSocket(final String host, final int ignoredPort) throws IOException {
try {
final Socket socket = new Socket(host, remotePort);
this.remoteHost = host;
return socket;
} catch (IOException ioe) {
throw new IOException("Failed to open a socket back to host " + host + " on port " + remotePort, ioe);
}
}
// ....
}
Так, единственная вещь, остающаяся вызвать Ваши два способа, которыми соединение для пребывания на том же наборе портов является некоторой логикой, чтобы распознать, что Вы перезваниваете к клиентскому. В той ситуации просто удостоверьтесь, что Ваш метод фабрики для удаленного объекта называет конструктора RemoteObjectWrapper вершиной с набором параметра обратного вызова к истинному.
У меня были различные проблемы с реализацией архитектуры RMI Server/Client, с обратными вызовами клиента. Мой сценарий таков, что и сервер и клиент находятся за Firewall/NAT. В конце концов, я получил полностью рабочую реализацию. Вот основные вещи, которые я сделал:
На Firewall/Router/Local Server PC открыт порт 6620. На брандмауэре/маршрутизаторе/локальном серверном ПК откройте порт 1099. На маршрутизаторе/НАТ перенаправьте входящие соединения на порт 6620 на 192.168.1.10:6620 На Router/NAT перенаправить входящие соединения по порту 1099 на 192.168.1.10:1099
В реальной программе:
System.getProperties().put("java.rmi.server.hostname", IP 80.80.80.10);
MyService rmiserver = new MyService();
MyService stub = (MyService) UnicastRemoteObject.exportObject(rmiserver, 6620);
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
registry.rebind("FAManagerService", stub);
На Firewall/Router/Local Server PC открыть порт 1999. На маршрутизаторе/НАТ перенаправьте входящие соединения на порт 1999 на 10.0.1.123:1999
В самой программе:
System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
UnicastRemoteObject.exportObject(this, 1999);
MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");
Надеюсь, это поможет. Iraklis