Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содержание] [Дальше]

Создание приложений с базами данных для Интернета и интрасетей: практическое руководство

(С) Александр Вячеславович Фролов, Григорий Вячеславович Фролов, 2000

9. Применение аплетов Java

9. Применение аплетов Java.. 1

Система Layout Manager.. 2

Режим FlowLayout 2

Режим GridLayout 3

Режим BorderLayout 3

Режим CardLayout 3

Режим GridBagLayout 4

Поля gridx и gridy. 5

Поля gridwidth и gridheight. 5

Поле fill. 5

Поле anchor. 6

Поля weightx и weighty. 6

Поля ipadx и ipady. 6

Поле insets. 6

Аплет с формой.. 7

Главный класс аплета GridBag. 8

Метод init.. 9

Поле First name. 9

Метка для поля First name. 10

Кнопка OK.. 10

Поле Middle name и метка этого поля. 10

Кнопка Cancel 10

Поле Last name. 10

Метка поля Last name. 11

Поле ZIP. 11

Метка поля ZIP. 11

Поле Country. 11

Метка поля Country. 11

Метод actionPerformed. 11

Класс AppletMsgBox.. 12

Конструктор класса AppletMsgBox. 12

Метод actionPerformed. 13

Классы Java для работы в сети.. 13

Класс InetAddress.. 13

Класс URL.. 14

Конструкторы класса. 14

Методы класса URL. 15

Класс URLConnection.. 15

Взаимодействие приложений Java и расширений сервера Web.. 16

Аплет для передачи номера кредитной карточки. 16

Инициализация аплета. 18

Отправка данных расширению сервера Web. 18

Размещение аплета в документе HTML. 19

Исходный текст расширения ISAPI 19

Передача параметров странице ASP.. 20

Последняя глава нашей книги посвящена использованию аплетов Java в приложениях Интернета. Как Вы знаете, программы, написанные на языке программирования Java, способны работать практически на всех распространенных компьютерных платформах. В результате одна из разновидностей таких программ, а именно аплеты Java, получили широкое распространение на страницах серверов Web. Именно так реализуется главное преимущество аплетов Java — совместимость с различными платформами, так как посетители этих страниц могут запускать свои браузеры под управлением различных операционных систем.

Заметим, однако, что в большинстве случаев аплеты Java используются только для достижения всевозможных визуальных эффектов, создания «интеллектуальных» графических ссылок, меню и т. д. Между тем, они способны решать и более сложные задачи, такие, как непосредственное взаимодействие с расширениями сервера Web в виде приложений CGI и ISAPI и передача параметров страницам ASP. Мы рассмотрим именно эти вопросы, не нашедшие, на наш взгляд, достойного отражения в многочисленных книгах, посвященных приложениям Java.

Тем из Вас, кто еще никогда не создавал программ на языке Java, мы предлагаем наше руководство, размещенное на сервере создателя этого языка Sun Microsystems по адресу http://www.sun.ru/java/books/online/index.html.

Система Layout Manager

В наших примерах мы покажем, как создавать аплеты с формами, позволяющими вводить информацию о кредитных карточках. Эти формы содержат такие элементы управления, как текстовые поля и кнопки.

Начинающих программистов, особенно тех, кто создавал приложения для Windows или OS/2, может шокировать способ, которым в приложениях Java выполняется размещение компонентов и контейнеров внутри окна. Самая большая и неприятная на первый взгляд особенность заключается в невозможности размещения компонентов с указанием точных координат (хотя с применением специальной техники это все же достижимо).

Другая особенность — программы Java не имеют ресурсов, подобных ресурсам исполнимых файлов Windows и описывающих диалоговые панели или элементы управления. Внешний вид пользовательского интерфейса определяется динамически во время выполнения программы.

Поясним, в чем тут дело и для чего нужно преодолевать такие трудности.

