Дистанційний виклик методів (RMI) - Thinking In Java Enterprise (український переклад)
Традиційний підхід до виконання коду на інших машинах, рознесених по мережі, може збентежити через свою нудну і схильну до помилок реалізації. Найкращий спосіб розглянути цю проблему полягає в припущенні, що деякі об'єкти розміщуються на іншій машині, і що ви можете надсилати повідомлення цим віддаленим об'єктам і отримувати результат, начебто вони розміщуються на вашій локальній машині. Це спрощення в точності є тим, що дозволяє робити віддалений виклик методів (RMI) в Java. Цей розділ зробить огляд кроків, необхідних для створення власного RMI об'єкта.
Віддалений інтерфейс
RMI ускладнює використання інтерфейсів. Коли ви хочете створити віддалений об'єкт, ви маскуєте реалізацію, що лежить в основі, спілкуючись по інтерфейсу. Тобто коли клієнт отримує посилання на віддалений об'єкт, реально він отримує посилання на інтерфейс, який з'єднаний з деяким локальним кодом заглушки, яка спілкується по мережі. Але ви не думаєте про це, ви просто надсилаєте повідомлення через ваше посилання на інтерфейс.
Коли ви створюєте віддалений інтерфейс, ви повинні дотримуватися наступного посібника:
- Віддалений інтерфейс повинен бути публічним (він не може мати "пакетний рівень доступу", таким чином він не може бути "дружнім"). Інакше клієнт отримає помилку при спробі завантажити віддалений об'єкт, який реалізує віддалений інтерфейс.
- Віддалений інтерфейс повинен успадковуватись від інтерфейсу java.rmi.Remote.
- Кожен метод віддаленого інтерфейсу повинен декларувати java.rmi.RemoteException у своєму розділі throws на додаток до решти специфічних для застосування винятків.
- Віддалений об'єкт,що передається як аргумент або значення, що повертається (або безпосередньо, або як вбудована частина локального об'єкта), повинен бути декларований як віддалений інтерфейс, а не клас реалізації.
Ось найпростіший віддалений інтерфейс, який представляє службу точного часу:
//: c15:rmi:PerfectTimeI.java // The PerfectTime remote interface. package c15.rmi;
public interface PerfectTimeI extends Remote long getPerfectTime() throws RemoteException; > ///:
Він виглядає як будь-який інший інтерфейс, за винятком того, що він є спадкоємцем Remote і всі його методи викидають RemoteException. Пам'ятайте, що всі методи інтерфейсу є автоматично публічними.
Реалізація віддаленого інтерфейсу
Сервер повинен містити клас, який успадковується UnicastRemoteObject і реалізує віддалений інтерфейс. Цей клас може мати додаткові методи, але для клієнта, звичайно, будуть доступні тільки методи віддаленого інтерфейсу, так як клієнт отримуватиме лише посилання на інтерфейс, а не на клас, що реалізує його.
Ви повинні явно визначити конструктор для віддаленого об'єкта, навіть якщо ви визначаєте лише конструктор за замовчуванням, котрий викликає конструктор базового класу. Ви повинні написати його, оскільки він повинен викидати RemoteException.
Ось реалізація віддаленого інтерфейсу PerfectTimeI:
//: c15:rmi:PerfectTime.java // Реалізація віддаленого // об'єкта PerfectTime. // package c15.rmi;
Public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI // Реалізація інтерфейсу: public long getPerfectTime() throws RemoteException return System.currentTimeMillis() ; >
// Необхідно реалізовувати конструктор, //RemoteException, що викидає: public PerfectTime () throws RemoteException // super(); // Викликається автоматично >
// Реєстрація для обслуговування RMI. Викидає // винятки на консоль. public static void main ( String [] args ) throws Exception System.setSecurityManager ( new RMISecurityManager ()) ; PerfectTime pt = new PerfectTime(); Naming.bind ("//peppy:2005/PerfectTime", pt); System.out.println ("Ready to do time"); > > ///:
Тут main( ) обробляє всі деталі установки сервера. Коли ви обслуговуєте RMI об'єкти, в деякому місці вашої програми ви повинні:
- Створити та встановити менеджер безпеки, який підтримує RMI. Для RMI доступний тільки один такий менеджер, як частина пакета Java, він називається RMISecurityManager.
- Створити один або кілька екземплярів віддаленого об'єкта. Тут ви можете бачити створення об'єкта PerfectTime.
- Зареєструвати щонайменше один віддалений об'єкт у RMI репозиторії віддалених об'єктів з метою завантаження. Один віддалений об'єкт може мати методи, які роблять посилання інші віддалені об'єкти. Це дозволить вам зробити так, щоб клієнт звертався до репозиторію лише один раз, щоб отримати перший віддалений об'єкт.
Налаштування репозиторію
У цьому прикладі ви бачите статичний метод Naming.bind( ). Однак цей виклик вимагає, щоб репозиторій був запущений як окремий процес на комп'ютері. Ім'я сервера репозиторію rmiregistry, у 32-х Windows ви використовуєте команду:
для запуску його у фоновому режимі. Для системи Unix використовується команда:
- localhost не працює з RMI. Тобто, експериментуючи з RMI на одній машині, ви повинні надати ім'я машини.Щоб знайти ім'я вашої машини в 32-бітному середовищі Windows, запустіть контрольну панель і виберіть "Network". Виберіть закладку "Identification", і ви побачите ім'я комп'ютера. У багатьох випадках я називаю мій комп'ютер "Peppy". Зауважте, що регістр літер ігнорується.
- RMI не буде працювати до тих пір, поки ваш комп'ютер не матиме активного TCP/IP з'єднання, навіть якщо всі компоненти просто спілкуються один з одним на вашій локальній машині. Це означає, що ви повинні з'єднати ваш комп'ютер з вашим провайдером Internet, перш ніж пробувати запускати програму або ви отримаєте незрозуміле повідомлення про виключення.
Враховуючи все вищесказане, команда bind( ) набуває вигляду:
Якщо ви використовуєте порт за промовчанням 1099, вам не потрібно вказувати порт, так що ви можете написати:
Ім'я служби довільно, в даному випадку це PerfectTime, що збігається з ім'ям класу, але ви можете використовувати будь-яку назву, яку хочете. Важливо те, що воно має бути унікальним у відомому клієнту репозиторії, до якого він звертається для віддаленого об'єкта. Якщо ім'я вже є у репозиторії, ви отримаєте AlreadyBoundException. Щоб запобігти цьому, ви завжди можете використовувати rebind( ) разом bind( ), оскільки rebind( ) або додає новий запис, або замінює вже існуючий.
Навіть після того, як main( ) завершить роботу, ваш об'єкт буде створений і зареєстрований, тому він залишиться жити в репозиторії, чекаючи приходу клієнтів і запитів від них. Доки працює rmiregistry і ви не викликали метод Naming.unbind( ) з вашим ім'ям, об'єкт буде існувати. З цієї причини, коли ви розробляєте код, вам необхідно вивантажувати rmiregistry і перезапускати його, коли ви скомпілювали нову версію вашого віддаленогооб'єкт.
Вам не потрібно примусово запускати rmiregistry як зовнішній процес. Якщо ви знаєте, що ваша програма є єдиним, який використовує репозиторій, ви можете запускати його всередині програми за допомогою рядка:
Як і раніше, 2005 – це номер порту, який ми будемо використовувати у цьому прикладі. Це еквівалентно запуску rmiregistry 2005 з командного рядка, але такий запуск часто буде послідовнішим, коли ви розробляєте RMI код, тому що при цьому пропускаються додаткові кроки запуску та зупинки репозиторію. Як тільки ви виконаєте цей код, ви можете викликати bind( ), використовуючи Naming, як і раніше.
Створення заглушок та скелетів
Якщо ви скомпілюєте та запустите PerfectTime.java, програма не працюватиме, навіть якщо у вас буде правильно запущений репозиторій. Це тому, що робоче середовище для RMI ще створено. Ви повинні спочатку створити заглушки та скелети, які забезпечать операцію мережного з'єднання та дозволять вам вважати, що віддалений об'єкт – це просто інший локальний об'єкт на вашій машині.
Те, що відбувається за сценою, досить складно. Будь-які об'єкти, які ви передаєте при виклику або отримуєте від віддаленого об'єкта повинні реалізовувати Serializable (якщо ви хочете передавати віддалену посилання замість самого об'єкта, об'єктні аргументи можуть реалізовувати Remote), так що ви можете уявити, що заглушки та скелети автоматично виконують серіалізацію та десеріалізацію, начебто вони "керують" усіма аргументами через мережу і повертають результат. На щастя, вам не потрібно знати нічого цього, але ви повинні створити заглушки та скелети. Це простий процес: ви викликаєте інструмент rmic для вашого компілюваного коду і створюєте необхідні файли. Таким чином потрібнопросто додати ще один крок у процес компіляції.
Однак, інструмент rmic вибагливий до пакетів і classpaths. PerfectTime.java розташований у пакеті c15.rmi, і якщо ви викликаєте rmic в тому ж директорії, в якому розташований PerfectTime.class, rmic не знайде файл, оскільки він шукає змінної classpath. Таким чином, ви повинні вказати розташування в змінній classpath, наприклад так:
Вам не потрібно перебувати в директорії PerfectTime.class, коли ви запускаєте цю команду, а результат буде поміщено в поточний каталог.
Коли запуск rmic завершиться успішно, ви отримаєте два нових класи в директорії:
відповідно це заглушка та скелет. Тепер ви готові отримати сервер та клієнт для спілкування між собою.
Використання віддаленого об'єкта
Головна мета RMI полягає у спрощенні використання віддалених об'єктів. Є тільки одна додаткова річ, яку ви повинні виконати у клієнтській програмі, це знайти та отримати віддалений інтерфейс із сервера. Решта - це звичайне Java програмування: посилка повідомлень об'єкту. Ось програма, яка використовує PerfectTime:
//: c15:rmi:DisplayPerfectTime.java // Використання віддаленого об'єкта PerfectTime. // package c15.rmi;
public class DisplayPerfectTime public static void main ( String [] args ) throws Exception System.setSecurityManager ( new RMISecurityManager ()) ; PerfectTimeI t = (PerfectTimeI) Naming .lookup ("//peppy:2005/PerfectTime"); for (int i = 0; i
Рядок ідентифікатора такий самий, як і використовується для реєстрації об'єкта за допомогою Naming, а перша частина представляє URL і номер порту. Так як ви використовуєте URL, ви можете також вказати машину в Інтернеті.
Те, що приходить відNaming.lookup( ) має бути приведений до віддаленого інтерфейсу, а не до класу. Якщо замість цього ви використовуватимете клас, ви отримаєте виняток.
Ви можете бачити виклик методу