Создание приложений с базами данных для Интернета и интрасетей: практическое руководство (С) Александр Вячеславович Фролов, Григорий Вячеславович Фролов, 2000 Поле Middle name и метка этого поля Конструктор класса AppletMsgBox Взаимодействие приложений Java и расширений сервера Web Аплет для передачи номера кредитной карточки Отправка данных расширению сервера Web Размещение аплета в документе HTML Исходный текст расширения ISAPI Передача параметров странице ASP Последняя глава нашей книги посвящена использованию аплетов Java в приложениях Интернета. Как Вы знаете, программы, написанные на языке программирования Java, способны работать практически на всех распространенных компьютерных платформах. В результате одна из разновидностей таких программ, а именно аплеты Java, получили широкое распространение на страницах серверов Web. Именно так реализуется главное преимущество аплетов Java — совместимость с различными платформами, так как посетители этих страниц могут запускать свои браузеры под управлением различных операционных систем. Заметим, однако, что в большинстве случаев аплеты Java используются только для достижения всевозможных визуальных эффектов, создания «интеллектуальных» графических ссылок, меню и т. д. Между тем, они способны решать и более сложные задачи, такие, как непосредственное взаимодействие с расширениями сервера Web в виде приложений CGI и ISAPI и передача параметров страницам ASP. Мы рассмотрим именно эти вопросы, не нашедшие, на наш взгляд, достойного отражения в многочисленных книгах, посвященных приложениям Java. Тем из Вас, кто еще никогда не создавал программ на языке Java, мы предлагаем наше руководство, размещенное на сервере создателя этого языка Sun Microsystems по адресу http://www.sun.ru/java/books/online/index.html. В наших примерах мы покажем, как создавать аплеты с формами, позволяющими вводить информацию о кредитных карточках. Эти формы содержат такие элементы управления, как текстовые поля и кнопки. Начинающих программистов, особенно тех, кто создавал приложения для Windows или OS/2, может шокировать способ, которым в приложениях Java выполняется размещение компонентов и контейнеров внутри окна. Самая большая и неприятная на первый взгляд особенность заключается в невозможности размещения компонентов с указанием точных координат (хотя с применением специальной техники это все же достижимо). Другая особенность — программы Java не имеют ресурсов, подобных ресурсам исполнимых файлов Windows и описывающих диалоговые панели или элементы управления. Внешний вид пользовательского интерфейса определяется динамически во время выполнения программы. Поясним, в чем тут дело и для чего нужно преодолевать такие трудности. Создавая приложения Java, никогда не следует забывать о том, что они предназначены для работы на различных компьютерных платформах. При этом Вы не можете полагаться на то, что Вам будет доступен какой-либо конкретный шрифт, кнопки или другие компоненты будут иметь определенный размер или форму, а видеоадаптер будет работать в режиме с каким-либо заданным или заранее известным разрешением. Для того чтобы обеспечить работу приложений Windows в режимах с различным разрешением видеоадаптера, размеры элементов управления «привязываются» к размерам системного шрифта. Однако указанный способ недостаточно универсален для применения на различных платформах, так как в разных операционных системах, вероятно, эта «привязка» будет выполняться по-разному. Кроме того, теоретически системный шрифт в какой-нибудь операционной системе может отсутствовать как таковой. С другой стороны, динамическое формирование внешнего вида пользовательского интерфейса во время работы программы позволит адаптировать его «на ходу» к особенностям конкретной операционной системы. Для этого приложения Java используют достаточно гибкую и мощную систему управления размещением компонентов и контейнеров с названием Layout Manager. Система Layout Manager способна работать в нескольких основных режимах, отличающихся различными стратегиями размещения компонентов, определения их размеров и выравнивания. В самом простом режиме FlowLayout компоненты добавляются в окно контейнера с применением следующего алгоритма. Каждый новый добавленный компонент располагается вслед за предыдущим в направлении слева направо и сверху вниз, при этом выполняется центровка компонентов по горизонтали. Одной из особенностей данного режима является возможное изменение взаимного расположения добавленных компонентов при изменении размеров контейнера. Установка режима FlowLayout выполняется при помощи метода setLayout, как это показано ниже: setLayout(new FlowLayout()); Далее компоненты добавляются в окно контейнера методом add, например: TextField tf; Когда установлен режим размещения GridLayout, все компоненты располагаются в ячейках таблицы, имеющей заданное количество строк и столбцов. Размеры компонентов изменяются таким образом, чтобы они полностью занимали свои ячейки. Установка режима GridLayout выполняется при помощи метода setLayout, как это показано ниже: setLayout(new GridLayout()); Далее компоненты добавляются в окно контейнера методом add, например: TextField tf; Режим BorderLayout предполагает разделение окна контейнера на рамку и центральную часть. Методу add при этом указывается направление от центра окна, в котором следует размещать компоненты. Направление указывается следующим образом: add("Center", btn1); // центр Здесь мы добавили в окно контейнера компоненты btn1, …, btn5. При этом компонент btn1 располагается в центре окна контейнера, а остальные компоненты — по бокам. Размеры компонентов изменяются таким образом, чтобы они полностью заполняли контейнер. Заметим, что Вы не обязаны каждый раз добавлять в контейнер именно пять компонентов и задействовать при этом все возможные направления. Режим размещения CardLayout предназначен для поочередного размещения нескольких компонентов в одном контейнере (например, класса Panel). При добавлении компонента в контейнер необходимо передать его имя методу add через первый параметр, например: picFrame pf; Остальные компоненты добавляются аналогичным образом. В классе CardLayout предусмотрено несколько методов, предназначенных для выбора отображаемого компонента. Эти методы перечислены в таблице 9-1. Таблица 9-1. Методы для выбора компонента
Всем указанным методам, кроме метода show, передается через единственный параметр ссылка на родительский контейнер, в котором выполняется размещение. Методу show через второй параметр дополнительно передается имя компонента (как строка класса String). Последний режим размещения системы LayoutManager — это режим GridBagLayout. Он считается наиболее трудным, однако по сравнению с другими режимами он очень гибкий. В ряде случаев Вам просто не обойтись без него. Так же как и рассмотренный нами ранее режим GridLayout, режим GridBagLayout предполагает размещение компонентов в ячейках некоторой таблицы заданной размерности. Вот наиболее важные отличия между этими режимами: · в режиме GridLayout размещаемые компоненты изменяют свои размеры таким образом, чтобы заполнить ячейки таблицы, в которых они располагаются. Режим GridBagLayout позволяет контролировать этот процесс, причем при необходимости Вы можете задать стратегию такого изменения или отказаться от него вовсе; · в режиме GridLayout каждый компонент занимает только одну ячейку. Что же касается режима GridBagLayout, то здесь компоненты могут занимать несколько смежных ячеек в строках или столбцах; · при изменении размеров контейнера во время работы приложения при использовании режима GridLayout все компоненты неизбежно изменяют свои размеры. Это далеко не всегда удобно. В режиме GridBagLayout Вы можете управлять стратегией изменения размеров компонентов или отказаться от такого изменения. Режим размещения компонентов GridBagLayout удобен для создания диалоговых панелей, содержащих такие компоненты, как текстовые поля редактирования, переключатели, кнопки и т. д. Выбирая соответствующим образом параметры размещения отдельных компонентов путем заполнения соответствующих полей класса GridBagConstraints, можно создавать панели, напоминающие по своему внешнему виду и поведению стандартные диалоговые панели Windows или других операционных систем с графическим интерфейсом. При этом можно добиться, чтобы размеры компонентов и их взаимное расположение не изменялись при корректировке размеров окна контейнера. Это невозможно при работе в других режимах размещения, таких, как FlowLayout или GridLayout. Как пользоваться режимом размещения GridBagLayout? Схема достаточно проста. Прежде всего Вы должны создать объект класса GridBagLayout при помощи конструктора и выбрать его, как это показано ниже: GridBagLayout gbl = new
GridBagLayout(); Далее Вам нужно создать объект класса GridBagConstraints, поля которого будут определять параметры размещения отдельных компонентов: GridBagConstraints c = new GridBagConstraints(); Далее Вам нужно задать значения полей объекта класса GridBagConstraints, например, так (позже мы расскажем о назначении отдельных полей): c.anchor = GridBagConstraints.NORTH; Подготовив объект класса GridBagConstraints, Вам нужно установить его в системе Layout Manager методом setConstraints и добавить очередной компонент в окно контейнера методом add: tf = new TextField(30); Далее описанная процедура выполняется над всеми остальными добавляемыми компонентами, причем объект класса GridBagConstraints можно не создавать каждый раз заново, а использовать повторно. Но если все так просто, то в чем же тогда сложность работы с режимом размещения GridBagLayout? Очевидно, дело в выборе значений параметров объекта класса GridBagConstraints. Перечислим эти поля и дадим их краткую характеристику. Полную информацию Вы найдете в документации JDK. Поля gridx и gridy Поля gridx и gridy задают соответственно номер столбца и номер строки для ячейки, в которую будет помещен компонент. Левой верхней ячейке соответствуют нулевые значения. В качестве значений для этих полей можно также указывать константу GridBagConstraints.RELATIVE. Если эта константа указана в поле gridx, номер столбца размещаемого компонента будет на единицу больше номера столбца для компонента, размещенного ранее. Аналогично и для поля gridy. Вы можете использовать значение GridBagConstraints.RELATIVE в данных полях при последовательном размещении компонентов в ячейках таблицы в направлении слева направо и сверху вниз. Поля gridwidth и gridheight Поля gridwidth и gridheight определяют количество ячеек, занимаемых добавляемым компонентом. Если компонент полностью помещается в одну ячейку, Вы вправе задать в этих полях значение единицы. Если же компонент должен занимать, например, две смежные ячейки в одной строке, то для gridwidth нужно задать значение, равное двум, а для gridheight — значение, равное единице. Специальное значение GridBagConstraints.REMAINDER указывает, что компонент должен занять все оставшееся место в текущей строке (для поля gridwidth) или в текущем столбце (для поля gridheight). В поля gridwidth и gridheight можно также записать значение GridBagConstraints.RELATIVE. В этом случае будет задано такое расположение компонента, при котором он займет все оставшееся место в строке (для поля gridwidth) или столбце (для поля gridheight), оставив при этом одну свободную ячейку в последнем столбце или строке. Поле fill Поле fill определяет стратегию распределения свободного пространства ячейки (или ячеек) таблицы для компонента, если его размеры меньше размеров выделенного для него места. Возможные значения приведены в таблице 9-2. Таблица 9-2. Значения поля fill
Поле anchor Поле anchor задает выравнивание компонента внутри отведенного для него пространства. Он включается в работу, когда размеры компонента меньше размеров выделенного для него места. Для поля anchor Вы можете указать значения, приведенные в таблице 9-3. Таблица 9-3. Значения поля anchor
Поля weightx и weighty Эти поля определяют стратегию изменения размеров компонента, отвечая за выделение пространства для столбцов (weightx) и строк (weighty). Если записать в них нулевые значения, все добавленные компоненты займут место в центре контейнера и будут выровнены по центру (как по вертикали, так и по горизонтали). Чтобы размеры компонента изменялись по горизонтали или вертикали, в поля weightx и weightx нужно записать значения от 0,0 до 1,0. Если в столбце несколько компонентов, то его ширина определяется компонентом с максимальным значением weightx. Аналогичное утверждение верно и для строк. Заметим, что дополнительное пространство добавляется к строкам и столбцам снизу и справа соответственно. Поля ipadx и ipady В полях ipadx и ipady Вы можете указать, что размеры компонента необходимо увеличить на заданное количество пикселов по горизонтали и вертикали соответственно. Поле insets Поле insets позволяет задать для компонента отступы от краев выделенной ему области. По умолчанию такие отступы отсутствуют. В поле insets необходимо записать ссылку на объект класса Insets, созданную соответствующим конструктором. Этот конструктор имеет следующий прототип: public Insets( В качестве примера демонстрации режима размещения GridBagLayout приведем исходные тексты аплета GridBag .с формой В окне нашего аплета находится форма для регистрации посетителей сервера, в которой нужно заполнить несколько стандартных полей (рис. 9-1). Рис. 9-1. Форма для регистрации посетителей Если Вы заполните форму и щелкнете кнопку OK, на экране появится диалоговая панель, отображающая введенные значения в окне многострочного редактора (рис. 9-2). Кнопка Cancel удаляет введенную информацию. Рис. 9-2. Отображение введенной информации Как в окне основного аплета, так и в окне диалоговой панели мы установили режим размещения компонент GridBagLayout. Перейдем к рассмотрению исходного текста аплета. Полностью он приведен в листинге 9-1. Листинг 9-1 Вы найдете в файле ch09/Gridbag/GridBag.java на прилагаемом к книге компакт-диске. Главный класс аплета GridBag создан на базе класса Applet и реализует интерфейс ActionListener: import java.applet.Applet; Данный интерфейс необходим для обработки событий, вызываемых нажатием кнопок. В главном классе мы определили несколько полей, предназначенных для хранения ссылок на компоненты — текстовые поля, метки класса Label и кнопки: TextField tfFirstName; Метод init Этот метод получает управление при инициализации аплета. Он создает и размещает все компоненты в окне аплета, а затем регистрирует обработчики событий от кнопок. Компоненты создаются обычным образом при помощи соответствующих конструкторов: tfFirstName = new TextField(20); Далее мы устанавливаем режим размещения GridBagLayout и создаем объект класса GridBagConstraints, необходимый для задания параметров размещения отдельных компонент: GridBagLayout gbl = new
GridBagLayout(); Ниже мы расскажем о выборе этих параметров. Поле First name Заполнение параметров и размещение этого поля выполняется следующим образом: c.anchor = GridBagConstraints.NORTHWEST; В параметре anchor мы указываем, что выравнивание поля следует выполнять в направлении вверх влево, поэтому поле будет прижато к верхнему левому углу контейнера. Параметр fill имеет значение GridBagConstraints.NONE, а значит, при корректировке размеров контейнера размеры поля изменяться не будут. Так как значение полей gridheight и gridwidth равно единице, поле занимает одну ячейку таблицы. Поля gridx и gridy содержат значение GridBagConstraints.RELATIVE, поэтому добавление поля выполняется в направлении слева направо и сверху вниз. И наконец, поле insets задает отступы сверху и слева, равные 10 пикселам. Для этой метки мы используем те же параметры, что и для самого поля: gbl.setConstraints(lbFirstName, c); В результате метка займет положение справа от поля First name. Вот как заполняются параметры размещения для кнопки OK: c.gridwidth =
GridBagConstraints.REMAINDER; Как видите, параметр gridwidth имеет значение, равное GridBagConstraints.REMAINDER. В результате кнопка будет последним компонентом в первой строке. Ее размеры останутся неизменными при корректировке размеров контейнера, так как поле fill имеет значение GridBagConstraints.NONE. Чтобы несколько увеличить размеры кнопки OK по горизонтали, мы задали в поле ipadx значение, равное 32 пикселам. Поле Middle name и метка этого поля Перед добавлением поля и его метки мы восстанавливаем параметры ipadx и gridwidth, измененные на предыдущем этапе: c.ipadx = 0; В результате поле Middle name будет размещено в первой ячейки второй строки. Эта кнопка размещается так: c.gridwidth =
GridBagConstraints.REMAINDER; Здесь в поле gridwidth мы указали значение GridBagConstraints.REMAINDER, поэтому кнопка Cancel будет последней во второй строке. Обратите внимание на поле weightx. Его значение не равно нулю, значит, последний столбец нашей таблицы займет все оставшееся место в направлении вправо. Если бы значение этого поля равнялось нулю, все компоненты оказались бы выровненными по центру в горизонтальном направлении. Вы можете попробовать это сами. Это поле добавляется в начало третьей строки: c.ipadx = 0; Здесь мы просто восстанавливаем параметры, аналогичные параметрам поля Middle name, расположенного в начале второй строки. Метка поля Last name Эта метка занимает всю оставшуюся часть третьей строки, так как в поле gridwidth мы задали значение GridBagConstraints.REMAINDER: c.gridwidth =
GridBagConstraints.REMAINDER; При размещении этого поля мы восстанавливаем значение параметра gridwidth, измененное на предыдущем этапе: c.gridwidth = 1; Для этого компонента мы выделяем всю оставшуюся часть строки, задавая в поле gridwidth значение GridBagConstraints.REMAINDER: c.gridwidth =
GridBagConstraints.REMAINDER; Это поле занимает одну ячейку последней строки, поэтому в поле gridwidth мы записали значение 1: c.gridwidth = 1; Для этой метки мы установили следующие параметры: c.weighty = 1.0; Так как в поле weighty указано значение 1, для последней строки таблицы отводится все оставшееся снизу пространство контейнера. Если же записать сюда нулевое значение, все компоненты будут центрированы в окне контейнера по вертикали. Перед завершением работы метод init регистрирует обработчики событий от кнопок: btnOK.addActionListener(this); Метод actionPerformed В задачу этого метода входит обработка событий, происходящих в результате щелчка кнопки, и отображение диалоговой панели. При щелчке кнопки OK метод actionPerformed получает строки из полей нашей формы и записывает их в текстовую переменную с именем s: String s = "<Personal
information>"; Далее метод создает диалоговую панель класса AppletMsgBox (определенный в нашем приложении), передавая строку s соответствующему конструктору: AppletMsgBox amsgbox; Панель затем отображается методом show. В том случае если Вы щелкнете кнопку Cancel, поля формы очистятся: tfFirstName.setText(""); Класс AppletMsgBox Этот класс мы создали для отображения диалоговой панели с сообщением и кнопкой OK. Он образован на базе класса Frame и реализует интерфейс ActionListener: class AppletMsgBox extends Frame В классе AppletMsgBox определены два поля: Button btnOK; Первое из них хранит ссылку на кнопку, а второе — ссылку на многострочный редактор текста, в окне которого мы отображаем сообщение. Конструктор класса AppletMsgBox Конструктору класса AppletMsgBox передаются два параметра — строка сообщения и строка заголовка: public AppletMsgBox(String msg, String
title) Первым делом конструктор класса AppletMsgBox вызывает конструктор базового класса Frame, передавая ему строку заголовка title, и устанавливает размеры окна панели: super(title); Кнопка и редактор текста создаются обычным образом: btnOK = new Button("OK"); Так как поле ta будет использоваться только для отображения сообщений, мы отменяем функцию редактирования, вызывая метод setEditable. Далее мы устанавливаем режим размещения компонента GridBagLayout: GridBagLayout gbl = new
GridBagLayout(); Параметры размещения текстового поля выбираются следующим образом: c.anchor = GridBagConstraints.CENTER; Задавая в поле anchor значение GridBagConstraints.CENTER, мы добиваемся центрирования редактора внутри выделенного ему пространства. Так как поле fill имеет значение GridBagConstraints.BOTH, размеры окна редактора изменяются таким образом, чтобы он занимал всю поверхность выделенной ему ячейки таблицы. Мы расположили окно редактора в одной ячейке (значение поля gridheight равно единице), причем так, чтобы оно заняло всю первую строку (в поле gridwidth мы установили значение GridBagConstraints.REMAINDER). Что же касается кнопки, то ее размеры останутся постоянными при корректировке размеров контейнера: c.fill = GridBagConstraints.NONE; Заметим, что мы не ввели значения в полях weightx и weighty. В результате при изменении размеров окна диалоговой панели и редактор, и кнопка остаются в его центре. Перед завершением своей работы конструктор регистрирует обработчик событий для кнопки btnOK: btnOK.addActionListener(this); Метод actionPerformed Этот метод скрывает окно диалоговой панели, когда пользователь щелкает кнопку OK: public void
actionPerformed(ActionEvent e) Язык программирования Java отличается богатой и продуманной библиотекой классов, предназначенной для решения самых разных задач — от создания архивов ZIP и работы с растровыми графическими изображениями до задач организации взаимодействия приложений Java через сеть. В этом разделе кратко описаны основные классы сетевой библиотеки Java, которые потребуются нам для связи аплетов с расширениями сервера Web. Класс InetAddress Для работы с адресами IP в библиотеке классов Java предназначен класс InetAddress. С его помощью приложение определяет адрес IP локального узла, а также адреса удаленного узла, заданного своим доменным именем. Вот прототипы наиболее интересных методов этого класса: public static InetAddress
getLocalHost(); Заметим, что создание объекта класса InetAddress выполняется не с помощью оператора new, а с применением статических методов getLocalHost, getByName и getAllByName. Метод getLocalHost создает объект класса InetAddress для локального узла, то есть для той рабочей станции, на которой выполняется приложение Java: InetAddress iaLocal; В том случае, если Вас интересует удаленный узел сети, Вы можете создать для него объект класса InetAddress, используя методы getByName или getAllByName. Первый возвращает адрес узла, а второй — массив всех адресов IP, связанных с данным узлом. Если узел с указанным именем не существует, при выполнении методов getByName и getAllByName возникает исключение UnknownHostException. Методам getByName и getAllByName допустимо передавать не только имя узла, например www.sun.com, но и строку адреса IP в виде четырех десятичных чисел, разделенных точками. Кратко рассмотрим другие методы класса InetAddress. Метод getAddress возвращает массив из четырех байт IP-адреса объекта. Байт с нулевым индексом этого массива содержит старший байт адреса IP. Метод toString возвращает текстовую строку, которая содержит имя узла, разделитель «/» и адрес IP в виде четырех десятичных чисел, разделенных точками. Средствами метода getHostName Вы можете определить имя узла, для которого был создан объект класса InetAddress. И наконец, метод equals предназначен для сравнения адресов IP как объектов класса InetAddress. Класс URL Для работы с ресурсами, заданными адресами URL, в библиотеке классов Java имеется очень удобный и мощный класс с названием URL. С помощью определенных в нем конструкторов и методов нетрудно извлечь и проверить отдельные компоненты адреса: протокол, адрес узла, номер порта, имя файла. Вы также можете открыть поток, связанный с ресурсом и прочитать его для отображения, обработки или для копирования в другой поток. Расскажем кратко о классе URL. В этом классе предусмотрено четыре конструктора. Первый из них создает объект URL для сетевого ресурса, адрес URL которого передается в виде текстовой строки через единственный параметр spec: public URL(String spec); В процессе создания объекта проверяется заданный адрес URL. Если адрес указан неверно, возникает исключение MalformedURLException. Это же происходит при попытке использовать протокол, с которым данная система не может работать. Второй вариант конструктора класса URL допускает раздельное указание протокола, адреса узла, номера порта, а также имя файла: public URL(String protocol, String host, int port, String file); Третий вариант предполагает использование номера порта, принятого по умолчанию: public URL(String protocol, String host, String file); Для протокола HTTP, например, это порт с номером 80. И наконец, четвертый вариант конструктора допускает указание контекста адреса URL и строки адреса URL: public URL(URL context, String spec); Строка контекста позволяет указывать компоненты адреса URL, отсутствующие в строке spec, такие, как протокол, имя узла, файла или номер порта. Методы класса URL Кратко рассмотрим самые интересные методы, определенные в классе URL. Метод openStream позволяет создать входной поток для чтения файла ресурса, связанного с созданным объектом класса URL: public final InputStream openStream(); Для выполнения операции чтения из созданного таким образом потока Вы можете использовать метод read, определенный в классе InputStream. Метод getHost позволит Вам определить имя узла, соответствующего данному объекту URL: public String getHost(); Метод getFile позволяет получить имя файла, связанного с данным объектом URL: public String getFile(); Метод getPort предназначен для определения номера порта, на котором выполняется связь для объекта URL: public int getPort(); Методом getProtocol Вы можете определить протокол, использование которого приводит к установлению соединения с ресурсом, заданным объектом URL: public String getProtocol(); Метод getRef возвращает текстовую строку ссылки на ресурс, соответствующий данному объекту URL: public String getRef(); Метод hashCode возвращает хэш-код объекта URL: public int hashCode(); Вы можете использовать метод equals для определения идентичности адресов URL, заданных двумя объектами класса URL: public boolean equals(Object obj); Если адреса URL идентичны, метод equals возвращает значение true, если нет — значение false. Метод toExternalForm возвращает текстовую строку внешнего представления адреса URL, определенного данным объектом класса URL: public String toExternalForm(); Метод toString возвращает текстовую строку, представляющую данный объект класса URL: public String toString(); Класс URLConnection В этом классе нам интересен метод openConnection. Он предназначен для создания канала между приложением и сетевым ресурсом, представленным объектом класса URL: public URLConnection openConnection(); В этом классе также определены методы getOutputStream и getInputStream, средствами которых Вы сможете создать соответственно потоки вывода и ввода , привязанные к каналу. Взаимодействие приложений Java и расширений сервера Web Библиотеки классов Java позволяют организовать взаимодействие между приложением Java и такими расширениями сервера Web, как CGI или ISAPI. В этом случае приложения или аплеты Java смогут посылать произвольные данные расширению сервера Web для обработки, а затем получать результат этой обработки. Методика организации взаимодействия приложений Java и расширений сервера Web основана на применении классов URL и URLConnection. Приложение Java, желающее работать с расширением сервера Web, создает объект класса URL для программы расширения (то есть для исполняемого модуля расширения CGI или библиотеки динамической компоновки DLL расширения ISAPI). Далее приложение получает ссылку на канал передачи данных с этим расширением, представленную в качестве объекта класса URLConnection. Затем, пользуясь методами getOutputStream и getInputStream из класса URLConnection, приложение создает с расширением сервера Web выходной и входной канал передачи данных. Когда приложение отправляет данные в выходной канал, созданный подобным образом, они попадают в стандартный поток ввода приложения CGI или ISAPI. Все выглядит так, как будто бы данные отправлены методом POST из формы, определенной в документе HTML. Обработав полученные данные, расширение сервера Web записывает их в свой стандартный выходной поток. После этого они становятся доступны приложению Java через входной поток, открытый методом getInputStream класса URLConnection. Аплет для передачи номера кредитной карточки Некоторые процессинговые компании предлагают своим клиентам систему безопасного ввода информации о кредитных карточках, реализованную на базе аплетов Java. Их работа основана на шифровании на стороне клиента сведений о карточке, введенных покупателем. При этом аплет шифрует информацию перед передачей ее через сеть, а специальное приложение, работающее на сервере процессинговой компании, получает ее и расшифровывает. Заметим, что подобная задача также решается при помощи клиентского элемента управления ActiveX, работающего на компьютере покупателя, или при помощи специальной программы. Однако первый способ, основанный на применении ActiveX, не пользуется особой популярностью из-за потенциальной опасности клиентских элементов управления ActiveX. Второй, предполагающий загрузку специальной программы, неудобен для покупателя. Еще один способ безопасной передачи номера кредитной карточки связан с использованием протокола Secure HTTP. При этом информация шифруется средствами браузера, а расшифровывается на стороне сервера Web. Но и этот способ не без недостатков. Во-первых, длина ключа, равная для интернациональных версий браузера Microsoft Internet Explorer, составляет 40 бит, чего не всегда достаточно. Во-вторых, прежде чем применять указанный протокол, необходимо приобрести специальный сертификат, за использование которого в дальнейшем придется вносить ежемесячную или ежегодную плату. Аплеты Java полностью обезопасят Вашу информацию. Их можно применять для защищенной передачи данных независимо от наличия соответствующих браузера, кроме того, Вам не придется приобретать какие-либо сертификаты. Однако, если Вы решили создать собственное средство защищенной передачи критичной информации через Интернет, то мы настоятельно рекомендуем выполнять шифрование с использованием каких-либо известных и хорошо проверенных методов. В Интернете немало доступных исходных текстов криптографических программ, созданных на основе серьезных математических исследований. Что же касается самодельных криптографических систем, то, скорее всего, они не смогут противостоять серьезным попыткам дешифрования. Мы не будем описывать криптографические алгоритмы и программы, так как это выходит за рамки рассматриваемого в книге материала. Мы приведем исходные тексты аплета CreditCard, передающего информацию о кредитной карточке расширению сервера Web в открытом виде. При необходимости Вы сами добавите к аплету криптографические модули. Внешний вид аплета CreditCard показан на рис. 9-3.
Рис. 9-3. Ввод номера кредитной карточки в окне аплета После ввода информации о кредитной карточке посетитель должен щелкнуть кнопку OK. При этом данные из полей формы ввода будут переданы расширению сервера Web, созданному нами в виде приложения ISAPI. Это приложение извлечет данные формы и отправит их обратно аплету. Аплет покажет строку, принятую расширением ISAPI, в диалоговой панели, показанной на рис. 9-4.
Рис. 9-4. Информация о кредитной карточке, полученная от расширения ISAPI Реальная система передачи номера кредитной карточки обязательно шифрует информацию перед передачей ее на сервер. При этом аплет, выполняющий шифрование, не должен содержать в себе никаких ключей. Дело в том, что злоумышленники могут вскрыть байт-код аплета с целью извлечения секретного ключа. Поэтому лучше, если для получения ключа аплет, например, установит прямую связь с сервером процессинговой компании с применением сокетов и получит ключ через этот канал. Расширение сервера Web в реальной системе должно расшифровать полученную информацию и выполнить платежную операцию через сервер банка. Для разработки программных модулей, выполняющих все описанные выше операции, необходимо привлекать специалистов процессинговой компании или банка (если Вы создаете сервер процессинговой компании). Поэтому мы не станем описывать этот процесс более подробно, а займемся исходными текстами аплета, выполняющего обмен данными с расширением сервера Web. Полный исходные текст аплета Вы найдете в листинге 9-2. Листинг 9-2 хранится в файле ch09/CreditCard/CreditCard.java на прилагаемом к книге компакт-диске. При инициализации аплета метод init вначале создает все необходимые компоненты и устанавливает режим размещения GridBagLayout: tfName = new TextField(20); Далее метод init устанавливает параметры размещения отдельных компонентов и добавляет их в окно аплета. При этом используется техника, примененная нами ранее в этой главе при создании аплета GridBag. Отправка данных расширению сервера Web Когда пользователь щелкает кнопку OK, управление передается методу actionPerformed. Рассмотрим выполняемые им действия. Прежде всего, этот метод извлекает данные из компонентов, размещенных в окне аплета. Данные оформляются в виде текстовой строки параметров запуска расширения ISAPI с именем iscard.dll, как это показано ниже: URL u; При этом создается новый объект класса URL, представляющий собой адрес URL запускаемого расширения ISAPI. Все операции выполняются в блоке try-catch, так как при работе приложения могут возникать ошибки, связанные, например, с разрывом связи между браузером покупателя и сервером. Далее мы создаем канал связи с расширением ISAPI как объект класса URLConnection: URLConnection c; С помощью методов setDoOutput и setDoInput для канала разрешается выполнения операций вывода и ввода соответственно. Чтобы принять данные от расширения ISAPI, мы создаем входной поток данных класса DataInputStream, основанный на потоке InputStream. Этот поток, в свою очередь, получен при помощи метода getInputStream: DataInputStream is; Чтение из потока выполняется в цикле: String str=""; Здесь просто накапливаются полученные данные в строке str. По достижении конца потока мы его закрываем: is.close(); После получения всех строк от расширения сервера Web мы отображаем содержимое строки str в диалоговой панели: amsgbox = new AppletMsgBox(str,
"Information"); Размещение аплета в документе HTML Аплет, выполняющий обмен данными с сервера Web, надо загружать именно с этого сервера, а не с какого-либо другого. Поэтому в документе HTML с аплетом правильно указывайте параметры тега <APPLET>: <applet name="CreditCard"
code="CreditCard" Параметр CODE должен задавать имя аплета, а параметр CODEBASE — адрес URL каталога, в котором будет расположен аплет. Полный исходный текст документа HTML, подготовленный нами для аплета CreditCard, Вы найдете в листинге 9-3. Листинг 9-3 хранится в файле ch09/CreditCard/CreditCard.html на прилагаемом к книге компакт-диске. Исходный текст расширения ISAPI В листинге 9-4 находится полный исходный текст расширения ISAPI, подготовленного нами для совместной работы с аплетом CreditCard. Листинг 9-4 Вы найдете в файле ch09/IsCard/IsCard.c на прилагаемом к книге компакт-диске. Все основные действия выполняются расширением в функции HttpExtensionProc. Получив управление, эта функция записывает в буфер szBuff заголовок формируемого документа: wsprintf(szBuff, "Content-Type: text/plain\r\n\r\n"); Обратите внимание: в заголовке мы указали, что документ представляет собой текст без форматирования, а не страницы HTML. Передаваемый текст из буфера будет прочитан аплетом CreditCard из входного потока. Далее функция HttpExtensionProc получает строку параметров, переданную аплетом при загрузке расширения, и добавляет ее в буфер: wsprintf(szTempBuf, "%s",
lpECB->lpszQueryString); Таким образом, наше расширение ISAPI отправляет обратно аплету полученные от него параметры. Содержимое буфера посылается аплету следующим образом: if(!lpECB->ServerSupportFunction(lpECB->ConnID, Создавая реальный проект, не забудьте выполнить сканирование строки lpECB->lpszQueryString с целью извлечения из него параметров и при необходимости — расшифровку. Далее Вы сможете использовать параметры в соответствии с логикой обработки номеров кредитных карточек, предусмотренной банком или процессинговой компанией. Передача параметров странице ASP Если по каким-либо причинам Вы не сумеете создать на сервере расширение ISAPI, описанный способ передачи информации о кредитных карточках можно реализовать с помощью серверных сценариев, расположенных в документах ASP. При этом аплет Java получит номер кредитной карточки посетителя и зашифрует его, а затем передаст странице ASP с использованием механизма загрузки нового документа в окно браузера. В этом разделе мы приведем пример аплета CreditCard2, выполняющего указанные действия. Полный исходный текст аплета приведен в листинге 9-5. Листинг 9-5 Вы найдете в файле ch09/CreditCard2/CreditCard2.java на прилагаемом к книге компакт-диске. Так же как и только что описанный аплет CreditCard, аплет CreditCard2 получает от посетителя информацию о кредитной карточке при помощи компонентов, добавленных в режиме размещения GridBagLayout. Когда посетитель щелкает в окне аплета кнопку OK, метод actionPerformed загружает страницу ASP с именем ccard.asp, указывая ее полный адрес URL в конструкторе класса URL: u = new
URL("http://saturn/ccard.asp?" + Для того чтобы серверный сценарий, расположенный на странице ccard.asp, получил информацию о кредитной карточке, при создании объекта класса URL мы добавляем к адресу страницы параметры, отделив их символом «?». При этом параметры отделяются друг от друга символом «&». Чтобы загрузить страницу ccard.asp в окно браузера, мы создаем объект класса AppletContext, вызывая для этого метод getAppletContext: AppletContext appletContext; Далее, пользуясь полученным контекстом, аплет загружает в окно браузера документ с адресом URL, подготовленным в переменной u: if (u != null) Для загрузки мы вызываем метод showDocument, передавая ему в качестве второго параметра имя окна для загрузки документа. Пользуясь этим параметром, аплет может загрузить документ в любой фрейм, в существующее или вновь созданное окно браузера. Параметр «_self» означает, что документ будет загружен в то же самое окно, где находится аплет. Исходный текст страницы ccard.asp приведен в листинге 9-6. Листинг 9-6 Вы найдете в файле ch09/CreditCard2/ccard.asp на прилагаемом к книге компакт-диске. В нем мы получаем параметры, переданные аплетом, при помощи объекта Request, а затем отображаем их значения в динамически создаваемом документе HTML: <p>Name =
<%=Request("Name")(1)%> В реальном проекте параметры можно, например, передать серверному элементу управления ActiveX для расшифровки и дальнейшей обработки. Таким образом, страница ASP станет удобным средством извлечения параметров из данных, отправленных посетителем Вашего сервера при помощи аплета Java.
|