Создавая приложения Java, никогда не следует забывать о том, что они предназначены для работы на различных компьютерных платформах. При этом Вы не можете полагаться на то, что Вам будет доступен какой-либо конкретный шрифт, кнопки или другие компоненты будут иметь определенный размер или форму, а видеоадаптер будет работать в режиме с каким-либо заданным или заранее известным разрешением.

Для того чтобы обеспечить работу приложений Windows в режимах с различным разрешением видеоадаптера, размеры элементов управления «привязываются» к размерам системного шрифта. Однако указанный способ недостаточно универсален для применения на различных платформах, так как в разных операционных системах, вероятно, эта «привязка» будет выполняться по-разному. Кроме того, теоретически системный шрифт в какой-нибудь операционной системе может отсутствовать как таковой.

С другой стороны, динамическое формирование внешнего вида пользовательского интерфейса во время работы программы позволит адаптировать его «на ходу» к особенностям конкретной операционной системы. Для этого приложения Java используют достаточно гибкую и мощную систему управления размещением компонентов и контейнеров с названием Layout Manager.

Система Layout Manager способна работать в нескольких основных режимах, отличающихся различными стратегиями размещения компонентов, определения их размеров и выравнивания.

Режим FlowLayout

В самом простом режиме FlowLayout компоненты добавляются в окно контейнера с применением следующего алгоритма. Каждый новый добавленный компонент располагается вслед за предыдущим в направлении слева направо и сверху вниз, при этом выполняется центровка компонентов по горизонтали. Одной из особенностей данного режима является возможное изменение взаимного расположения добавленных компонентов при изменении размеров контейнера.

Установка режима FlowLayout выполняется при помощи метода setLayout, как это показано ниже:

setLayout(new FlowLayout());

Далее компоненты добавляются в окно контейнера методом add, например:

TextField tf;
Button btnGetName;
  . . .
add(tf);
add(btnGetName);

Режим GridLayout

Когда установлен режим размещения GridLayout, все компоненты располагаются в ячейках таблицы, имеющей заданное количество строк и столбцов. Размеры компонентов изменяются таким образом, чтобы они полностью занимали свои ячейки.

Установка режима GridLayout выполняется при помощи метода setLayout, как это показано ниже:

setLayout(new GridLayout());

Далее компоненты добавляются в окно контейнера методом add, например:

TextField tf;
Button btnGetName;
  . . .
add(tf);
add(btnGetName);

Режим BorderLayout

Режим BorderLayout предполагает разделение окна контейнера на рамку и центральную часть. Методу add при этом указывается направление от центра окна, в котором следует размещать компоненты.

Направление указывается следующим образом:

add("Center", btn1); // центр
add("East",   btn2); // восток
add("West",   btn3); // запад
add("North", btn4); // север
add("South", btn5); // юг

Здесь мы добавили в окно контейнера компоненты btn1, …, btn5. При этом компонент btn1 располагается в центре окна контейнера, а остальные компоненты — по бокам. Размеры компонентов изменяются таким образом, чтобы они полностью заполняли контейнер.

Заметим, что Вы не обязаны каждый раз добавлять в контейнер именно пять компонентов и задействовать при этом все возможные направления.

Режим CardLayout

Режим размещения CardLayout предназначен для поочередного размещения нескольких компонентов в одном контейнере (например, класса Panel).

При добавлении компонента в контейнер необходимо передать его имя методу add через первый параметр, например:

picFrame pf;
pf = new picFrame();
add("pic0", pf);

Остальные компоненты добавляются аналогичным образом.

В классе CardLayout предусмотрено несколько методов, предназначенных для выбора отображаемого компонента. Эти методы перечислены в таблице 9-1.

Таблица 9-1. Методы для выбора компонента

Метод

Компонент для отображения

first

Первый

last

Последний

next

Следующий

previous

Предыдущий

show

Произвольный, заданный своим именем

