Раздел 3. Программирование прикладных задач в глобальных

advertisement
-1Прикладное программирование в ТС
Лекция 3-14
Лекция 3-14
Раздел 3. Программирование прикладных задач в телекоммуникационных системах на
языке Java
Тема 3.1. Работа по протоколам Internet в Java
3.1.1. Компоненты сетевых средств Java
3.1.2. Работа с адресами Internet
3.1.3. Обработка запросов
3.1.4. Реализация протоколов передачи данных и обработки данных
3.1.5. Работа по протоколу TCP
3.1.6. Работа по протоколу UDP
Тема 3.2. Сетевые службы Java
3.2.1. Сериализация объектов
3.2.1.1. Класс ObjectOutputStream
3.2.1.2. Класс ObjectInputStream
3.2.2. Служба имен и каталогов (JNDI)
3.2.2.1. Архитектура JNDI
3.2.2.2. Поддержка имен в JNDI
3.2.2.3. Работа с контекстом в JNDI
3.2.2.4. Контекст каталога
3.2.2.5. Поиск в контексте каталога
3.2.2.6. Ссылки на объекты
3.2.2.7. Обработка событий в JNDI
РАЗДЕЛ 3. ПРОГРАММИРОВАНИЕ ПРИКЛАДНЫХ ЗАДАЧ В
ГЛОБАЛЬНЫХ СЕТЯХ НА ЯЗЫКЕ JAVA
Тема 3.1. Сетевые средства Java
3.1.1. Компоненты сетевых средств Java
Язык Java обеспечивает мощные средства создания сетевых приложений для
серверов и клиентов при помощи сравнительно небольших программ. Сетевые средства
Java содержат следующие компоненты:
 средства, обеспечивающие работу в сети Internet (пакет java.net в Java 2
Standard Edition);
 средства доступа к базам данных (пакеты java.sql и javax.sql в Java 2
Standard Edition и пакет javax.sql в Java 2 Enterprise Edition);
 поиск распределенных объектов в сети JNDI (Java Naming and Directory Interface)
(группа пакетов javax.naming в Java 2 Standard Edition);
 механизм распределенных объектов RMI (группа пакетов java.rmi в Java 2
Standard Edition);
 система обмена сообщениями между частями распределенного приложения JMS
(Java Message Service) (пакет javax.jms в Java 2 Enterprise Edition);
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-2Прикладное программирование в ТС
Лекция 3-14
 средства обработки данных на Web-сервере (группа пакетов javax.servlet и
javax.ejb в Java 2 Enterprise Edition).
3.1.2. Работа с адресами Internet
Для работы с адресами хостов сети Internet Java использует класс InetAddress.
Как известно, хосты в Internet идентифицируются одним из двух способов: по адресу или
по имени.
Адрес – это четырехбайтовое число, обычно имеющее форму a.b.c.d, например,
192.100.81.100. Когда компьютеры обмениваются данными, сетевой протокол использует
этот числовой адрес, чтобы определить, куда посылать информацию. Имена хостов
являются символьными, например, netcom.com.
Для установки соответствия имен и адресов используется служба DNS (Domain
Name Service — система доменных имен).
Метод
public static InetAddress
getByAddress(String host, byte[] addr)
throws UnknownHostException
формирует объект InetAddress без обращения к службе DNS.
Класс InetAddress выполняет всю рутинную работу по поиску имен. Методы
public static synchronized InetAddress
getByName(String host) throws UnknownHostException
public static InetAddress getByAddress(byte[] addr)
throws UnknownHostException
используют в качестве параметра соответственно имя и адрес хоста (в виде массива
байтов, размерностью 4) и возвращают экземпляр InetAddress, например
InetAddress microsoftAddress = getByName("microsoft.com");
При определении хоста используется служба DNS.
Хост может иметь несколько адресов. Например, компьютер входит в локальную
сеть организации и имеет выход по протоколу РРР в Internet. Тогда у него будет два
адреса: адрес РРР и адрес в локальной сети. Можно получить все адреса конкретного
хоста в виде массива объектов InetAddress, вызвав метод
public static synchronized InetAddress[]
getAllByName(String host)throws UnknownHostException.
Метод
public static InetAddress getLocalHost()
throws UnknownHostException
возвращает объект InetAddress только для локального хоста.
После создания объекта класса InetAddress для локального или удаленного узла
можно использовать другие методы этого класса.
Класс InetAddress имеет два метода для получения адресов, которые он хранит.
Метод
public String getHostName()
возвращает имя хоста, a
public byte[] getAddress()
возвращает его числовой адрес в виде массива байтов, размерностью 4. Следующий
фрагмент программы распечатывает числовые адреса, пользуясь точками как
разделителями:
byte[] addr = someInetAddress.getAddress();
System.out.println((addr[0]&0xff)+"."+(addr[1]&0xff)+"."+
(addr[2]&0xff)+"."+(addr[3]&0xff));
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-3Прикладное программирование в ТС
Лекция 3-14
Значения адресов логически умножаются (функцией AND) на шестнадцатеричное
значение ff (десятичное 255) поскольку байтовые значения в Java являются 8разрядными числами со знаком. Это означает, что когда самый левый бит равен 1, число
считается отрицательным. В адресах Internet не используются отрицательные числа. При
логическом умножении адреса на число 255 значение не меняется, но трактуется как 32разрядное целое, у которого самый левый разряд равен 0, а восемь правых представляют
адрес.
Подклассы Inet4Address и Inet6Address класса InetAddress позволяют
работать с адресами хостов для протоколов IPv4 и IPv6.
3.1.3. Обработка запросов
Класс URL позволяет создать объект, содержащий всю необходимую информацию
для доступа к сетевому ресурсу. Объект URL позволяет также получить данные с
удаленного компьютера.
Класс URL имеет шесть конструкторов:
public URL(String spec) throws MalformedURLException
public URL(String protocol, String host, String file)
throws MalformedURLException
public URL(String protocol, String host, int port,
String file) throws MalformedURLException
public URL(String protocol, String host, int port,
String file, URLStreamHandler handler)
throws MalformedURLException
public URL(URL context, String spec)
throws MalformedURLException
public URL(URL context, String spec,
URLStreamHandler handler)
throws MalformedURLException.
Параметры в этих конструкторах имеют следующий смысл: spec – строка,
содержащая URL, protocol – используемый протокол, host – адрес компьютера, file
–путь к файлу; port – номер порта; handler – обработчик протокола соединения;
context – URL, используемый для создания нового URL (полный URL получается путем
сцепления строк context и spec). Все конструкторы могут возбуждать исключение
MalformedURLException, возникающее в тех случаях, когда-либо не опознан
протокол, либо URL имеет неправильный синтаксис.
Примеры конструкторов:
URL firstURLObject = new URL("http://www.yahoo.com/");
URL secondURLObject = new URL("http","www.yahoo.com","/");
URL thirdURLObject =
new URL("http", "www.Yahoo.com", 80, "/");
URL fourthURLObject =
new URL(firstURLObject,"text/suggest.html");
После создания объекта URL можно получить содержимое URL в объектной или
строковой форме можно получить с помощью методов Object getContent() или
String toExternalForm(). Значения отдельных частей URL (имени файла, пути к
файлу, имени хоста, номера порта, используемого протокола, запроса по методу GET,
имени и пароля пользователя, а также ссылки на фрагмент Web-страницы) можно
получить с помощью методов String getFile(), String getPath(), String
getHost(), String getPort(), String getProtocol(), String getQuery(),
String getUserInfo() и String getRef().
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-4Прикладное программирование в ТС
Лекция 3-14
Установка элементов URL выполняется с помощью методов:
protected void set(String protocol, String host,
int port, String file, String ref)
protected void set(String protocol, String host, int port,
String authority, String userInfo, String path,
String query, String ref)
Создав объект URL, можно получать данные из сети. Для этого можно выбрать
один из двух способов:

непосредственное чтение ресурса по объекту URL;