Всем указанным методам, кроме метода show, передается через единственный параметр ссылка на родительский контейнер, в котором выполняется размещение. Методу show через второй параметр дополнительно передается имя компонента (как строка класса String).

Режим GridBagLayout

Последний режим размещения системы LayoutManager — это режим GridBagLayout. Он считается наиболее трудным, однако по сравнению с другими режимами он очень гибкий. В ряде случаев Вам просто не обойтись без него.

Так же как и рассмотренный нами ранее режим GridLayout, режим GridBagLayout предполагает размещение компонентов в ячейках некоторой таблицы заданной размерности. Вот наиболее важные отличия между этими режимами:

·       в режиме GridLayout размещаемые компоненты изменяют свои размеры таким образом, чтобы заполнить ячейки таблицы, в которых они располагаются. Режим GridBagLayout позволяет контролировать этот процесс, причем при необходимости Вы можете задать стратегию такого изменения или отказаться от него вовсе;

·       в режиме GridLayout каждый компонент занимает только одну ячейку. Что же касается режима GridBagLayout, то здесь компоненты могут занимать несколько смежных ячеек в строках или столбцах;

·       при изменении размеров контейнера во время работы приложения при использовании режима GridLayout все компоненты неизбежно изменяют свои размеры. Это далеко не всегда удобно. В режиме GridBagLayout Вы можете управлять стратегией изменения размеров компонентов или отказаться от такого изменения.

Режим размещения компонентов GridBagLayout удобен для создания диалоговых панелей, содержащих такие компоненты, как текстовые поля редактирования, переключатели, кнопки и т. д.

Выбирая соответствующим образом параметры размещения отдельных компонентов путем заполнения соответствующих полей класса GridBagConstraints, можно создавать панели, напоминающие по своему внешнему виду и поведению стандартные диалоговые панели Windows или других операционных систем с графическим интерфейсом. При этом можно добиться, чтобы размеры компонентов и их взаимное расположение не изменялись при корректировке размеров окна контейнера. Это невозможно при работе в других режимах размещения, таких, как FlowLayout или GridLayout.

Как пользоваться режимом размещения GridBagLayout?

Схема достаточно проста.

Прежде всего Вы должны создать объект класса GridBagLayout при помощи конструктора и выбрать его, как это показано ниже:

GridBagLayout gbl = new GridBagLayout();
setLayout(gbl);

Далее Вам нужно создать объект класса GridBagConstraints, поля которого будут определять параметры размещения отдельных компонентов:

GridBagConstraints c =  new GridBagConstraints();

Далее Вам нужно задать значения полей объекта класса GridBagConstraints, например, так (позже мы расскажем о назначении отдельных полей):

c.anchor = GridBagConstraints.NORTH;
c.fill = GridBagConstraints.NONE;
c.gridheight = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(40, 0, 0, 0);
c.ipadx = 0;
c.ipady = 0;
c.weightx = 0.0;
c.weighty = 0.0;

Подготовив объект класса GridBagConstraints, Вам нужно установить его в системе Layout Manager методом setConstraints и добавить очередной компонент в окно контейнера методом add:

tf = new TextField(30);
gbl.setConstraints(tf, c);
add(tf);

Далее описанная процедура выполняется над всеми остальными добавляемыми компонентами, причем объект класса 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

Значение

Стратегия выделения места

GridBagConstraints.NONE

Компонент не изменяет своих размеров

GridBagConstraints.BOTH

Изменяется высота и ширина, причем таким образом, чтобы компонент занимал все отведенное для него пространство

GridBagConstraints.HORIZONTAL

Компонент растягивается по горизонтали

GridBagConstraints.VERTICAL

Компонент растягивается по вертикали

Поле anchor

Поле anchor задает выравнивание компонента внутри отведенного для него пространства. Он включается в работу, когда размеры компонента меньше размеров выделенного для него места.

Для поля anchor Вы можете указать значения, приведенные в таблице 9-3.

Таблица 9-3. Значения поля anchor

Значение

Направление выравнивания

GridBagConstraints.CENTER

По центру

GridBagConstraints.NORTH

Вверх

GridBagConstraints.SOUTH

Вниз

GridBagConstraints.EAST

Вправо

GridBagConstraints.WEST

Влево

GridBagConstraints.NORHEAST

Вверх вправо

GridBagConstraints.NORTHWEST

Вверх влево

GridBagConstraints.SOUTHEAST

Вниз вправо

GridBagConstraints.SOUTHWEST

Вниз влево

Поля weightx и weighty

Эти поля определяют стратегию изменения размеров компонента, отвечая за выделение пространства для столбцов (weightx) и строк (weighty).

Если записать в них нулевые значения, все добавленные компоненты займут место в центре контейнера и будут выровнены по центру (как по вертикали, так и по горизонтали).

Чтобы размеры компонента изменялись по горизонтали или вертикали, в поля weightx и weightx нужно записать значения от 0,0 до 1,0.

Если в столбце несколько компонентов, то его ширина определяется компонентом с максимальным значением weightx. Аналогичное утверждение верно и для строк.

Заметим, что дополнительное пространство добавляется к строкам и столбцам снизу и справа соответственно.

Поля ipadx и ipady

В полях ipadx и ipady Вы можете указать, что размеры компонента необходимо увеличить на заданное количество пикселов по горизонтали и вертикали соответственно.

Поле insets

Поле insets позволяет задать для компонента отступы от краев выделенной ему области. По умолчанию такие отступы отсутствуют.

В поле insets необходимо записать ссылку на объект класса Insets, созданную соответствующим конструктором. Этот конструктор имеет следующий прототип:

public Insets(
  int top,      // отступ сверху
  int left,   // -"- слева
  int bottom, // -"- снизу
  int right); // -"- справа

Аплет с формой

В качестве примера демонстрации режима размещения GridBagLayout приведем исходные тексты аплета GridBag .с формой

В окне нашего аплета находится форма для регистрации посетителей сервера, в которой нужно заполнить несколько стандартных полей (рис. 9-1).

Рис. 9-1. Форма для регистрации посетителей

Если Вы заполните форму и щелкнете кнопку OK, на экране появится диалоговая панель, отображающая введенные значения в окне многострочного редактора (рис. 9-2). Кнопка Cancel удаляет введенную информацию.

Рис. 9-2. Отображение введенной информации

Как в окне основного аплета, так и в окне диалоговой панели мы установили режим размещения компонент GridBagLayout.

Перейдем к рассмотрению исходного текста аплета. Полностью он приведен в листинге 9-1.

Листинг 9-1 Вы найдете в файле ch09/Gridbag/GridBag.java на прилагаемом к книге компакт-диске.

Главный класс аплета GridBag

Главный класс аплета GridBag создан на базе класса Applet и реализует интерфейс ActionListener:

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class GridBag2 extends Applet
  implements ActionListener
{
  . . .
}

Данный интерфейс необходим для обработки событий, вызываемых нажатием кнопок.

В главном классе мы определили несколько полей, предназначенных для хранения ссылок на компоненты — текстовые поля, метки класса Label и кнопки:

TextField tfFirstName;
Label lbFirstName;
 
TextField tfMiddleName;
Label lbMiddleName;
 
TextField tfLastName;
Label lbLastName;
 
TextField tfZip;
Label lbZip;
 
TextField tfCountry;
Label lbCountry;
 
Button btnOK;
Button btnCancel;

Метод init

Этот метод получает управление при инициализации аплета. Он создает и размещает все компоненты в окне аплета, а затем регистрирует обработчики событий от кнопок.

Компоненты создаются обычным образом при помощи соответствующих конструкторов:

tfFirstName = new TextField(20);
lbFirstName = new Label("First name");
    