создание объекта URLConnection.
Непосредственное чтение ресурса требует меньше программного текста, но это
решение обеспечивает доступ к ресурсу только для чтения. Для такого доступа в классе
URL имеется метод
InputStream openStream(),
с помощью которого выполняется побайтовое чтение ресурса.
Следующий фрагмент программы выполняет чтение и вывод на экран файла
index.html с Web-сервера www.yahoo.com.
URL yahoo = new URL("http:// /index.html");
BufferedReader in = new BufferedReader(
new InputStreamReader(yahoo.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
При использовании второго метода подключения к ресурсу сначала с помощью
метода
public URLConnection openConnection() throws IOException
класса URL создается объект URLConnection, например, для предыдущего примера:
URLConnection yc = yahoo.openConnection();
Объект in в этом случае создается с помощью следующего оператора:
BufferedReader in = new BufferedReader(
new InputStreamReader(yc.getInputStream()));
с использованием метода
public InputStream getInputStream() throws IOException
класса URLConnection.
Кроме этого метода, класс URLConnection имеет множество методов обработки
полей заголовков и типов данных.
Метод
public String getHeaderField(String fieldName)
возвращает значение поля заголовка с именем fieldName. Если это поле отсутствует в
ресурсе, метод возвращает null.
Метод
public String getHeaderField(int n)
возвращает значение n-го поля в ресурсе. Если в нем нет такого количества полей
заголовков, метод возвращает null. Соответствующее поле можно получить при помощи
метода
public int getHeaderFieldKey(int n),
который возвращает имя n-го поля в ресурсе. Если в нем нет такого количества полей
заголовков, метод возвращает null.
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-5Прикладное программирование в ТС
Лекция 3-14
Для получения значений наиболее часто используемых полей заголовков в классе
URLConnection определены следующие методы:
public String getContentEncoding()
public int getContentLength()
public String getContentType()
public long getDate()
public long Expiration()
public long getLastModified()
возвращающие соответственно значения полей Content-encoding, Content-length и Contenttype в заголовке, а также время отправки, истечения срока и действия и последней
модификации (в виде количества миллисекунд с 1 января 1970 г.).
Содержимое принятого ресурса можно получить с помощью метода
public Object getContent() throws IOException.
Тип объекта (например, String для текста или Image для изображения) можно
определить с помощью метода getContentType() (например, text/html или
image/gif).
Иногда программа пытается получить доступ к URL, который требует
идентификации пользователя. Для такой идентификации необходимо заполнить поля в
диалоговом окне, автоматически раскрывающемся при открытии URL. Поскольку
программа на языке Java не всегда предполагает присутствие пользователя, можно
сообщить классу URLConnection, должен ли он разрешить взаимодействие с
пользователем.
Метод
public void setAllowUserInteraction(boolean allowInteraction),
когда получает true в качестве значения параметра, разрешает взаимодействие с
пользователем, а метод
public boolean getAllowUserInteraction()
возвращает true, если этот класс будет взаимодействовать с пользователем. Метод
public static void
setDefaultAllowUserInteraction(boolean default)
изменяет значение по умолчанию для флага разрешения взаимодействия с пользователем
у всех новых экземпляров класса URLConnection. Изменение значения по умолчанию
не оказывает влияния на уже созданные экземпляры.
Соединение URLConnection позволяет не только принимать, но и передавать
данные. Можно сообщить соединению URLConnection, должен ли он разрешать ввод
или вывод при помощи методов
public void setDoInput(Boolean doInput)
и
public void setDoOutput(Boolean doOutput).
Можно установить в true любое из этих значений или сразу оба. Значения по
умолчанию: true для ввода и false для вывода.
Флаги doInput и doOutput можно опросить методами
public boolean getDoInput()
public boolean getDoOutput().
Если передача выполняется по протоколу HTTP, можно задать заголовки запроса с
помощью метода
public void setRequestProperty(String key, String value)
класса URLConnection, например, для соединения yc:
yc.setRequestProperty("content-type", "text/html");
Непосредственно передача данных выполняется с помощью метода
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-6Прикладное программирование в ТС
Лекция 3-14
public OutputStream getOutputStream() throws IOException
Следующий фрагмент программы передает строку s для соединения
connection:
String s = new String("Greetings");
URL url = new URL("http://java.sun.com/cgi-bin/backwards");
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
PrintWriter out =
new PrintWriter(connection.getOutputStream());
out.println(s);
out.close();
Подкласс HttpURLConnection класса URLConnection поддерживает средства
соединения, специфичные для протокола HTTP.
Конструктор класса
protected HttpURLConnection(URL u)
создает объект для заданного u.
Основные методы этого класса:
public String getRequestMethod()
public void setRequestMethod(String method)
throws ProtocolException
public int getResponseCode() throws IOException
public String getResponseMessage() throws IOException
позволяют соответственно получить метод запроса (например, "GET" или "PUT") при
приеме, установить метод запроса при передаче, получить код ответа Web-сервера
(например, 200) или сообщение Web-сервера (например, "HTTP/1.0 200 OK").
Для преобразования данных формы при приеме и передаче используются классы
URLEncoder и URLDecoder.
Класс URLEncoder содержит статический метод
public static String encode(String s, String enc),
который преобразует строку s с кодировкой enc (например, "cp1251" или "cp866") в
формат x-www-form-urlencoded, используемый для передачи данных формы на
сервер. При этом
буквы, цифры и знак подчеркивания не меняется. Пробелы
преобразуются в символы "+", а все остальные символы переводятся в
шестнадцатеричную форму и записываются в виде %xx, где xx — шестнадцатеричное
представление символа. Метод
public static String decode(String s, String enc)
класса URLDecoder осуществляет обратное преобразование.
3.1.4. Реализация протоколов передачи данных и обработки
данных
Классы URL и URLConnection удобны для простых программ, которые
подключаются к Web-серверам для обмена данными. Для других приложений
необходимо реализовать собственные обработчики протоколов и принятых данных.
Абстрактный класс URLStreamHandler отвечает за синтаксический анализ URL
и создание объекта URLConnection для доступа к этому URL. Когда первый раз
открывается соединение с URL, использующим данный протокол, просматривается набор
пакетов в поисках обработчика протокола данного URL. Этот обработчик должен носить
имя протокол.Handler. Например, если открывается URL HTTP, класс URL ищет класс
с именем имя-пакета.http.Handler. Можно указать другой маршрут поиска,
установив системный параметр java.protocol.handler.pkgs. Этот параметр
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-7Прикладное программирование в ТС
Лекция 3-14
должен содержать названия альтернативных пакетов для поиска, отделенных друг от
друга вертикальной чертой, например:
mypackages.urls|thirdparty.lib.
По умолчанию поиск ограничивается пакетом sun.net.www.protocol.
Как минимум, любой класс, производный от URLStreamHandler, должен
реализовывать метод
protected abstract URLConnection openConnection(URL u).
Этот метод возвращает экземпляр URLConnection, который настроен на
соответствующий протокол. Например, если программист создал свой собственный
URLStreamHandler для протокола FTP, этот метод возвращает URLConnection,
который настроен на протокол FTP.
Когда документ вызывается с использованием протокола HTTP, Web-сервер
посылает серию заголовков до посылки содержательных данных. Один из пунктов
заголовка сообщает, какие именно данные посылаются. Эти данные иногда называются
содержимым, а их тип (который называется тип данных MIME) указывается в заголовке
"Тип данных". Web-браузер использует этот тип, чтобы решить, как поступить с
приходящими данными.
Если программист хочет написать собственный обработчик для данных некоторого
MIME-типа, он может создать подкласс абстрактного класса ContentHandler, который
будет выполнять синтаксический анализ данных и возвращать объект, представляющий
содержимое.
Процесс установки собственного обработчика данных почти идентичен установке
собственного обработчика URLStreamHandler. Ему следует дать имя в формате
имя-пакета.major.minor.
Имена major и minor происходят из заголовка типа данных MIME, имеющего форму
Content-type: major/minor
Одной из наиболее распространенных комбинаций major/minor является
text/plain. При определении собственного обработчика text/plain ему можно дать
имя MyPackage.text.plain. По умолчанию класс URLConnection ищет
обработчики данных только в пакете с именем sun.net.www.content. Можно задать
дополнительные
имена
пакетов,
указав
в
системном
параметре
java.content.handler.pkgs список имен, разделенных вертикальными чертами.
Единственным методом, который необходимо реализовать в собственном
обработчике ContentHandler, является метод
public abstract Object getContent(URLConnection urlc)
throws IOException.
При реализации этого метода программист должен определить, как анализировать
данные и какой объект возвращать.
3.1.5. Работа по протоколу TCP
Используя принцип сокетов, появившийся сначала в UNIX, разработчик может
создавать свои прикладные протоколы, отличные от HTTP.
Сокет (socket) – это описатель сетевого соединения с другим приложением. Сокет
TCP использует протокол TCP, наследуя все свойства этого протокола. Для создания
сокета TCP необходимы следующие данные:
 IP-адрес локальной машины;
 Номер порта TCP, который использует приложение на локальной машине;
 IP-адрес машины, с которой устанавливается связь;
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-8Прикладное программирование в ТС
Лекция 3-14
 Номер порта TCP, на который отзывается приложение, ожидающее
установления связи.
Сокеты часто используются в приложениях клиент-сервер: централизованная
служба ждет дистанционных обращений от различных машин с запросами конкретных
ресурсов и обрабатывает запросы по мере поступления. Стандартным прикладным
протоколам назначены порты по умолчанию с номерами меньше 1024 (например, порт 80
для HTTP).
Приложение-клиент также должно привязаться к порту, чтобы начать обмен через
сокет. Поскольку связь инициируется со стороны клиента, конкретный номер порта не
имеет большого значения и может быть выбран непосредственно при необходимости
установить соединение. Как правило, приложениям-клиентам даются динамически
назначаемые порты с номерами, большими 1024.
Поскольку два приложения могут одновременно привязаться к одному порту на
одной и той же машине, сокет служит уникальным идентификатором сетевого
соединения, благодаря тому, что клиенты будут работать на разных машинах или разных
портах, и таким образом обеспечивается уникальность соединения.
В Java имеется два стандартных класса, которые позволяют создавать сетевые
приложения на основе сокетов с использованием протокола TCP: Socket и
ServerSocket.
Класс Socket применяется для организации обычного двустороннего обмена
данными и имеет следующие основные конструкторы:
public Socket()
public Socket(String host, int port)
throws UnknownHostException, IOException
public Socket(InetAddress address, int port)
throws IOException
public Socket(InetAddress address, int port,
InetAddress localAddr, int localPort) throws IOException
public Socket(String host, int port, InetAddress localAddr,
int localPort) throws IOException
Первый конструктор создает сокет без соединения. Остальные конструкторы
создают сокет со следующими параметрами: host – имя хоста, port – номер порта,
address – IP-адрес хоста, localAddr – адрес локального хоста, localPort – номер
локального порта.
Класс Socket не содержит явных методов посылки и приема данных. Вместо
этого он предоставляет методы, возвращающие входные и выходные потоки, позволяя
программисту пользоваться возможностями классов ввода-вывода из пакета java.io.
Метод
public InputStream getInputStream() throws IOException
возвращает объект InputStream для порта, а метод
public OutputStream getOutputStream() throws IOException
возвращает объект OutputStream.
Методы
public InetAddress getInetAddress()
public int getPort()
возвращают адрес хоста и номер порта другого участника соединения, а методы
public int getLocalPort()
public InetAddress getLocalAddress()
позволяют получить номер и адрес локального порта.
Закрытие соединения выполняется с помощью метода
public synchronized void close() throws IOException.
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
-9Прикладное программирование в ТС
Лекция 3-14
Ниже приведен фрагмент программы, открывающий сокет и принимающий из него
данные, а также посылающий введенные с клавиатуры данные в сокет:
Socket echoSocket;
PrintWriter out;
BufferedReader in;
try {
echoSocket = new Socket("taranis", 7);
out =
new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
echoSocket.getInputStream()));
}
catch (UnknownHostException e) {
System.err.println("Don't know about host: taranis.");
System.exit(1);
}
catch (IOException e) {
System.err.println("I/O Error");
System.exit(1);
}
BufferedReader stdIn = new BufferedReader(
new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("echo: " + in.readLine());
}
out.close();
in.close();
stdIn.close();
echoSocket.close();
Чтение данных из порта не вполне соответствует чтению из файла, хотя оба
являются входными потоками. Когда программа читает файл, все данные уже в нем
находятся. При соединении между портами возможна попытка прочитать данные до того,
как другая сторона пошлет их. Методы чтения из входных потоков блокируются, то есть
ждут данных, если их нет, поэтому программисту необходимо следить за тем, чтобы
программа не закончила работу во время ожидания. Типичное решение этой проблемы –
запустить вычислительный поток для чтения данных из порта и уведомить программу о
пришедших данных.
Класс ServerSocket прослушивает поступающие запросы на соединение и
создает объект Socket для каждого нового соединения.
Конструктор
public ServerSocket()
создает серверный сокет без соединения
Для создания серверного порта в конструкторе следует указать номер порта,
который надо прослушивать:
public ServerSocket (int portNumber) throws IOException
Если безразлично, какой порт использовать, можно переложить ответственность за
назначение портов на систему, указав номер порта 0.
Многие реализации портов используют понятие журнал невыполненных
запросов. Когда с сервером одновременно связываются много клиентов, то соединения,
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 10 Прикладное программирование в ТС
Лекция 3-14
которые еще следует принять, записываются в такой журнал. Когда сервер исчерпает
лимит, установленный этим журналом, он отказывает новым клиентам. Для создания
ServerSocket с определенным лимитом журнала невыполненных запросов, следует
передать конструктору номер порта и лимит журнала:
public ServerSocket(int portNumber, int backlogLimit)
throws IOException
И, наконец, с помощью конструктора
public
ServerSocket(int
portNumber,
int
backlogLimit,
InetAddress bindAddr) throws IOException
можно, помимо номера порта и лимита журнала, задать IP-адрес, с которым будет
связываться сервер.
Следующий фрагмент программы создает объект ServerSocket:
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(4444);
}
catch (IOException e) {
System.out.println("Could not listen on port: 4444");
System.exit(-1);
}
После того как серверный порт создан, метод
public Socket accept() throws IOException
будет возвращать объект Socket для каждого нового соединения, например:
Socket clientSocket;
try {
clientSocket = serverSocket.accept();
}
catch (IOException e) {
System.out.println("Accept failed: 4444");
System.exit(-1);
}
Если нет поступивших запросов, метод accept() ждет, пока не появится запрос
на соединение. Если программист не хочет, чтобы при этом простаивала вся программа,
метод accept() должен находиться в отдельном вычислительном потоке.
После того, как сервер установит соединение с клиентом, открываются выходной и
входной потоки с помощью операторов
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true);
BufferedReader in =
new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
а затем выполняется чтение для входного потока in с помощью метода readLine() и
запись для выходного потока out с помощью метода println().
Когда отпадает необходимость в приеме запросов, следует закрыть объект
ServerSocket при помощи метода
public void close() throws IOException.
Этот метод не влияет на существующие соединения между портами,
установленные данным объектом ServerSocket. Если нужно их закрыть, следует
сделать это явно для каждого соединения.
Когда нужно выяснить адрес и номер серверного порта, можно воспользоваться
методами
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 11 Прикладное программирование в ТС
Лекция 3-14
public InetAddress getInetAddress()
public int getLocalPort().
Метод getLocalPort() особенно полезен, когда номер порта назначен
системой. В этом случае необходимо сообщить клиентам, какой номер использовать.
3.1.6. Работа по протоколу UDP
Протокол UDP является ненадежным, основанным на сообщениях протоколом без
установления логического соединения. В этом протоколе диалог должен быть разделен на
небольшие сообщения, которые умещаются в небольшой пакет определенного размера.
Такие сообщения, которыми обмениваются сетевые программы, называются
дейтаграммами (datagrams). Дейтаграмма содержит массив байтов. Принимающая
программа может извлечь этот массив и декодировать информацию, а затем, возможно,
послать ответную дейтаграмму.
Как и для протокола TCP, при программировании обмена с помощью протокола
UDP используется концепция сокета, но сокеты UDP сильно отличаются от сокетов TCP.
Если продолжить почтовую аналогию, то сокет UDP соответствует почтовому ящику.
Если при организации связи с использованием протокола TCP между приложениями
устанавливается канал «точка-точка», то при связи по протоколу UDP приложения
принимают и отправляют независимые пакеты данных. В отличие от протокола TCP,
протокол UDP не гарантирует надежную доставку дейтаграмм получателю, поэтому
надежность доставки и правильный порядок сборки данных из дейтаграмм должна
обеспечивать прикладная программа.
Программирование с использованием UDP требует решить следующие задачи:
 Создание правильно адресованной дейтаграммы;
 Создание сокета для рассылки и получения дейтаграмм данным приложением;
 Помещение дейтаграмм в сокет для передачи по назначению;
 Ожидание получения дейтаграмм из сокета;
 Декодирование дейтаграмм для выделения самого сообщения, адреса