tfMiddleName = new TextField(20);
lbMiddleName = new Label("Middle name");
    
tfLastName = new TextField(20);
lbLastName = new Label("Last name");
    
tfZip = new TextField(10);
lbZip = new Label("ZIP code");
    
tfCountry = new TextField(8);
lbCountry = new Label("Country");
    
btnOK = new Button("OK");
btnCancel = new Button("Cancel");

Далее мы устанавливаем режим размещения GridBagLayout и создаем объект класса GridBagConstraints, необходимый для задания параметров размещения отдельных компонент:

GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c =
  new GridBagConstraints();
setLayout(gbl);

Ниже мы расскажем о выборе этих параметров.

Поле First name

Заполнение параметров и размещение этого поля выполняется следующим образом:

c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.NONE;
c.gridheight = 1;
c.gridwidth = 1;
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(10, 10, 0, 0);
    
gbl.setConstraints(tfFirstName, c);
add(tfFirstName);

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

Параметр fill имеет значение GridBagConstraints.NONE, а значит, при корректировке размеров контейнера размеры поля изменяться не будут.

Так как значение полей gridheight и gridwidth равно единице, поле занимает одну ячейку таблицы.

Поля gridx и gridy содержат значение GridBagConstraints.RELATIVE, поэтому добавление поля выполняется в направлении слева направо и сверху вниз.

И наконец, поле insets задает отступы сверху и слева, равные 10 пикселам.

Метка для поля First name

Для этой метки мы используем те же параметры, что и для самого поля:

gbl.setConstraints(lbFirstName, c);
add(lbFirstName);

В результате метка займет положение справа от поля First name.

Кнопка OK

Вот как заполняются параметры размещения для кнопки OK:

c.gridwidth = GridBagConstraints.REMAINDER;
c.ipadx = 32;
gbl.setConstraints(btnOK, c);
add(btnOK);

Как видите, параметр gridwidth имеет значение, равное GridBagConstraints.REMAINDER. В результате кнопка будет последним компонентом в первой строке. Ее размеры останутся неизменными при корректировке размеров контейнера, так как поле fill имеет значение GridBagConstraints.NONE.

Чтобы несколько увеличить размеры кнопки OK по горизонтали, мы задали в поле ipadx значение, равное 32 пикселам.

Поле Middle name и метка этого поля

Перед добавлением поля и его метки мы восстанавливаем параметры ipadx и gridwidth, измененные на предыдущем этапе:

c.ipadx = 0;
c.gridwidth = 1;
gbl.setConstraints(tfMiddleName, c);
add(tfMiddleName);
gbl.setConstraints(lbMiddleName, c);
add(lbMiddleName);

В результате поле Middle name будет размещено в первой ячейки второй строки.

Кнопка Cancel

Эта кнопка размещается так:

c.gridwidth = GridBagConstraints.REMAINDER;
c.ipadx = 10;
c.weightx = 1.0;
gbl.setConstraints(btnCancel, c);
add(btnCancel);

Здесь в поле gridwidth мы указали значение GridBagConstraints.REMAINDER, поэтому кнопка Cancel будет последней во второй строке.

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

Поле Last name

Это поле добавляется в начало третьей строки:

c.ipadx = 0;
c.gridwidth = 1;
c.weightx = 0.0;
gbl.setConstraints(tfLastName, c);
add(tfLastName);

Здесь мы просто восстанавливаем параметры, аналогичные параметрам поля Middle name, расположенного в начале второй строки.

Метка поля Last name

Эта метка занимает всю оставшуюся часть третьей строки, так как в поле gridwidth мы задали значение GridBagConstraints.REMAINDER:

c.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(lbLastName, c);
add(lbLastName);

Поле ZIP

При размещении этого поля мы восстанавливаем значение параметра gridwidth, измененное на предыдущем этапе:

c.gridwidth = 1;
gbl.setConstraints(tfZip, c);
add(tfZip);

Метка поля ZIP

Для этого компонента мы выделяем всю оставшуюся часть строки, задавая в поле gridwidth значение GridBagConstraints.REMAINDER:

c.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(lbZip, c);
add(lbZip);

Поле Country

Это поле занимает одну ячейку последней строки, поэтому в поле gridwidth мы записали значение 1:

c.gridwidth = 1;
gbl.setConstraints(tfCountry, c);
add(tfCountry);

Метка поля Country

Для этой метки мы установили следующие параметры:

c.weighty = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(lbCountry, c);
add(lbCountry);

Так как в поле weighty указано значение 1, для последней строки таблицы отводится все оставшееся снизу пространство контейнера. Если же записать сюда нулевое значение, все компоненты будут центрированы в окне контейнера по вертикали.

Перед завершением работы метод init регистрирует обработчики событий от кнопок:

btnOK.addActionListener(this);
btnCancel.addActionListener(this);

Метод actionPerformed

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

При щелчке кнопки OK метод actionPerformed получает строки из полей нашей формы и записывает их в текстовую переменную с именем s:

String s = "<Personal information>";
s = "First name: "
  + tfFirstName.getText() +
  "\nMiddle name: "
+ tfMiddleName.getText() +
  "\nLast name: "
   + tfLastName.getText() +
  "\nZIP code: "
    + tfZip.getText() +
  "\nCountry: "
       + tfCountry.getText();

Далее метод создает диалоговую панель класса AppletMsgBox (определенный в нашем приложении), передавая строку s соответствующему конструктору:

AppletMsgBox amsgbox;
amsgbox = new AppletMsgBox(s, "Information");
amsgbox.show();

Панель затем отображается методом show.

В том случае если Вы щелкнете кнопку Cancel, поля формы очистятся:

tfFirstName.setText("");
tfMiddleName.setText("");
tfLastName.setText("");
tfZip.setText("");
tfCountry.setText("");

Класс AppletMsgBox

Этот класс мы создали для отображения диалоговой панели с сообщением и кнопкой OK. Он образован на базе класса Frame и реализует интерфейс ActionListener:

class AppletMsgBox extends Frame
  implements ActionListener
{
  . . .
}

В классе AppletMsgBox определены два поля:

Button btnOK;
TextArea ta;

Первое из них хранит ссылку на кнопку, а второе — ссылку на многострочный редактор текста, в окне которого мы отображаем сообщение.

Конструктор класса AppletMsgBox

Конструктору класса AppletMsgBox передаются два параметра — строка сообщения и строка заголовка:

public AppletMsgBox(String msg, String title)
{
  . . .
}

Первым делом конструктор класса AppletMsgBox вызывает конструктор базового класса Frame, передавая ему строку заголовка title, и устанавливает размеры окна панели:

super(title);
setSize(400, 200);

Кнопка и редактор текста создаются обычным образом:

btnOK = new Button("OK");
ta = new TextArea(msg, 5, 40);
ta.setEditable(false);

Так как поле ta будет использоваться только для отображения сообщений, мы отменяем функцию редактирования, вызывая метод setEditable.

Далее мы устанавливаем режим размещения компонента GridBagLayout:

GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gbl);

Параметры размещения текстового поля выбираются следующим образом:

c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.BOTH;
c.gridheight = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = GridBagConstraints.RELATIVE;
c.gridy = GridBagConstraints.RELATIVE;
c.insets = new Insets(10, 0, 0, 0);
gbl.setConstraints(ta, c);
add(ta);

Задавая в поле anchor значение GridBagConstraints.CENTER, мы добиваемся центрирования редактора внутри выделенного ему пространства.

Так как поле fill имеет значение GridBagConstraints.BOTH, размеры окна редактора изменяются таким образом, чтобы он занимал всю поверхность выделенной ему ячейки таблицы.