отправителя и другой служебной информации.
Механизм обмена дейтаграммами по протоколу UDP реализован в языке Java с
использованием классов DatagramPacket и DatagramSocket.
Класс DatagramPacket используется для создания дейтаграмм. При получении
дейтаграммы по протоколу UDP класс DatagramPacket используется также для чтения
данных, адреса отправителя и служебной информации.
Когда необходимо послать дейтаграмму другой программе, создается объект
DatagramPacket, который содержит адрес хоста и номер порта, принимающего
дейтаграммы. При создании дейтаграммы необходимо указать массив для хранения
данных и их длину (в байтах). Объект DatagramPacket используется двумя способами:
 как контейнер данных, которые нужно переслать через порт дейтаграммы (в
этом случае массив, использованный для создания пакета, должен содержать
пересылаемые данные, а длина должна в точности равняться количеству посылаемых
байтов);
 как почтовый ящик для поступающих дейтаграмм (в этом случае массив
должен быть достаточно большим, чтобы в него поместились получаемые данные, а длина
должна равняться максимальному количеству байтов, которые можно принять).
Создать отсылаемый пакет DatagramPacket можно с помощью одного из
следующих конструкторов:
public DatagramPacket(byte[] buf, int length,
InetAddress address, int port)
public DatagramPacket(byte[] buf, int offset, int length,
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 12 Прикладное программирование в ТС
Лекция 3-14
InetAddress address, int port)
В этих конструкторах параметр buf – это массив байт, содержащий отправляемое
сообщение; length – количество байт, которое должно быть помещено в пакет;
address – IP-адрес получателя; port указывает номер порта, на который посылается
дейтаграмма.
Во втором конструкторе данные будут передаваться не с начала буферной области,
а начиная со смещения offset, что удобно при фрагментировании дейтаграмм.
Когда DatagramPacket создается для приема данных можно воспользоваться
одним из следующих конструкторов:
public DatagramPacket(byte[] buf, int length)
public DatagramPacket(byte[] buf,
int offset, int length)
В этих конструкторах параметр buf определяет массив байт, в который будет
помещаться принятая дейтаграмма, а параметр length – количество считываемых байт.
Второй конструктор задает смещение offset, начиная с которого помещается
дейтаграмма, и удобен для приема фрагментированных дейтаграмм.
Класс DatagramPacket также предоставляет методы для получения
компонентов дейтаграммы:
 public InetAddress getAddress() – для
поступающих данных
возвращает адрес, с которого была послана дейтаграмма, для отправляемых данных –
адрес, по которому дейтаграмма посылается;
 public int getPort() – для поступающей дейтаграммы это номер порта,
с которого она была послана, для отправляемой — номер порта, куда она посылается;
 public byte[] getData() – данные, содержащиеся в дейтаграмме в виде
массива байт;
 public int getLength() – длина дейтаграммы;
 public int getOffset() – смещение дейтаграммы.