Мы расположили окно редактора в одной ячейке (значение поля gridheight равно единице), причем так, чтобы оно заняло всю первую строку (в поле gridwidth мы установили значение GridBagConstraints.REMAINDER).

Что же касается кнопки, то ее размеры останутся постоянными при корректировке размеров контейнера:

c.fill = GridBagConstraints.NONE;
c.ipadx = 35;
gbl.setConstraints(btnOK, c);
add(btnOK);

Заметим, что мы не ввели значения в полях weightx и weighty. В результате при изменении размеров окна диалоговой панели и редактор, и кнопка остаются в его центре.

Перед завершением своей работы конструктор регистрирует обработчик событий для кнопки btnOK:

btnOK.addActionListener(this);

Метод actionPerformed

Этот метод скрывает окно диалоговой панели, когда пользователь щелкает кнопку OK:

public void actionPerformed(ActionEvent e)
{
  if(e.getSource().equals(btnOK))
  {
     setVisible(false);
  }
}

Классы Java для работы в сети

Язык программирования Java отличается богатой и продуманной библиотекой классов, предназначенной для решения самых разных задач — от создания архивов ZIP и работы с растровыми графическими изображениями до задач организации взаимодействия приложений Java через сеть.

В этом разделе кратко описаны основные классы сетевой библиотеки Java, которые потребуются нам для связи аплетов с расширениями сервера Web.

Класс InetAddress

Для работы с адресами IP в библиотеке классов Java предназначен класс InetAddress. С его помощью приложение определяет адрес IP локального узла, а также адреса удаленного узла, заданного своим доменным именем.

Вот прототипы наиболее интересных методов этого класса:

public static InetAddress getLocalHost();
public static InetAddress
getByName(String host);
public static InetAddress[]
getAllByName(String host);
public byte[] getAddress();
public String toString();
public String getHostName();
public boolean equals(Object obj);

Заметим, что создание объекта класса InetAddress выполняется не с помощью оператора new, а с применением статических методов getLocalHost, getByName и getAllByName.

Метод getLocalHost создает объект класса InetAddress для локального узла, то есть для той рабочей станции, на которой выполняется приложение Java:

InetAddress iaLocal;
iaLocal = InetAddress.getLocalHost();

В том случае, если Вас интересует удаленный узел сети, Вы можете создать для него объект класса 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);
lbName = new Label("Your name");
    
tfNumber = new TextField(20);
lbNumber = new Label("Credit card number");
    
tfExpiration = new TextField(20);
lbExpiration = new Label("Expiration date");
    
btnOK = new Button("OK");
btnCancel = new Button("Cancel");
    
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
    
setLayout(gbl);

Далее метод init устанавливает параметры размещения отдельных компонентов и добавляет их в окно аплета. При этом используется техника, примененная нами ранее в этой главе при создании аплета GridBag.

Отправка данных расширению сервера Web

Когда пользователь щелкает кнопку OK, управление передается методу actionPerformed. Рассмотрим выполняемые им действия.

Прежде всего, этот метод извлекает данные из компонентов, размещенных в окне аплета. Данные оформляются в виде текстовой строки параметров запуска расширения ISAPI с именем iscard.dll, как это показано ниже:

URL u;
try
{
  u = new URL("http://saturn/cgi-bin/iscard.dll?" +
     "Name=" + tfName.getText() +
     "&cc=" + tfNumber.getText() +
     "&expir=" + tfExpiration.getText() + "&");
  . . .
}
catch(Exception ioe)
{
  String serr=ioe.toString();
  amsgbox = new AppletMsgBox(serr, "Information");
  amsgbox.show();
}

При этом создается новый объект класса URL, представляющий собой адрес URL запускаемого расширения ISAPI. Все операции выполняются в блоке try-catch, так как при работе приложения могут возникать ошибки, связанные, например, с разрывом связи между браузером покупателя и сервером.