Кроме того, методы
public void setAddress(InetAddress iaddr)
public void setPort(int)
public void setLength(int length)
public void setData(byte[] buf)
public void setData(byte[] buf, int offset, int length)
позволяют установить соответственно IP-адрес и порт и длину дейтаграммы, а также
поместить данные в дейтаграмму (два метода).
Отправка и получение дейтаграмм осуществляется при помощи класса
DatagramSocket, который создает сокет UDP.
При использовании конструктора
public DatagramSocket() throws SocketException
номер порта будет назначен автоматически. Программа должна указывать номер порта
лишь в тех случаях, когда другие программы посылают ей дейтаграммы по собственной
инициативе. Клиенты, которые посылают серверу дейтаграммы и получают ответы, могут
иметь номера портов, назначенные системой, а сервер сам увидит их обратные адреса на
дейтаграммах.
В конструкторе
public DatagramSocket(int port) throws SocketException
задается конкретный порт для отправки и приема дейтаграмм (этот конструктор
используется для сервера).
Третий конструктор
public DatagramSocket(int port, InetAddress laddr)
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 13 Прикладное программирование в ТС
Лекция 3-14
throws SocketException
позволяет, кроме порта, задать IP-адрес (этот конструктор обычно используется на
сервере в том случае, если для хоста зарезервировано несколько IP-адресов).
Метод
public void disconnect()
отключает сокет, а метод
public void connect(InetAddress address, int port)
связывает сокет с удаленным адресом для этого сокета.
Метод
public void send(DatagramPacket p) throws IOException
посылает дейтаграмму по месту назначения (которое хранится в объекте
DatagramPacket), а метод
public synchronized void receive(DatagramPacket packet)
throws IOException
читает дейтаграмму и записывает ее в объект DatagramPacket.
Когда необходимость приема или получения дейтаграммы отпадает, порт можно
закрыть при помощи метода
public synchronized void close().
Методы
public InetAddress getInetAddress()
public int getPort()
позволяет получить IP-адрес и номер порта, к которому подключен сокет. Если сокет не
подключен, первый метод возвращает null, а второй метод возвращает -1. Если
необходимо узнать IP-адрес и порт локального хоста, можно воспользоваться методами
public InetAddress getLocalAddress()
и
public int getLocalPort().
Проверить состояние подключения или закрытия сокета можно с помощью
методов
public boolean isConnected()
и
public boolean isClosed().
Ниже следует фрагмент программы, посылающий содержимое строки greetings
клиентскому хосту:
public static int serverPort = 666;
public static int clientPort = 999;
public static DatagramSocket ds;
public static byte buffer[] = new byte[1024];
ds = new DatagramSocket(serverPort);
…
String greetings = new String("Server greets you!");
byte [] byteArray = greetings.getBytes();
System.arraycopy(byteArray,0,buffer,0,byteArray.length);
ds.send(new DatagramPacket(buffer, byteArray.length,
InetAddress.getLocalHost(),clientPort));
Фрагмент программы, принимающий дайтаграмму от хоста-сервера и выводящий
ее содержимое на дисплей:
public static int clientPort = 999;
public static DatagramSocket ds;
public static byte buffer[] = new byte[1024];
ds = new DatagramSocket(clientPort);
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 14 Прикладное программирование в ТС
Лекция 3-14
…
DatagramPacket p =
new DatagramPacket(buffer, buffer.length);
ds.receive(p);
System.out.println(
new String(p.getData(), 0, p.getLength()));
Тема 3.2. Сетевые службы Java
3.2.1. Сериализация объектов
Сериализация (serialization) – это процесс записи состояния объекта в байтовый
поток. Этот процесс используется, когда необходимо сохранить состояние программы в
постоянной памяти, например, в файле. Сохраненное состояние можно восстановить,
используя процесс десериализации (deserialization).
Сериализация используется также для реализации механизма вызова удаленных
методов (RMI, Remote Method Invocation). Механизм RMI позволяет объекту Java на
одной машине вызывать метод другого объекта Java, находящегося на удаленной машине.
Удаленный объект можно указывать как аргумент удаленного метода. Посылающая
машина сериализирует объект и передает его. Принимающая машина десериализует
принятый объект.
Следует отметить, что если в объекте имеются ссылки на другие объекты, эти
объекты также сериализуются, а в процессе десериализации все эти объекты и их ссылки
восстанавливаются в исходную иерархию.
Средства сериализации могут работать только с объектами, которые реализуют
интерфейс Serializable. В этом интерфейсе не определено никаких конструкторов,
свойств и методов и реализация интерфейса Serializable каким-либо классом просто
указывает на то, что данный класс может быть сериализован. При этом, если класс
сериализован (т. е. реализует интерфейс Serializable), то все его подклассы также
сериализованы.
Следует отметить, что переменные в классе, которые объявлены как transient
или static, не сохраняются средствами сериализации.
3.2.1.1. Класс ObjectOutputStream
Класс
ObjectOutputStream
позволяет записывать данные примитивных
типов и образы объектов в выводной поток (сериализовать их). Для этого класса
определены следующие конструкторы:
ObjectOutputStream()
ObjectOutputStream(OutputStream out)
Первый конструктор позволяет подклассам этого класса, которые создают свою
реализацию класса OutputStream, не выделять память для своих данных, просто
используемых в классе ObjectOutputStream, а второй конструктор создает объект
ObjectOutputStream, записывающий свои данные в указанный поток out.
Класс
ObjectOutputStream реализует унаследованные от класса
OutputStream методы write() для записи одного байта, байтового массива и части
байтового массива, а также метод flush(), сбрасывающий в выходной поток
содержимое буферов вывода, и метод close(), закрывающий выводной поток. Кроме
того, класс ObjectOutputStream, так же как и рассмотренный ранее класс
DataOutputStream, реализует интерфейс DataOutput, содержащий методы для
записи в выводной поток данных примитивного типа.
Основным же методом класса ObjectOutputStream является метод
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 15 Прикладное программирование в ТС
Лекция 3-14
public void writeObject(Object obj) throws IOException,
записывающий в выходной поток объект obj. С помощью этого метода можно
осуществлять запись во внешнюю память и в сеть (сериализацию) любых объектов Java
(поскольку все без исключения классы Java являются производными от класса Object).
Пример сериализации объектов типа String и Date:
try {
ObjectOutputStream outputStream =
new ObjectOutputStream(
new FileOutputStream("objects.dat");
outputStream.writeObject("This String is an object.");
outputStream.writeObject(new Date());
outputStream.flush();
outputStream.close();
}
catch (IOException e) {
System.err.println("Exception: " + e.getMessage());
}
Кроме метода writeObject(), в классе ObjectOutputStream определены и
другие методы, позволяющие управлять процессом сериализации объектов.
3.2.1.2. Класс ObjectInputStream
Класс ObjectInputStream читает объекты, предварительно записанные с
помощью методов класса ObjectOutputStream, из входного потока. Для класса
ObjectInputStream определены следующие конструкторы:
ObjectInputStream()
ObjectInputStream(InputStream in)
Первый конструктор позволяет подклассам этого класса, которые создают свою
реализацию класса InputStream, не выделять память для своих данных, просто
используемых в классе ObjectInputStream, а второй конструктор создает объект
ObjectOutputStream, читающий свои данные из указанного потока out.
Класс ObjectInputStream реализует унаследованные от класса InputStream
методы read(), позволяющие читать из входного потока один байт, байтовый массив
или часть байтового массива, а также метод available(), возвращает количество
байтов, которые доступны в буфере ввода, метод skipBytes(), пропускающий во
входном потоке заданное количество байт и метод close(), закрывающий входной
поток.
Класс ObjectInputStream, так же как и рассмотренный ранее класс
DataInputStream, реализует интерфейс DataInput, содержащий методы для чтения
их входного потока данных примитивного типа.
Чтение записанного с помощью метода
writeObject() объекта
(десериализацию объекта) выполняет метод
public Object readObject() throws IOException.
Пример десериализации объектов String и Date:
try {
String s;
Date d;
ObjectInputStream inputStream = new ObjectInputStream(
new FileInputStream("objects.dat");
s = (String) inputStream.readObject();
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 16 Прикладное программирование в ТС
Лекция 3-14
d = inputStream.readObject();
inputStream.close();
}
catch (IOException e) {
System.err.println("Exception: " + e.getMessage());
}
В классе есть также ObjectInputStream методы для управления процессом
десериализации объекта.
3.2.2. Служба имен и каталогов Java (JNDI)
3.2.2.1. Архитектура JNDI
Служба имен позволяет обращаться к объектам из программ, используя при этом
символьные имена объектов (объекты класса String), удобные для восприятия
пользователями. В основе службы имен лежат такие понятия, как дескрипторы, имена и
контекст.
Под объектом, как правило, подразумевают экземпляр класса или некоторую
область памяти, содержащую данные или функции. Дескрипторы этих объектов также
называют ссылками (или указателями).
Дескрипторы могут связываться с объектами, находящимися как на локальных, так
и на удаленных компьютерах. Дескрипторы чаще всего имеют двоичный формат и не
предназначены для просмотра пользователями. Во многих случаях дескриптор
представляет собой число, но иногда в его состав включают информацию о протоколе.
Конкретный вид дескриптора обычно зависит от платформы.
Значения дескрипторов, описывающих объекты, могут изменяться с течением
времени, однако, с точки зрения программы, каждый дескриптор указывает на
конкретный объект. В традиционных нераспределенных программах дескрипторы жестко
связаны с соответствующими объектами. Имена переменных и экземпляров объектов
имеют смысл только в пределах скомпилированного фрагмента кода и в конкретном
окружении.
Имя является логическим значением, позволяющее обращаться к объектам как в
распределенных, так и в нераспределенных системах. Имя часто представляется в форме,
удобной для восприятия пользователем, например, имя файла в файловой системе.
Структура имени определяется синтаксическими правилами, поддерживаемыми
конкретной службой имен. Правила для различных служб имен могут различаться.
Установление соответствия между именем и объектом называется связыванием имен (или
просто связыванием).
Имя имеет значение только в конкретном контексте. Так, если на компьютере есть
два файла с одинаковыми именами, например, index.html, то для того, чтобы
однозначно определить, какой файл нам нужен, мы должны указать не только имя файла,
но и путь к нему, т.е. в данном случае роль контекста будет выполнять полное имя файла.
В контексте могут содержаться имена других контекстов (subcontext). Так
образуется иерархическая структура вложенных друг в друга контекстов. Для полного
определения объекта надо указать его составное имя (compound name), в которое входит
последовательность имен вложенных контекстов, так же как в файловой системе
указывается полный путь к файлу.
Во главе иерархии контекстов стоит начальный контекст (initial context). В
системе JNDI нет единого корневого контекста и нет операции монтирования, подобно
монтированию файловых систем. Поэтому во всем пространстве имен может быть
несколько начальных контекстов.
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 17 Прикладное программирование в ТС
Лекция 3-14
Вся совокупность связанных между собой контекстов образует систему именования
(naming system), а все множество имен — пространство имен (namespace).
Таким образом, каждый контекст представляет собой набор соответствий между
именами и объектами, причем в роли объекта может выступать как реальный ресурс, так и
другой контекст. В каждом контексте поддерживается собственный стандарт именования
объектов и субконтекстов. Каждая служба имен следует определенному набору
соглашений об именовании, который используется при связывании имен с объектами и
определении дескрипторов объектов по именам.
Среди контекстов выделяются контексты каталогов (directory context). Контексты
каталогов снабжены средствами поиска объектов не только по имени, как обычный
контекст, но и по другим признакам, например, по их содержимому. В простейшем случае
создается множество атрибутов (attributes) объекта, по которым осуществляется поиск.
Например, атрибутами файла могут служить его длина, имя владельца, дата модификации,
права доступа к файлу. Контексты каталогов обычно создаются в иерархических системах
именования, откуда произошло их название.
Совокупность классов и их методов работы с контекстами каталогов называется
службой каталогов (directory service).
Технология Java предлагает свой интерфейс службы имен и каталогов – JNDI (Java
Naming and Directory Interface).
Архитектура JNDI представлена на рисунке:
Система JNDI предоставляет приложениям Java набор функций API; посредством
специального адаптера обращения к функциям API преобразуются в вызовы конкретного
сервера имен. На этапе инициализации JNDI передается несколько параметров,
определяющих, какому из серверов должны делегироваться вызовы. Адаптер,
используемый для отображения вызовов API в вызовы сервера имен или сервера
каталогов, называется интерфейсом поставщика услуг – SPI (Service Provider Interface).
SPI для некоторых наиболее популярных типов служб реализовала компания Sun. Пакеты
SPI также поставляют разработчики серверов, стремящиеся обеспечить работу своих
продуктов с JNDI, и, кроме того, адаптеры специально разрабатываются независимыми
производителями. Пакеты SPI для систем RMI и CORBA будут рассмотрены ниже
Библиотеки классов JNDI входят и в состав J2SE и в состав J2EE и распределены
по следующим пакетам:
 javax.naming – содержит основные классы и интерфейсы JNDI API,
используемые приложениями Java для доступа к различным службам имен;
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 18 Прикладное программирование в ТС
Лекция 3-14
 javax.naming.directory – содержит классы JNDI API,
которые
предназначены для выполнения дополнительных функций, связанных с поддержкой
каталогов и расширяющих возможности службы имен;
 javax.naming.event – содержит
классы и интерфейсы JNDI API,
предназначенные для обработки событий;
 javax.naming.ldap – содержит классы и интерфейсы JNDI API, которые
предназначены для поддержки расширенных средств, определяемых стандартом LDAPv3.
Данный пакет применяется при работе с простым протоколом доступа к каталогам – LDAP
(Lightweight Directory Access Protocol).
 javax.naming.spi – содержит классы и интерфейсы JNDI SPI. Данный
пакет предназначен для отображения вызовов JNDI API в обращения к конкретным
службам имен.
3.2.2.2. Поддержка имен в JNDI
Чтобы клиент JNDI мог воспользоваться услугами службы имен, он должен
получить дескриптор, описывающий исходный контекст JNDI. После этого можно
выполнять связывание имен с объектами и преобразовывать имена в ссылки на объекты,
пользуясь стандартными интерфейсами JNDI. Имена представляются с помощью объектов
String или специальных интерфейсов JNDI, инкапсулирующих имена.
Правила записи имен JNDI такие же, как в большинстве файловых систем. Простое
(atomic) имя системы JNDI – это строка символов. Составное имя состоит из простых
имен, связанных разделителем (separator). Разделителем чаще всего служит наклонная
черта, например, com/jdbc/myDB или точка, например, www.some.com. В составном
имени некоторые простые имена могут быть пустыми, например, имя /ABC//XYZ/ начинается с пустого имени, заканчивается пустым именем, кроме того, в его середине тоже
пустое имя. Простые имена могут быть компонентами составного имени.
Методы создания и удаления компонентов составного имени, выделения его частей
и сравнения имен описаны интерфейсом Name. Этот интерфейс предписывает
перенумеровывать компоненты составного имени, начиная с основного компонента,
получающего номер 0. Количество компонентов можно определить с помощью метода
public int size().
Компонент с номером n можно получить с помощью метода
public String get(int n).
Набор всех компонентов возвращает метод
public Enumeration getAll().
Часть имени, предшествующая компоненту с номером n, возвращается методом
public Name getPrefix(int n).
Часть имени от компонента с номером n включительно до последнего компонента
возвращается методом
public Name getSuffix(int n).
Удалить компонент с номером n из имени можно методом
public Object remove(int n)
InvalidNameException.
Добавить компонент перед указанной позицией n или в конец имени можно с
помощью методов
public Name add(int n, String comp)
throws InvalidNameException
public Name add(String comp)
throws InvalidNameException
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 19 Прикладное программирование в ТС
Лекция 3-14
а добавить компоненты имени перед указанной позицией n или в конец имени – с
помощью методов
public Name addAll(int posn, Name n)
throws InvalidNameException
public Name addAll(Name suffix)
throws InvalidNameException.
Методы добавления возвращают старое имя, то, что было до добавления
компонента.
Метод
public int compareTo(Object name)
сравнивает два имени одного и того же класса и возвращает отрицательное число, ноль
или положительное число, если имя меньше, равно или больше имени name
соответственно. Правила сравнения задаются реализацией метода, обычно это
лексикографическое сравнение и выполнение метода сводится к применению
одноименного метода класса String.
Метод
public boolean isEmpty()
позволяет определить, является ли имя пустым, а методы
public boolean startsWith(Name n)
public boolean endsWith(Name n)
позволяют определить, начинается или заканчивается имя заданным именем n.
Интерфейс Name реализован в двух классах: CompoundName
и
CompositeName.
Составные имена класса CompoundName отражают иерархическую структуру
контекстов, каталогов, доменов в конкретных системах именования. В частности,
иерархия может состоять только из одного уровня, образуя «плоское» (flat) пространство
имен. Синтаксические элементы имени определяются объектом класса Hashtable или
его подкласса Properties, состоящим из системных свойств со следующими именами.
 jndi.syntax.direction – порядок следования компонентов имени.
Значения "right_to_left", "left_to_right", "flat" (последнее означает, что в
имени есть только один компонент);
 jndi.syntax.separator – разделитель компонентов, обычно наклонная
черта "/" (это свойство необходимо определить, если значение свойства
jndi.syntax.direction не равно "flat").
 jndi.syntax.separator2 – дополнительный разделитель (если это свойство
не определено, то дополнительный разделитель совпадает с основным разделителем, т. е.
значением свойства jndi.syntax.separator);
 jndi.syntax.ignorecase – значение "true" предписывает игнорировать
регистр букв при сравнении имен, значение "false" или отсутствие этого свойства –
учитывать регистр;
 jndi.syntax.escape – строка символов экранирования, обычно обратная
наклонная черта "\";
 jndi.syntax.beginquote – начальный символ экранирования, обычно
кавычка;
 jndi.syntax.endquote – конечный символ экранирования, обычно кавычка
(если это свойство не определено, то конечный символ совпадает с начальным символом,
т. е. со значением свойства jndi.syntax.beginquote);
 jndi.syntax.beginquote2 – дополнительный начальный символ
экранирования, обычно апостроф.
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 20 Прикладное программирование в ТС
Лекция 3-14
 jndi.syntax.endquote2
–
дополнительный
конечный
символ
экранирования, обычно апостроф (если это свойство не определено, то конечный символ
совпадает
с
начальным
символом,
т.
е.
со
значением
свойства
jndi.syntax.beginquote2);
 jndi.syntax.trimblanks – значение "true" заставляет отбрасывать
начальные и конечные пробельные символы при сравнении компонентов имени (значение
"false" или отсутствие этого свойства – учитывать пробельные символы при сравнении);
 jndi.syntax.separator.java – строка разделителей атрибутов имени,
имеющих вид «ключ-значение» (например, запятая в строке name=Иванов, age=35);
 jndi.syntax.separator.typeval – строка, отделяющая ключ от значения
(знак равенства в предыдущем примере).
Объекты класса создаются с помощью конструктора
public CompoundName(String n, Properties syntax)
throws InvalidNameException
Перед созданием экземпляра класса CompoundName необходимо определить
описанные выше свойства, например, следующим образом:
private Properties compProps = new Properties();
compProps.put("jndi.syntax.direction", "left_to_right");
compProps.put("jndi.syntax.separator", "/");
compProps.put("jndi.syntax.ignorecase", "false");
compProps.put("jndi.syntax.escape", "\\");
compProps.put("jndi.syntax.beginquote", "\"");
compProps.put("jndi.syntax.beginquote2", "'");
compProps.put("jndi.syntax.trimblanks", "false");
Затем можно создать экземпляр составного имени, используя конструктор класса,
например:
CompoundName cname =
new CompoundName("jdbc/myDB", compProps);
В классе CompoundName реализованы все методы интерфейса Name и, кроме того,
добавлен метод
public String toString(),
позволяющий получить строковое значение имени.
Компоненты составного имени класса CompositeName могут принадлежать
разным системам именования, поэтому составное имя этого класса, как правило,
разбивается на компоненты, после чего каждый компонент обрабатывается по правилам
своей системы именования. Компоненты разделяются наклонной чертой и записываются
слева направо, точнее говоря, при разбиении составного имени на компоненты
применяются значения синтаксических элементов, которые определены в классе
CompositeName следующим образом:
static private Properties compositeSyntax =
new Properties();
static {
compositeSyntax.put("jndi.syntax.direction",
"left_to_right");
compositeSyntax.put("jndi.syntax.separator", "/");
compositeSyntax.put("jndi.syntax.ignorecase",
"false");
compositeSyntax.put("jndi.syntax.escape", "\\");
compositeSyntax.put("jndi.syntax.beginquote", "\"") ;
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 21 Прикладное программирование в ТС
Лекция 3-14
compositeSyntax.put("jndi.syntax.beginquote2", "'");
}
Кроме компонентов и символа "/" в составном имени могут встречаться
метасимволы: апострофы, кавычки и символ "\". Метасимволы предназначены для
отмены специального значения "/" – быть разделителем простых имен в составном
имени. Составное имя класса CompositeName можно создать либо с помощью
конструктора по умолчанию:
public CompositeName()
либо с помощью конструктора
public CompositeName(String n)
throws InvalidNameException
например:
CompositeName cname = new CompositeName("jdbc/myDB");
Класс CompositeName реализует все методы интерфейса Name, например:
String firstComp = cname.get(0);
for (int i = 0; i < cname.size(); i++)
System.out.println(cname.get(i));
или
Enumeration comps = cname.getAll();
while (comps.hasMoreElements())
System.out.println(comps.nextElement());
Система имен JNDI позволяет также задавать имя в виде строки URL, первая часть
которой, схема, указывает поставщика услуг конкретной системы именования:
 "file:" – адрес должен обрабатываться поставщиком услуг файловой системы;
 "rmi:" – адрес должен обрабатываться поставщиком услуг реестра RMI;
 "dns:" – адрес должен обрабатываться поставщиком услуг сервера DNS;
 "ldap:" – адрес должен обрабатываться поставщиком услуг сервера
LDAP;
 "ior:", "corbaname:", "corbaloc:", "iiopname:", "iiop:" –
адрес должен обрабатываться поставщиком услуг COS Name Server;
 "nds:" – адрес должен обрабатываться поставщиком услуг службы каталогов
NDS фирмы Novell;
 "nis:" – адрес должен обрабатываться поставщиком услуг сервера сетевой
информационной системы NIS или NIS+;
 "java:" – логическое пространство имен Java.
Адрес URL со схемой "java:" определяет логическое пространство имен
различных ресурсов, используемых компонентами приложения Java 2 – сервлетами,
страницами JSP, компонентами EJB. Это логическое пространство имен создается
контейнером, содержащим компоненты.
В пространстве имен "java:" создается корневой контекст "comp" для имен
компонентов и их ресурсов, а в нем два вложенных контекста: "env" (environment) и
"userTransaction". Первый контекст содержит имена ресурсов приложения,
например, "java:comp/env/EmpNames", а также вложенные контексты для имен
компонентов определенного типа:
 "ejb" – имена компонентов EJB;
 "jdbc" – имена источников данных Datasource интерфейса JDBC;
 "jms" – соединения системы сообщений JMS;
 "mail" – соединения электронной почты JavaMail;
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 22 Прикладное программирование в ТС
Лекция 3-14
 "url" – соединения по адресам URL.
Например, база данных myDB, являющаяся источником данных, получает имя
"java:comp/env/jdbc/myDB".
Контекст "UserTransaction" связан с классом UserTransaction.
Его используют компоненты, управляющие транзакцией.
Некоторые из остальных поставщиков услуг будут рассмотрены ниже.
3.2.2.3. Работа с контекстом в JNDI
Методы работы с контекстом описаны в интерфейсе Context. Все методы в этом
интерфейсе встречаются парами – у одного метода аргумент типа Name, у другого – типа
String. Основные методы осуществляют связывание имени name с объектом obj:
public void bind(Name name, Object obj)
throws NamingException
public void bind(String name, Object obj)
throws NamingException
public void rebind(Name name, Object obj)
throws NamingException
public void rebind(String name, Object obj)
throws NamingException
Методы bind() отличаются от методов rebind() только тем, что первые
выбрасывают исключение класса NameAlreadyBoundException, если в контексте
уже есть такое имя, а последние просто связывают с именем name другой объект. Поиск
объекта по имени name выполняет один из двух методов:
Object lookup(Name name)
Object lookup(String name).
Другие методы переименовывают объект:
public void rename(Name oldName, Name newName)
public void rename(String oldName, String newName),
или удаляют имя из контекста:
public void unbind(Name name)
public void unbind(String name).
Методы
public NamingEnumeration list(Name name)
public NamingEnumeration list(String name)
возвращают список всех имен, входящих в контекст name, в виде объекта, реализующего
интерфейс NamingEnumeration – расширение интерфейса Enumeration.
Методы
public NamingEnumeration listBindings(Name name)
public NamingEnumeration listBindings(String name)
возвращают все связывания контекста, т. е. имена вместе со связанными с ними
объектами.
Вложенный контекст с именем name создается с помощью одного из методов:
public Context createSubcontext(Name name)
throws NamingException
public Context createSubcontext(String name)
throws NamingException,
а удаляется с помощью методов
public void destroySubcontext(Name name)
throws NamingException
public void destroySubcontext(String name)
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 23 Прикладное программирование в ТС
Лекция 3-14
throws NamingException.
Получить полное имя контекста в его пространстве имен можно с помощью
метода:
public String getNameInNamespace()
throws NamingException.
Приложению JNDI необходимо иметь доступ к свойствам, определяющим
окружение, в котором функционируют службы именования и каталогов. Эти системные
свойства описываются в интерфейсе Context как строковые константы. Примерами этих
свойств являются:
 INITIAL_CONTEXT_FACTORY
–
"java.naming.factory.initial"
(значение этого свойства – полное имя класса, создающего начальный контекст, обычно
это поставщик услуг какой-нибудь системы именования);
 PROVIDER_URL
–
"java.naming.provider.url"
(строка
URL,
содержащая адрес сервера имен системы именования, связанной с JNDI, или начальный
контекст);
 LANGUAGE – "java.naming.language" (языки, используемые контекстом,
они записываются в соответствии с рекомендацией RFC 1766 через двоеточие, например,
"en-US:ru:de");
 SECURITY_PROTOCOL
–
"java.naming.security.protocol"
(защищенный протокол передачи данных, используемый JNDI, например, SSL);
 SECURITY_AUTHENTICATION
–
"java.naming.security.authentication" (уровень защищенности системы,
одно из значений "none", "simple", "strong");
 SECURITY_PRINCIPAL – "java.naming.security.principal" (имя
владельца объектов в защищенной системе);
 SECURITY_CREDENTIALS – "java.naming.security.credentials"
(пароль или иное средство проверки в защищенной системе).
Операции с окружением выполняются с помощью методов
public Object addToEnvironment(String propName,
Object propVal) throws NamingException
public Object removeFromEnvironment(String propName)
throws NamingException
public Hashtable getEnvironment()
throws NamingException
Первый и второй методы соответственно добавляют и удаляют свойство из
окружения, а третий метод позволяет получить свойства окружения в виде объекта
Hashtable.
По окончании работы с контекстом его рекомендуется закрыть с помощью метода
public void close() throws NamingException,
чтобы освободить занимаемые контекстом ресурсы.
Интерфейс Context и все его методы реализованы в классе InitialContext,
создающим начальный контекст, в котором потом можно создавать вложенные контексты.
Начальный контекст можно создать с помощью конструктора по умолчанию
public InitialContext() throws NamingException
например:
InitialContext ctx = new InitialContext();
Затем можно заполнить контекст связываниями, задавая имена объектам:
ctx.bind("ivanov", st[0]);
ctx.bind("petrov", st[l]);
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 24 Прикладное программирование в ТС
Лекция 3-14
При необходимости в контексте можно создать и заполнить вложенный контекст:
Context subctx = ctx.createSubcontext("emp");
subctx.bind("ivanov", stud[0]);
subctx.bind("petrov", stud[1]);
После определения пространства имен любой клиент системы JNDI может искать в
нем объект:
Student st = (Student)ctx.lookup("emp/ivanov");
Перед созданием начального контекста можно задать ему свойства, описанные в
интерфейсе Context. Для этого надо создать объект класса Hashtable или
Properties, записать в него свойства начального контекста и применить конструктор
public InitialContext(Hashtable environment)
throws NamingException.
Например, чтобы создать контекст JNDI, связанный с файловой системой
локальной машины, надо задать реализацию SPI для файловой системы:
Properties prop = new Properties ();
prop.put("INITIAL_CONTEXT_FACTORY",
"com.sun.jndi.fscontext.RefFSContextFactory");
Context ctx = new InitialContext(prop);
После этого можно обращаться к файлам по их именам с помощью
метода lookup():
File ae =
(File)ctx.lookup("С:\\autoexec.bat");
Просмотреть список файлов какого-либо каталога файловой системы можно с
помощью операторов:
NamingEnumeration list = ctx.list("adm\\log");
while (list.hasMore()) {
NameClassPair nc = (NameClassPair)list.next();
System.out.println(nc);
}
Объекты класса NameClassPair содержат пару "имя JNDI- имя класса Java".
Имена JNDI и класса можно получить или установить с помощью методов
public String getClassName()
public String getName()
public void setClassName(String name)
public void setName(String name)
этого класса.
У класса NameClassPair есть расширение – класс Bindings, хранящий пару
"имя JNDI – объект". Кроме имени JNDI и имени класса, которые можно получить
унаследованными от класса NameClassPair методами, этот класс позволяет получить
или установить еще и сам объект с помощью методов
public Object getObject()
public void setObject(Object obj),
например:
NamingEnumeration bindings = ctx.listBindings("adm\\log");
while (bindings.hasMore(}) {
Binding bd = (Binding)bindings.next();
System.out.println(bd.getName() +
": " + bd.getObject());
}
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 25 Прикладное программирование в ТС
Лекция 3-14
3.2.2.4. Контекст каталога
Контекст
каталога
описан
интерфейсом
DirContext
пакета
javax.naming.directory, расширяющим интерфейс Context. Поскольку контекст
каталога отличается от простого контекста наличием атрибутов, многие методы
интерфейса Context переопределены с учетом атрибутов контекста, описанных
интерфейсом Attributes. Например:
public void bind(Name name, Object obj, Attributes attrs)
throws NamingException
public void bind(String name, Object obj,
Attributes attrs) throws NamingException
public void rebind(Name name, Object obj,
Attributes attrs) throws NamingException
public void rebind(String name, Object obj,
Attributes attrs) throws NamingException
public DirContext createSubcontext(Name name,
Attributes attrs) throws NamingException
public DirContext createSubcontext(String name,
Attributes attrs) throws NamingException
Интерфейс DirContext описывает следующие методы получения атрибутов
public Attributes getAttributes(Name name)
throws NamingException
public Attributes getAttributes(String name)
throws NamingException
public Attributes getAttributes(Name name,
String[] attrIds) throws NamingException
public Attributes getAttributes(String name,
String[]attrIds) throws NamingException
возвращающие множество всех атрибутов объекта с именем name или некоторых его
атрибутов, имена которых записаны в массив attrIds.
Методы
public void modifyAttributes(Name name,
int mod_op,
Attributes attrs) throws NamingException
public void modifyAttributes(String name,
int mod_op,
Attributes attrs) throws NamingException
изменяют множество атрибутов объекта с именем name. Вид изменения определяется
вторым аргументом mod_op, который принимает одно из трех значений:
 ADD_ATTRIBUTE – добавить атрибуты из множества attrs к множеству
атрибутов объекта с именем name;
 REPLACE_ATTRIBUTE — заменить значения тех атрибутов объекта с именем
name, которые входят в множество атрибутов attrs;
 REMOVE_ATTRIBUTE — удалить те атрибуты объекта с именем name, которые
входят в множество атрибутов attrs.
Методы
public void modifyAttributes(Name name,
ModificationItem[] mods) throws NamingException
public void modifyAttributes(String name,
ModificationItem[] mods) throws NamingException
последовательно выполняют модификации, занесенные в массив модификаторов,
выраженных объектами класса ModificationItem. Это небольшой класс, содержащий
пару «вид изменения – изменяемый атрибут», которая заносится в конструктор класса:
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 26 Прикладное программирование в ТС
Лекция 3-14
public ModificationItem(int mod_op, Attribute attr)
Методы класса
public int getModificationOp()
public Attribute getAttribute()
позволяют получить значение параметра mod_op или значение атрибута.
Каждый атрибут описан интерфейсом Attribute. Рассмотрим подробнее этот
интерфейс.
У атрибута есть имя и в нем содержится ноль или больше значений. Количество
значений атрибута можно узнать с помощью метода
public int size().
Значения атрибута могут быть упорядоченными или неупорядоченными.
Упорядоченные значения могут дублироваться, различаясь номерами. Тип и
представление значений атрибута определяются схемой (schema) атрибута. Это понятие
детализируется реализациями интерфейса.
Имя атрибута возвращается с помощью метода
public String getID(),
а значение добавляется в атрибут с помощью метода
public boolean add(Object value).
Если значения атрибута не упорядочены, и такое значение уже есть в атрибуте, то
метод не делает ничего и возвращает false; если значения упорядочены, то новое
значение добавляется в конец списка значений.
Если значения атрибута упорядочены, то новое значение можно вставить в
определенное место списка значений с помощью метода
public void add(int ix, Object attrVal).
Установить значение в упорядоченном списке значений можно с помощью метода
public Object set(int ix, Object attrVal).
Этот метод возвращает старое значение атрибута. Получить значение атрибута
можно с помощью методов
public Object get() throws NamingException
public Object get(int ix) throws NamingException.
Список всех значений возвращается с помощью метода
public NamingEnumeration getAll()
throws NamingException.
Метод
public DirContext getAttributeDefinition()
throws NamingException
возвращает определение схемы для данного атрибута (определение схемы содержит,
например, такую информацию как правила сравнения значений атрибутов).
Метод
public DirContext getAttributeSyntaxDefinition()
throws NamingException
возвращает определение синтаксиса для данного атрибута, которое определяет формат
значений атрибута.
Удалить значение можно с помощью одного из методов
public Object remove(int ix)
public boolean remove(Object attrval)
Первый метод возвращает удаленный объект, а второй возвращает true, если
удаление сделано, и false в противном случае.
Удалить все значения атрибута можно с помощью метода
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 27 Прикладное программирование в ТС
Лекция 3-14
public void clear().
Интерфейс Attribute реализован в классе BasicAttribute. Экземпляры
класса создаются его конструкторами. Два конструктора создают неупорядоченный или
упорядоченный атрибут с именем id без значений:
public BasicAttribute(String id)
public BasicAttribute(String id, boolean ordered).
Другие два конструктора создают атрибут с именем id и со значением value:
public BasicAttribute(String id, Object value)
public BasicAttribute(String id, Object value,
boolean ordered).
Методы класса просто реализуют соответствующие методы интерфейса
Attribute, например:
BasicAttribute bal =
newBasicAttribute("dns", "host.some.com");
BasicAttribute ba2 =
new BasicAttribute("ip", "210.135.1.10");
BasicAttribute ba3 =
new BasicAttribute("port", "1521");
ba3.add("1526");
Интерфейс Attributes описывает множество всех атрибутов контекста каталога.
Методы работы с этим множеством аналогичны соответствующим методам для
коллекций. Два метода заносят атрибут во множество атрибутов:
public Attribute put(Attribute attr)
public Attribute put(String attrID, Object val).
Эти методы возвращают старое значение атрибута, или null, если такого значения
не было.
Методы
public Attribute get(String name)
public NamingEnumeration getAll()
public NamingEnumeration getIDs()
возвращают один атрибут, множество всех атрибутов или множество идентификаторов
всех атрибутов каталога контекста.
Метод
public Attribute remove(String attrID)
удаляет атрибут из множества, возвращая удаленное значение или null, если атрибута с
именем attrID нет во множестве атрибутов.
Наконец, метод
public int size()
возвращает количество атрибутов.
Класс BasicAttributes реализует интерфейс Attributes. Имена атрибутов
множества этого класса могут сравниваться между собой с учетом регистра букв или без
учета. Это определяется в конструкторе класса:
public BasicAttributes(boolean ignoreCase).
Конструктор по умолчанию
public BasicAttributes()
и конструктор
public BasicAttributes(String attrID, Object val)
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 28 Прикладное программирование в ТС
Лекция 3-14
создают пустое множество атрибутов и множество с одним атрибутом, в котором регистр
букв учитывается при сравнении имен атрибутов.
Еще один конструктор
public BasicAttributes(String attrID,
Object val,
boolean ignoreCase)
учитывает все эти параметры.
Методы класса просто реализуют методы интерфейса Attributes.
Пример создания и перебора атрибутов и их значений:
Attributes attrs = new BasicAttributes();
attrs.put(ba1);
attrs.put(ba2);
attrs.put(ba3);
attrs.put("database", "myDB");
NamingEnumeration at = attrs.getAll();
while(at.hasMore()) {
Attribute bd = (Attribute)at.next();
System.out.print(bd.getID() + ":
");
NamingEnumeration at1 = bd.getAll();
while (at1.hasMore())
System.out.print(" " + at1.next());
System.out.println();
}
Пример модификации атрибута:
try {
Attributes repAttrs =
new BasicAttributes("ip", "210.135.1.25");
dirCtx.modifyAttributes("env/jdbc/myDB",
DirContext.REPLACE_ATTRIBUTE, repAttrs);
}
catch(NamingException ne){}
Пример модификации атрибута с использованием класса ModificationItem:
ModificationItem[] mods = new ModificationItem[2];
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
new BasicAttribute("name", "Иванов"));
mods[1] =
new ModificationItem( DirContext.MODIFY_ATTRIBUTE,
new BasicAttribute("ip", "210.135.1.25"));
dirCtx.modifyAttributes("env/jdbc/myDB", mods);
3.2.2.5. Поиск в контексте каталога
Хотя интерфейс DirContext наследует методы lookup() своего суперкласса
Context, но для поиска объекта в контексте заданного каталога по заданным атрибутам
он описывает метод 8 методов search().
public NamingEnumeration search(Name name,
Attributes matchingAttributes) throws NamingException
Этот метод и другие методы search() возвращают совокупность результатов
поиска. Каждый результат представлен в виде объекта класса SearchResult. Это
небольшой класс, расширяющий класс Bindings. Методы этого класса
public Attributes getAttributes()
public void setAttributes(Attributes attrs)
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 29 Прикладное программирование в ТС
Лекция 3-14
public String toString()
позволяют соответственно получить значения атрибутов, установить значения атрибутов
и получить строковое значение результата.
Метод
public NamingEnumeration search(Name name,
Attributes matchingAttributes,
String[] attributesToReturn)
throws NamingException
возвращает не все множество атрибутов найденных объектов, а только те атрибуты,
которые указаны в массиве attributesToReturn .
Метод
public NamingEnumeration search(Name name, String filter,
SearchControls cons) throws NamingException
использует при поиске фильтр filter и условия поиска cons.
Фильтр задается строкой пар «атрибут – значение», оформленной согласно
рекомендации RFC 2254, например, "age=32". Кроме равенства можно указать
приблизительное равенство "~=", больше или равно ">=", меньше или равно "<=". В
качестве значения атрибута можно записать звездочку "*", означающую любое значение.
Каждая пара заключается в скобки, пары могут быть связаны логическими операциями
конъюнкции "&", дизъюнкции "|" и отрицания "!". Знаки операций записываются перед
операндами, т.е. используется обратная польская запись. Например:
String filter = "(&(name=И*)(age=60))";
Интерпретация операций и их выполнение зависят от поставщика услуг
конкретной системы каталогов. Если поставщик услуг не может выполнить ту или иную
операцию, то выбрасывается исключение класса InvalidSearchFilterException.
Условия поиска содержатся в объекте класса SearchControls и заносятся в
конструктор класса
public SearchControls(int scope, long countlim,
int timelim, String[] attrs, boolean retobj,
boolean deref)
где:
 scope – это одно из трех значений:
1. OBJECT_SCOPE – вернуть не более одного значения;
2. ONELEVEL_SCOPE – не просматривать подкаталоги;
3. SUBTREE_SCOPE – просмотреть рекурсивно все поддерево каталогов;
 countlim – наибольшее число возвращаемых объектов. Значение 0 указывает,
что надо вернуть все найденные значения;
 timelim – наибольшее время в миллисекундах, выделенное на поиск. Ноль
означает неограниченное время;
 attrs – набор атрибутов, которые надо вернуть в результате поиска. Значение
null предписывает вернуть все атрибуты, пустое значение — не возвращать атрибуты;
 retobj – возвращать или не возвращать сам найденный объект;
 deref – не разрешать ссылки во время поиска.
Четвертый метод
public NamingEnumeration search(Name name, String filterExpr,
Object[] filterArgs, SearchControls cons)
throws NamingException
использует в параметре filterExpr выражения вида {n}, где n – неотрицательное
число. Эти выражения перед поиском заменяются элементом массива filterArgs[n].
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 30 Прикладное программирование в ТС
Лекция 3-14
Еще четыре таких же метода задают имя контекста name не как объект,
реализующий интерфейс Name, а как строку класса String.
3.2.2.6. Ссылки на объекты
Если объект не входит в текущую систему имен или каталогов или он настолько
велик, что система JNDI не может его хранить, то система JNDI хранит ссылку на него.
Каждый такой объект должен обеспечить возможность ссылки на него, реализовав метод
public Reference getReference() throws NamingException
интерфейса Referenceable в пакете javax.naming.
Класс Reference, экземпляр которого должен вернуть этот метод, содержит все
сведения, необходимые для поиска объекта:
 упорядоченный список адресов объекта addr;
 полное имя класса объекта className;
 имя класса-фабрики factory, создающего объект;
 местоположение этого класса-фабрики factoryLocation.
Эти сведения заносятся в конструктор класса
public Reference(String className, RefAddr addr,
String factory, String factoryLocation)
Как видно из описания аргументов конструктора, адрес объекта addr задается
экземпляром класса RefAddr. Это абстрактный класс, содержащий кроме адреса объекта
еще и тип адреса. Для получения объекта класс RefAddr содержит абстрактный метод
public abstract Object getContent(),
который реализован в подклассах BinaryRefAddr и StringRefAddr класса
RefAddr.
Класс BinaryRefAddr содержит двоичный адрес, например, МАС-адрес сетевой
карты, в виде массива байтов src, задаваемого в конструкторе
public BinaryRefAddr(String addrType, byte[] src)
Класс StringRefAddr содержит адрес в виде строки символов addr, задаваемой
в конструкторе
public StringRefAddr(String addrType, String addr),
например:
public class MyRefObject implements Referenceable {
public Reference getReference()
throws NamingException {
StringRefAddr sra =
new StringRefAddr("URL",
"file:/java/myclasses");
return new Reference("MyRefObject", sra,
null, null);
}
// Другие методы
}
Теперь можно включить ссылку на объект MyRefObject в систему имен JNDI с
помощью метода rebind():
MyRefObject mr = new MyRefObject();
ctx.rebind("myobject", mr);
Затем объект можно получить следующим образом:
MyRefObject newmr = (MyRefObject)ctx.lookup("myobject");
В том случае, когда адрес состоит из одной строковой ссылки, как, например, адрес
URL или имя типа CompositeName, записанное в виде строки, вместо класса
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 31 Прикладное программирование в ТС
Лекция 3-14
Reference удобнее использовать его расширение – класс LinkRef. Этот класс хранит
один адрес типа "LinkAddress". В его конструкторах
public LinkRef(Name addr)
public LinkRef(String addr)
просто задается ссылка addr в виде строки или объекта типа Name.
Метод
public String getLinkName() throws NamingException
класса LinkRef позволяет получить имя ссылки.
3.2.2.7. Обработка событий в JNDI
Служба JNDI обеспечивает средства для обработки событий, возникающих в
процессе работы службы имен и каталогов. Данные службы генерируют события,
связанные с переименованием объектов, добавлением и удалением объектов, а также с
изменением состояния объекта, зарегистрированного посредством службы имен. Эти
события могут быть использованы при работе клиентов, и JNDI предоставляет средства
для получения необходимой информации. Особенности поддержки событий зависят от
реализации SPI.
Класс NamingEvent в пакете javax.naming.event представляет событие,
которое генерируется службой тогда, когда один из зарегистрированных объектов
претерпевает изменения. Этот класс содержит данные об источнике события,
информацию об изменениях, зависящую от особенностей SPI, и ссылки на данные о
связанных объектах до и после события. Источником события является контекст,
реализующий интерфейс EventContext. Этот интерфейс представляет собой
расширение интерфейса Context, в котором предусмотрена возможность добавлять и
удалять блоки прослушивания (listener) для событий, связанных с работой службы имен.
Если при генерации события возникает ошибка, то блок прослушивания получает
информацию об этом посредством экземпляра класса NamingExceptionEvent. Если в
процессе работы службы имен возникает исключительная ситуация, каждый из объектов
NamingListener, зарегистрированных для получения информации о событиях,
оповещается об этом.
Для обработки событий, сгенерированных службой имен, используются
расширения интерфейса NamingListener с именами ObjectChangeListener и
NamespaceChangeListener. Реализацию интерфейса ObjectChangeListener
применяют для поддержки событий, связанных с изменением состояния объектов
(NamingEvent.OBJECT_CHANGED),
например
добавлением,
удалением
или
модификацией атрибутов, а также с заменой объекта в системе имен. Для того чтобы
получить подробную информацию о произведенных изменениях, можно проверить
состояние
объектов
связывания
до
и
после
события.
Интерфейс
NamespaceChangeListener реализуется теми объектами, которые должны
реагировать
на
добавление
(NamingEvent.OBJECT_ADDED),
удаление
(NamingEvent.OBJECT_REMOVED)
или
переименование
(NamingEvent.OBJECT_RENAMED) объекта, зарегистрированного службой имен.
Пример блока прослушивания:
import javax.naming.event.*;
public class SampleNamingListener
implements NamespaceChangeListener,
ObjectChangeListener {
public void objectAdded(NamingEvent namingEvent) {
// Поддержка добавления объекта
}
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
- 32 Прикладное программирование в ТС
Лекция 3-14
public void objectRemoved(NamingEvent namingEvent) {
// Поддержка удаления объекта
}
public void objectRenamed(NamingEvent namingEvent) {
// Поддержка переименования объекта
}
public void objectChanged(NamingEvent namingEvent) {
// Поддержка изменения атрибутов объекта
}
…
public void namingExceptionThrown
(NamingExceptionEvent namingExceptionEvent) {
// Поддержка исключений службы имен
}
С помощью методов класса NamingEvent
public Object getChangeInfo()
public EventContext getEventContext()
public Binding getNewBinding()
public Binding getOldBinding()
public int getType()
можно соответственно получить информацию об изменении для данного события,
получить источник события, получить связь объекта до или после изменения, а также
получить тип события (одна из приведенных выше констант).
С помощью методов
public NamingException getException()
public EventContext getEventContext()
класса NamingExceptionEvent можно получить брошенное исключение или
объект EventContext, вызвавший данное событие.
Файл: 446993030 Создан: 09.07.2007 Модифицирован: 18.01.2016
Автор: Шонин В.А.
Download