Далее мы создаем канал связи с расширением ISAPI как объект класса URLConnection:

URLConnection c;
c = u.openConnection();
c.setDoOutput(true);
c.setDoInput(true);

С помощью методов setDoOutput и setDoInput для канала разрешается выполнения операций вывода и ввода соответственно.

Чтобы принять данные от расширения ISAPI, мы создаем входной поток данных класса DataInputStream, основанный на потоке InputStream. Этот поток, в свою очередь, получен при помощи метода getInputStream:

DataInputStream is;
is = new DataInputStream(c.getInputStream());

Чтение из потока выполняется в цикле:

String str="";
String str1="";
while(true)
{
  str1 = is.readLine();
  if(str1 == null)
     break;
  str += str1;
}

Здесь просто накапливаются полученные данные в строке str.

По достижении конца потока мы его закрываем:

is.close();

После получения всех строк от расширения сервера Web мы отображаем содержимое строки str в диалоговой панели:

amsgbox = new AppletMsgBox(str, "Information");
amsgbox.show();

Размещение аплета в документе HTML

Аплет, выполняющий обмен данными с сервера Web, надо загружать именно с этого сервера, а не с какого-либо другого. Поэтому в документе HTML с аплетом правильно указывайте параметры тега <APPLET>:

<applet name="CreditCard" code="CreditCard"
codebase="http://www.YourServer.ru/AppletDir"
width="400" height="150" align="Top">
</applet>

Параметр 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);
strcat(szBuff, szTempBuf);

Таким образом, наше расширение ISAPI отправляет обратно аплету полученные от него параметры.

Содержимое буфера посылается аплету следующим образом:

if(!lpECB->ServerSupportFunction(lpECB->ConnID,
  HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))
{
  return HSE_STATUS_ERROR;
}

lpECB->dwHttpStatusCode = 200;
return HSE_STATUS_SUCCESS;

Создавая реальный проект, не забудьте выполнить сканирование строки 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?" +
  "Name=" + tfName.getText() +
  "&cc=" + tfNumber.getText() +
  "&expir=" + tfExpiration.getText() + "&");  

Для того чтобы серверный сценарий, расположенный на странице ccard.asp, получил информацию о кредитной карточке, при создании объекта класса URL мы добавляем к адресу страницы параметры, отделив их символом «?». При этом параметры отделяются друг от друга символом «&».

Чтобы загрузить страницу ccard.asp в окно браузера, мы создаем объект класса AppletContext, вызывая для этого метод getAppletContext:

AppletContext appletContext;
appletContext = getAppletContext();

Далее, пользуясь полученным контекстом, аплет загружает в окно браузера документ с адресом URL, подготовленным в переменной u:

if (u != null)
{
  appletContext.showDocument(u, "_self");
}

Для загрузки мы вызываем метод showDocument, передавая ему в качестве второго параметра имя окна для загрузки документа. Пользуясь этим параметром, аплет может загрузить документ в любой фрейм, в существующее или вновь созданное окно браузера. Параметр «_self» означает, что документ будет загружен в то же самое окно, где находится аплет.

Исходный текст страницы ccard.asp приведен в листинге 9-6.

Листинг 9-6 Вы найдете в файле ch09/CreditCard2/ccard.asp на прилагаемом к книге компакт-диске.

В нем мы получаем параметры, переданные аплетом, при помощи объекта Request, а затем отображаем их значения в динамически создаваемом документе HTML:

<p>Name = <%=Request("Name")(1)%>
<br>Credit card number = <%=Request("cc")(1)%>
<br>Expiration date = <%=Request("expir")(1)%>

В реальном проекте параметры можно, например, передать серверному элементу управления ActiveX для расшифровки и дальнейшей обработки. Таким образом, страница ASP станет удобным средством извлечения параметров из данных, отправленных посетителем Вашего сервера при помощи аплета Java.

 

[Назад] [Содержание] [Дальше]