Java -практические советы


         

Переход к Swing

В части 3 мы подробно рассмотрели возможности графической библиотеки AWT. Там же мы заметили, что в состав Java 2 SDK входит еще одна графическая библиотека, Swing, с более широкими возможностями, чем AWT. Фирма SUN настоятельно рекомендует использовать Swing, а не AWT, но, во-первых, Swing требует больше ресурсов, что существенно для российского разработчика, во-вторых, большинство браузеров не имеет в своем составе Swing. В-третьих, удобнее сначала познакомиться с библиотекой AWT, а уже потом изучать Swing.

Все примеры графических программ, приведенные в книге, будут выполняться методами библиотеки Swing после небольшой


переделки:

1. Добавьте в заголовок строку import javax.swing.*;.

2. Поменяйте Frame на JFrame, Applet на JApplet, Component нa JComponent, Panel на JPanei. He расширяйте свои классы от класса canvas, используйте jpanei или другие контейнеры Swing.

3. Замените компоненты AWT на близкие к ним компоненты Swing. Чаще всего надо просто приписать букву j: JButton, JcheckBox, JDialog, jList, JMenu и т. д. Закомментируйте временно строку import java.awt.*; и попробуйте откомпилировать программу. Компилятор покажет, какие компоненты требуют замены.

4. Включите в конструктор класса, расширяющего JFrame, строку Container с = getContentPane (); и располагайте все компоненты в контейнере с, Т. е. пишите c.add(), с.setLayout ().

5. Класс jFrame содержит средства закрытия своего окна, надо только настроить их. Вы можете убрать addwindowListener(...) и включить в конструктор обращение К методу setDefaultCloseQperation(JFrame.EXITJB_CLOSE).

6. В прямых подклассах класса jpanei замените метод paint о на paintcomponent () и удалите метод update о. Класс jpanei автоматически производит двойную буферизацию и надобности в методе update о больше нет. Уберите весь код двойной буферизации. В начало метода paintcomponent () включите обращение super.paintcomponent (g). Из подклассов классов JFrame, joialog, JAppiet метод paintcomponent () надо переместить в другие компоненты, например, JButton, JLabel, jpanei.

7. Используйте вместо класса image класс imageicon. Конструкторы этого класса выполнят необходимое преобразование. Класс imageicon автоматически применяет методы класса MediaTracker для ожидания окончания загрузки.

8. При создании апплетов расширением класса JAppiet не забывайте, что в классе Applet менеджером размещения по умолчанию служит класс FiowLayout, а в классе JAppiet менеджер размещения по умолчанию BorderLayout.

Пункты 4 и 6 требуют пояснения. Окно верхнего уровня в Swing, такое как JFrame, содержит корневую панель (root pane), на которой размещена слоеная панель (layered pane). Компоненты на слоеной панели можно размещать несколькими слоями, перекрывая друг друга. В одном из слоев находится панель содержимого (content pane) и строка меню (menu bar). Поверх самого верхнего слоя расположена прозрачная панель (glass pane). Поэтому нельзя просто поместить компонент в окно верхнего уровня. Его надо "положить" на какую-нибудь панель. Пункт 4 рекомендует размещать компоненты на панели содержимого.

Откомпилировав и запустив измененную программу, вы увидите, что ее внешний вид изменился, чаще всего не в лучшую сторону. Теперь надо настроить компоненты Swing. Библиотека Swing предоставляет для этого широчайшие возможности.

Архиватор jar

Для упаковки нескольких файлов в один архивный файл, со сжатием или без сжатия., в технологии Java разработан формат JAR. Имя архивного jar-файла может быть любым, но обычно оно получает расширение jar. Способ упаковки и сжатия основан на методе ZIP. Название JAR (Java ARchive) перекликается с названием известной утилиты TAR (Tape ARchive), разработанной в UNIX.

Отличие jar-файлов от zip-файлов только в том, что в первые автоматически включается каталог META-INF, содержащий несколько файлов с информацией об упакованных в архив файлах.

Архивные файлы очень удобно использовать в апплетах, поскольку весь архив загружается по сети сразу же, одним запросом. Все файлы апплета с байт-кодами, изображениями, звуковые файлы упаковываются в один или несколько архивов. Для их загрузки достаточно в теге <appiet> указать имена архивов в параметре archive, например:

<applet code = "MillAnim.class" archive = "first.jar, second.jar" 

width = "100%" height = "100%"></applet>

Основной файл MillAnim.class должен находиться в каком-либо из архивных файлов first.jar или second.jar. Остальные файлы отыскиваются в архивных файлах, а если не найдены там, то на сервере, в том же каталоге, что и HTML-файл. Впрочем, файлы апплета можно упаковать и в zip-архив, со сжатием или без сжатия.

Архивные файлы удобно использовать и в приложениях (applications). Все файлы приложения упаковываются в архив, например, appl.jar. Приложение выполняется прямо из архива, интерпретатор запускается с параметром -jar, например:

Java -jar appl.jar

Имя основного класса приложения, содержащего метод main (), указывается в файле MANIFEST.MF, речь о котором пойдет чуть ниже.

Архивные файлы удобны и просты для компактного хранения всей необходимой для работы программы информации. С файлами архива можно работать прямо из архива, не распаковывая их, с помощью классов пакета java.util.jar.

 

Создание архива

Jar-архивы создаются с помощью классов пакета java.util.jar или с помощью утилиты командной строки jar.

Правила применения утилиты jar очень похожи на правила применения утилиты tar. Набрав в командной строке слово jar и нажав клавишу <Enter>, вы получите краткое пояснение, показанное на рис. П.1.

В строке

jar {ctxu}[vfmOM] [jar-file] [manifest-file] [-C dir] files...

зашифрованы правила применения утилиты. Фигурные скобки показывают, что после слова jar и пробела надо написать одну из букв с, t, x или и. Эти буквы означают следующие операции:

с (create) — создать новый архив; t (table of contents) — вывести в стандартный вывод список содержимого архива; х (extract) — извлечь из архива один или несколько файлов; u (update) — обновить архив, заменив или добавив один или несколько файлов.

Рис. П.1. Правила употребления утилиты jar

После буквы, без пробела, можно написать одну или несколько букв, перечисленных в квадратных скобках. Они означают следующее:

v (verbose) — выводить сообщения о процессе работы с архивом в стандартный вывод; f (file) — записанный далее параметр jar-file показывает имя архивного файла; m (manifest) — записанный далее параметр manifest-file показывает имя файла описания; о (нуль) — не сжимать файлы, записывая их в архив;  м (manifest) — не создавать файл описания;

Параметр -i (index) предписывает создать в архиве файл INDEX.LIST. Он используется уже после формирования архивного файла.

После буквенных параметров-файлов через пробел записывается имя архивного файла jar-file, потом, через пробел, имя файла описания manifest-file, затем перечисляются имена файлов, которые надо занести в архив или извлечь из архива. Если это имена каталогов, то операция выполняется рекурсивно со всеми файлами каталога.

Перед первым именем каталога может стоять параметр -с. Конструкция -с dir означает, что на время выполнения утилиты jar текущим каталогом станет каталог dir. ,

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

Итак, в конце командной строки должно быть записано хотя бы одно имя файла или каталога. Если среди параметров есть буква f, то первый из этих файлов понимается как архивный jar-файл. Если среди параметров находится буква т, то первый файл понимается как файл описания (manifest-file). Если среди параметров присутствуют обе буквы, то имя архивного файла и имя файла описания должны идти в том же порядке, что и буквы f и т.

Если параметр f и имя архивного файла отсутствуют, то архивным файлом будет служить стандартный вывод.

Рис. П.2. Работа  с утилитой jar

Если параметр m и имя файла описания отсутствуют, то по умолчанию файл MANIFEST.MF, лежащий в каталоге META-INF архивного файла, будет содержать только номер версии.

На рис. П.2 показан процесс создания архива Base.jar в каталоге ch3. Сначала показано содержимое каталога ch3. Затем создается архив, в который включается файл Base.class и все содержимое подкаталога classes. Снова выводится содержимое каталога ch3. В нем появляется файл Base.jar. Потом выводится содержимое архива.

Как видите, в архиве создан каталог META-INF, а в нем файл MANIFEST.MF.

 

Файл описания MANIFEST.MF

Файл MANIFEST.MF, расположенный в каталоге META-INF архивного файла, предназначен для нескольких целей:

перечисления файлов из архива, снабженных цифровой подписью;  перечисления компонентов JavaBeans, расположенных в архиве;  указания имени основного класса для выполнения-приложения из архива;  записи сведений о версии пакета.

Вся информация сначала записывается в обычном текстовом файле с любым именем, например, manif. Потом запускается утилита jar, в которой этот файл указывается как значение параметра т, например:

jar cmf manif Base.jar classes Base.class

Утилита проверяет правильность записей в файле manif и переносит их в файл MANIFEST.MF, добавляя свои записи.

Файл описания manif должен быть написан по строгим правилам, изложенным в спецификации JAR File Specification. Ее можно найти в документации Java 2 SDK, в файле docs\guide\jar\jar.html.

Например, если мы хотим выполнять приложение с главным файлом Base.class из архива Base.jar, то файл manif должен содержать как минимум две строки:

Main-Class: Base

Первая строка содержит относительный путь к главному классу, но не к файлу, т. е. без расширения class. В этой строке каждый символ имеет значение, даже пробел. Вторая строка пустая — файл обязательно должен заканчиваться пустой строкой, точнее говоря, символом перевода строки '\n'.

После того как создан архив Base.jar, можно выполнять приложение прямо из него:

Java -jar Base.jar

 

Файл INDEX.LIST

Для ускорения поиска файлов и более быстрой их загрузки можно создать файл поиска INDEX.LIST. Это делается после создания архива. Утилита jar запускается еще раз с параметром -i, например:

jar -i Base.jar

После этого в каталоге META-INF архива появляется файл INDEX.LIST. На рис. П.З представлено, как создается файл поиска и как выглядит содержимое архива после его создания.

Рис. П.3. Создание  файла поиска

 

Компоненты JavaBeans

Многие программисты предпочитают разрабатывать приложения с графическим интерфейсом пользователя с помощью визуальных средств разработки: JBuilder, Visual Age for Java, Visual Cafe и др. Эти средства позволяют помещать компоненты в контейнер графически, с помощью мыши. На рис. П.4 показано окно JBuilder 4.

Левый нижний угол окна занимает форма, на которой размещаются компоненты. Сами компоненты показаны ярлыками на панели компонентов, расположенной выше формы. На рис. П.4 на панели компонентов виден ярлык компонента Button, показанный прямоугольником с надписью ОК, ярлык компонента checkbox, показанный квадратиком с крестиком, ярлык компонента checkboxGroup, обозначенный группой радиокнопок. Далее видны ярлыки компонентов choice, Label, List и другие компоненты AWT.

Чтобы поместить компонент в форму, надо щелкнуть кнопкой мыши на ярлыке компонента, перенести курсор мыши в нужное место формы и щелкнуть кнопкой мыши еще раз.

Затем с помощью мыши необходимо установить нужные размеры компонента. При этом можно передвинуть компонент на другое место. На рис. П.4 в форму помещена кнопка типа Button.


Рис. П.4. Окно JBuilder 4

Далее следует определить свойства (properties) компонента: текст, цвет текста и фона, вид курсора мыши, когда он появляется над компонентом Свойства определяются в окне свойств, расположенном справа от формы В левой колонке окна перечислены имена свойств, в правую колонку надо записать их значения. На рис. П.4 свойству label — надписи на кнопке — дано значение Выполнить. Это значение тут же появляется на кнопке

Потом можно задать обработку событий, открыв вторую страницу окна свойств.

Для того чтобы компонент можно было применять в таком визуальном средстве разработки как JBuilder, он должен обладать дополнительными качествами. У него должен быть ярлык, помещаемый на панель компонентов. Среди полей компонента должны быть выделены свойства (properties), которые будут показаны в окне свойств Следует определить методы доступа getXxx () /setXxx () /isXxx () к каждому свойству

Компонент, снабженный этими и другими необходимыми качествами, в технологии Java называется компонентом JavaBean. В него может входить один или несколько классов. Как правило, файлы этих классов упаковываются в jar-архив и отмечаются в файле MANIFEST.MF как Java-Bean: True.

Все компоненты AWT и Swing являются компонентами JavaBeans. Если вы создаете свой графический компонент по правилам, изложенным в части 3, то вы тоже получаете свой JavaBean. Но для того чтобы не упустить каких-либо важных качеств JavaBean, лучше использовать для их разработки специальные средства.

Фирма SUN поставляет набор необходимых утилит и классов BDK (Beans Development Kit) для разработки JavaBeans. Так же, как и SDK, этот набор хранится на сайте фирмы в виде самораспаковывающегося архива, например, bdkl_l-win.exe. Его содержимое распаковывается в один каталог, например, D:\bdkl.l. После перехода в каталог bdkl.l\beanbox\ и запуска файла run.bat (или run.sh в UNIX) открываются несколько окон утилиты Bean-Box, работа с которой ведется по тому же принципу, что и в визуальных средствах разработки.

Правила оформления компонентов JavaBeans изложены в спецификации JavaBeans API Specification, которую можно найти по адресу http:// java.sun.com/products/javabeans/docs/spec.html.

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

Специалисты пророчат большое будущее компонентному программированию. Они считают, что скоро будут созданы тысячи компонентов JavaBeans на все случаи жизни и программирование сведется к поиску в Internet нужных компонентов и сборке из них приложения.

 

Связь с базами данных через JDBC

Большинство информации хранится не в файлах, а в базах данных. Приложение должно уметь связываться с базой данных для получения из нее информации или для помещения информации в базу данных. Дело здесь осложняется тем, что СУБД (системы управления базами данных) сильно отличаются друг от друга и совершенно по-разному управляют базами данных. Каждая СУБД предоставляет свой набор функций для доступа к базам данных, и приходится для каждой СУБД писать свое приложение Но что делать при работе по сети, когда неизвестно, какая СУБД управляет базой на сервере?

Выход был найден корпорацией Microsoft, создавшей набор интерфейсов ODBC (Open Database Connectivity) для связи с базами данных, оформленных как прототипы функций языка С. Эти прототипы одинаковы для любой СУБД, они просто описывают набор действий с таблицами базы данных. В приложение, обращающееся к базе данных, записываются вызовы функций ODBC. Для каждой системы управления базами данных разрабатывается так называемый драйвер ODBC, реализующий эти функции для конкретной СУБД. Драйвер просматривает приложение, находит обращения к базе данных, передает их СУБД, получает от нее результаты и подставляет их в приложение. Идея оказалась очень удачной, и использование ODBC для работы с базами данных стало общепринятым.

Фирма SUN подхватила эту идею и разработала набор интерфейсов и классов, названный JDBC, предназначенный для работы с базами данных. Эти интерфейсы и классы составили пакет java.sqi, входящий в J2SDK Standard Edition, и его расширение javax.sql, входящее в J2SDK Enterprise Edition.

Кроме классов с методами доступа к базам данных для каждой СУБД необходим драйвер JDBC — промежуточная программа, реализующая методы JDBC. Существуют четыре типа драйверов JDBC.

1. Драйвер, реализующий методы JDBC вызовами функций ODBC. Это так называемый мост (bridge) JDBC-ODBC. Непосредственную связь с базой при этом осуществляет драйвер ODBC.

2. Драйвер, реализующий методы JDBC вызовами функций API самой СУБД.

3. Драйвер, реализующий методы JDBC вызовами функций сетевого протокола, независимого от СУБД. Этот протокол должен быть, затем, реализован средствами СУБД.

4. Драйвер, реализующий методы JDBC вызовами функций сетевого протокола СУБД.

Перед обращением к базе данных следует установить нужный драйвер, например, мост JDBC-ODBC:

try{

Class dr = sun.jdbc.odbc.JdbcOdbcDriver.class; 

}catch(ClassNotFoundException e){

System.err.println("JDBC-ODBC bridge not found " + e); 

}

Объект dr не понадобится в программе, но таков синтаксис. Другой способ установки драйвера показан в листинге П.1.

После того как драйвер установлен, надо связаться с базой данных. Методы связи описаны в интерфейсе connection. Экземпляр класса, реализующего этот интерфейс, можно получить одним из статических методов getConnection () класса DriverManager, например:

String url = "jdbc:odbc:mydb";

String login = "habib";

String password = "lnF4vb";

Connection qon = DriverManager.getConnection(url, login, password);

Обратите внимание на то, как формируется адрес базы данных url. Он начинается со строки "jdbc:", потом записывается подпротокол (subprotocol), в данном примере используется мост JDBC-ODBC, поэтому записывается "odbc:". Далее указывается адрес (subname) по правилам подпротокола, здесь просто имя локальной базы "mydb". Второй и третий аргументы — это имя и пароль для соединения с базой данных.

Если в вашей вычислительной системе установлен пакет javax.sql, то вместо класса DriverManager лучше использовать интерфейс DataSource.

Связавшись с базой данных, можно посылать запросы. Запрос хранится в объекте, реализующем интерфейс statement. Этот объект создается методом createstatement (), описанным в интерфейсе connection. Например:

Statement st = con.createStatement();

Затем запрос (query) заносится в этот объект методом execute () и потом выполняется методом getResultSet(). В простых случаях это можно сделать одним методом executeQuery (), например:

ResultSet rs = st.executeQuery("SELECT name, code FROM tbll");

Здесь из таблицы tbll извлекается содержимое двух столбцов name и code и заносится в объект rs класса, реализующего интерфейс ResultSet.

SQL-операторы INSERT, UPDATE, DELETE, CREATE TABLE и другие в простых случаях ВЫПОЛНЯЮТСЯ методом executeUpdate ().

Остается методом next () перебрать элементы объекта rs — строки полученных столбцов — и извлечь данные многочисленными методами getxxx () интерфейса ResultSet:

while (rs.next()){

emp[i] = rs.getString("name") ; 

num[i] = rs.getlnt("code");

i++; }

Методы интерфейса ResuitsetMetaData позволяют узнать количество полученных столбцов, их имена и типы, название таблицы, имя ее владельца и прочие сведения о представленных в объекте rs сведениях.

Если объект st получен методом

Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_OPDATABLE);

то можно перейти к предыдущему элементу методом previous (), к первому элементу — методом first о, к последнему — методом last о. Можно также изменять объект rs методами updatexxx () и даже изменять, удалять и добавлять соответствующие строки базы данных. Не все драйверы обеспечивают эти возможности, поэтому, надо проверить реальный тип объекта rs методами rs.getType() И rs.getConcurrency().

Интерфейс Statement расширен интерфейсом PreparedStatement, тоже позволяющим изменять объект ResultSet методами setxxxo.

Интерфейс Preparedstatement, в свою очередь, расширен интерфейсом caiiablestatement, в котором описаны методы выполнения хранимых процедур.

В листинге П.1 приведен типичный пример запроса к базе Oracle через драйвер Oracle Thin. Апплет выводит в окно браузера четыре поля ввода для адреса базы, имени и пароля пользователя, и запроса. По умолчанию формируется запрос к стартовой базе Oracle, расположенной на локальном компьютере. Результат запроса выводится в окно браузера.

Листинг П.1. Апплет, обращающийся к базе Oracle

import j ava.awt.*;

import java.awt.event.*;

import j ava.applet.*;

import java.util.*;

import j ava.sql.*;

public class JdbcApplet extends Applet

implements ActionListener, Runnable{ 

private TextField tfl, tf2, tf3; 

private TextArea ta; 

private Button bl, b2; 

private String url = "jdbc:oracle:thin:Slocalhost:1521:ORCL",

login = "scott", 

password = "tiger",

query = "SELECT * FROM dept"; 

private Thread th; 

private Vector results; 

public void init(){

setBackground(Color.white) ; 

try{

DriverManager.registerDriver(

new oracle.j dbc.driver.OracleDriver() ) ; 

}catch(SQLException e){

System.err.println(e); }

setLayout(null); 

setFont(new Font("Serif", Font.PLAIN, 14));

Label l1 = new Label("URL базы:", Label.RIGHT);

11.setBounds(20, 30, 70, 25); add(ll);

Label 12 = new Label("Имя:", Label.RIGHT);

12.setBounds(20, 60, 70, 25); add(12);

Label 13 = new Label("Пароль:", Label.RIGHT);

13.setBounds(20, 90, 70, 25); add(13);

tfl = new TextField(url, 30); 

tfl.setBounds(100, 30, 280, 25); add(tfl);

tf2 = new TextField(login, 30); 

tf2.setBounds(100, 60, 280, 25); add(tf2);

tf3 = new TextField(password, 30); 

tf3.setBounds(100, 90, 280, 25); add(tf3); 

tf3.setEchoChar('*');

Label 14 = new Label("Запрос:", Label.LEFT); 

14.setBounds(10, 120, 70, 25); add(14);

ta = new TextArea(query, 5, 50, TextArea.SCROLLBARS_NONE); 

ta.setBounds(10, 150, 370, 100); add(ta);

Button bl = new Button("Отправить"); 

bl.setBounds(280, 260, 100, 30); add(bl); 

b1.addActionListener(this);

}

public void actionPerformed(ActionEvent ae){ 

url = tfl.getText() ; 

login = tf2.getText(); 

password = tf3.getText(); 

query = ta.getText(); 

if (th == null){

th = new Thread(this); 

th. start () ; 

}

public void run(){ 

try{

Connection con =

DriverManager.getConnection(url, login, password); 

Statement st = con.createStatement(); 

ResultSet rs = st.executeQuery(query); 

ResultSetMetaData rsmd = rs.getMetaData();

// Узнаем число столбцов 

int n = rsmd.getColumnCount(); 

results = new Vector(); 

while (rs.nextOH String s = " ";

// Номера столбцов начинаются с 1! 

for (int i = 1; i <= n; i++)

s += " " + rs.getObject(i); 

results.addElement(s); }

rs.close(); 

st.close () ; 

con.closet); 

repaint(); 

}catch(Exception e){

System, err.println(e); 

}

repaint(); 

}

public void paint(Graphics g)( 

if (results == null){

g.drawstring("Can't execute the query", 5, 30); 

return; 

}

int у = 30, n = results.size(); 

for (int i = 0; i < n; i++)

g.drawString((String)results.elementAt(i), 5, у += 20); } }

Замечание по отладке

В главе 19 упоминалось, что для отладки сетевой программы удобно запустить и клиентскую, и серверную часть на одном компьютере, обращаясь к серверной части по адресу 127.0.0.1 или доменному имени localhost. He забывайте, что апплет может связаться по сети только с тем хостом, откуда он загружен. Следовательно, на компьютере должен работать Web-сервер. Если Web-сервер прослушивает порт 8080, то, чтобы загрузить HTML-страницу с апплетом, надо в браузере указывать адрес URL вида http://loca(host:8080/public/JdbcApplet.html . При этом учтите, что Web-сервер устанавливает свою иерархию каталогов, и каталог public на самом деле может быть каталогом usr/local/http/public или каким-нибудь другим.

Таким образом, JDBC позволяет проделать весь цикл работы с базой данных. Подробно со всеми возможностями JDBC можно познакомиться, прочитав спецификацию JDBC, имеющуюся в документации Java 2 SDK, в каталоге docs\guide\jdbc\spec\. Дополнения спецификации версии JDBC 2.0 изложены в каталоге docs\guide\jdbc\spec2\. В каталоге docs\guide\jdbc\getstart\ есть пособие по использованию JDBC.

 

Сервлеты

В главе 19 была упомянута технология CGI. Ее суть в том, что сетевой клиент, обычно браузер, посылает Web-серверу информацию вместе с указанием программы, которая будет обрабатывать эту информацию. Web-сервер, получив информацию, запускает программу, передает информацию на ее стандартный ввод и ждет окончания обработки. Результаты обработки программа отправляет на свой стандартный вывод, Web-сервер забирает их оттуда и отправляет клиенту. Написать программу можно на любом языке, лишь бы Web-сервер мог взаимодействовать с ней.

В технологии Java такие программы оформляются как сервлеты (servlets). Это название не случайно похоже на название "апплеты". Сервлет на Web-сервере играет такую же роль, что и апплет в браузере, расширяя его возможности.

Чтобы Web-сервер мог выполнять сервлеты, в его состав должна входить JVM и средства связи с сервлетами. Обычно все это поставляется в виде отдельного модуля, встраиваемого в Web-сервер. Существует много таких модулей: Resin, Tomcat, JRun, JServ. Они называются на жаргоне сервлетными движками (servlet engine).

Основные интерфейсы и классы, описывающие сервлеты, собраны в пакет javax.servlet. Расширения этих интерфейсов и классов, использующие конкретные особенности протокола HTTP, собраны в пакет javax.servlet.http. Еще два пакета— javax.servlet.jsp и javax.servlet.jsp.tagext — предназначены для связи сервлетов со скриптовым языком JSP (JavaServer Pages). Все эти пакеты входят в состав J2SDK Enterprise Edition. Они могут поставляться и отдельно как набор JSDK (Java Servlet Development Kit). Сервлет-ный движок должен реализовать эти интерфейсы.

Основу пакета javax.servlet составляет интерфейс servlet, частично реализованный в абстрактном классе GenericServiet и его абстрактном подклассе HttpServiet. Основу этого интерфейса составляют три метода:

init (Servietconfig config) — задает начальные значения сервлету, играет ту же роль, что и метод init () в апплетах; service(ServletRequest req, ServletResponse resp) — выполняет обработку поступившей сервлету информации req и формирует ответ resp; destroy () — завершает работу сервлета.

Опытный читатель уже понял, что вся работа сервлета сосредоточена в методе service о. Действительно, это единственный метод, не реализованный в классе GenericServiet. Достаточно расширить свой класс от класса . GenericServiet и реализовать в нем метод service (), чтобы получить собственный сервлет. Сервлетный движок, встроенный в Web-сервер, реализует интерфейсы ServletRequest и ServletResponse. Он Создает объекты req и resp, заносит всю поступившую информацию в объект req и передает этот объект сервлету вместе с пустым объектом resp. Сервлет принимает эти объекты как аргументы req и resp метода service о, обрабатывает информацию, заключенную в req, и оформляет ответ, заполняя объект resp. Движок забирает этот ответ и через Web-сервер отправляет его клиенту.

Основная информация, заключенная в объекте req, может быть получена методами read() и readLine() ИЗ байтового потока класса ServletlnputStream, непосредственно расширяющего класс inputstream, или из символьного потока класса BufferedReader. Эти потоки открываются, соответственно, методом req.getlnputStream() или методом req.getReader (). Дополнительные характеристики запроса можно получить многочисленными методами getxxxo объекта req. Кроме того, класс GenericServlet реализует массу методов getxxxo, позволяющих получить дополнительную информацию о конфигурации клиента.

Интерфейс servietResponse описывает симметричные методы для формирования ответа. Метод getoutputstreamo открывает байтовый поток класса ServletOutputStream, непосредственно расширяющего класс OutputStream. МеТОД getWriter () открывает символьный поток класса PrintWriter.

Итак, реализуя метод service!), надо получить информацию из входного потока объекта req, обработать ее и результат записать в выходной поток объекта resp.

Очень часто в объекте req содержится запрос к базе данных. В таком случае метод service о обращается через JDBC к базе данных и формирует ответ resp из полученного объекта ResultSet.

Протокол HTTP предлагает несколько методов передачи данных: GET, POST, PUT, DELETE. Для их использования класс GenericServlet расширен классом HttpServlet, находящимся В пакете javax.servlet.http. В этом классе есть методы для реализации каждого метода передачи данных:

doGet(HttpServletRequest req, HttpServletResponse resp) 

doPost(HttpServletRequest req, HttpServletResponse resp) 

doPut(HttpServletRequest req, HttpServletResponse resp) 

doDelete(HttpServletRequest req, HttpServletResponse resp)

Для работы с конкретным HTTP-методом передачи данных достаточно расширить свой класс от класса HttpServlet и реализовать один из этих методов. Метод service () переопределять не надо, в классе HttpServlet он только определяет, каким HTTP-методом передан запрос клиента, и обращается к соответствующему методу doXxxo. Аргументы перечисленных методов req и resp — это объекты, реализующие Интерфейсы HttpServletRequest и HttpServletResponse, расширяющие интерфейсы ServletRequest и ServietResponse, соответственно.

Интерфейс HttpServletRequest к тому же описывает множество методов getxxx (), позволяющих получить дополнительные свойства запроса req.

Интерфейс HttpServletResponse описывает методы addxxxO и setxxxo, дополняющие ответ resp, и статические константы с кодами ответа Web-сервера.

В листингах П.2 и П.З те же действия, что выполняет программа листинга П. 1, реализованы с помощью сервлета. Апплет теперь не нужен, в окно браузера выводится HTML-форма, описанная в листинге П.2.

Листинг П.2. HTML-форма запроса к базе данных

<html><head><title> JDBC Servlet</title></head> 

<body>

<form method = "POST" action = "/servlet/JdbcServlet"> 

<pre>

URL базы: <input type = "text" size = "40" name = "url"

value = "jdbc:oracle:thin:@localhost:1521:ORCL"> 

Имя: <input type = "text" size = "40" name = "login"

value = "scott"> 

Пароль: <input type = "password" size = "40" name = "password"

value = "tiger"> 

Запрос: 

<textarea rows = "5" cols "70" name = "query">

SELECT * FROM dept 

</textarea>

<input type = "submit" value = "Послать"> 

</pre> 

</form> 

</body> 

</html>

Сервлет получает из этой формы адрес базы url, имя login и пароль password пользователя, а также запрос query. Он обращается к базе данных Oracle через драйвер JDBC Oracle Thin, формирует полученный ответ в виде HTML-страницы и отправляет браузеру.

Листинг П.3 . Сервлет, выполняющий запрос к базе Oracle i

import java.io.*; 

import javax.servlet.*; 

import javax.servlet.http.*; 

import java.util.*; 

import j ava.sql.*;

public class JdbcServlet extends HttpServlet{ static) 

try{

DriverManager.registerDriver(

new oracle.j dbc.driver.OracleDriver()); 

)catch(SQLException e){

System.err.println(e) ; 

)

private Vector results = new Vector(); 

private int n;

public void doPost(HttpServletRequest req, HttpServletResponse resp)

throws IOException{ 

ServletExceptionf String url = req.getParameter("url") , 

login = req.getParameter("login") , 

password = req.getParameter("password") , 

query = req.getParameter("query");

// Задаем MIME-тип и кодировку для выходного потока pw 

resp.setContentType("text/html;charset=windows-1251"); 

PrintWriter pw = resp.getWriter(); 

try{

Connection con =

DriverManager.getConnection(url, login, password); 

Statement st = con.createStatement(); 

ResultSet rs = st.executeQuery(query); 

ResultSetMetaData rsmd = rs.getMetaData(); 

n = rsmd.getColumnCount(); while (rs.next()){ 

String s = " "; 

for (int i = 1; i <= n; i++)

s += " " + rs.getObject(i); 

results.addElement(s); }

rs.close(); 

st.close(); 

con.close(); 

}catch(SQLException e){

pw.println("From doPostf): " + e) ; 

}

pw.println("<html><head><title>Answers</titlex/head>"); 

рw.рrintln("<body><h2>Результаты 3anpoca</h2>"); 

n = results.size(); 

for (int i = 0; i < n; i++)

pw.println((String)results.elementAt(i) + "<br>"); 

pw.println("</bodyx/html>") ; 

pw.flush() ; 

pw.close () ; 

}

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

В системе J2SDKEE (Java 2 SDK Enterprise Edition) HTML-файл и сервлет образуют один Web-компонент. Они упаковываются в один файл с расширением war (web archive) и помещаются в так называемый Web-контейнер, управляемый Web-сервером, расширенным средствами J2SDKEE.

 

Java на сервере

Тенденция написания сетевых программ — побольше функций возложить на серверную часть программы и поменьше оставить клиентской части, сделав клиент "тонким", а сервер "толстым". Это позволяет, с одной стороны, использовать клиентскую часть программы на самых старых и маломощных компьютерах, а с другой стороны, облегчает модификацию программы — все изменения достаточно сделать только в одном месте, на сервере.

Сервер выполняет все больше функций, как говорят, служб или сервисов (services). Он и отправляет клиенту Web-страницы, и выполняет сервлеты, и связывается с базой данных, и обеспечивает транзакции. Такой многофункциональный сервер называется сервером приложений (application server). Большой популярностью сейчас пользуются серверы приложений WebLogic фирмы ВЕА Systems, IAS (Inprise Application Server) фирмы Borland, WebSphere фирмы IBM, OAS (Oracle Application Server). Важной характеристикой сервера приложений является способность расширять свои возможности путем включения новых модулей. Это удобно делать с помощью компонентов.

Фирма SUN Microsystems предложила свою систему компонентов EJB (Enterprise JavaBeans), включенную в Java 2 SDK Enterprise Edition. Подобно тому, как графические компоненты JavaBeans реализуют графический интерфейс пользователя, размещаясь в графических контейнерах, компоненты EJB реализуют различные службы на сервере, располагаясь в EJB-контейнерах. Этими контейнерами управляет EJB-сервер, включаемый в состав сервера приложений. В составе J2SDKEE EJB-сервер — это программа j2ee. Серверу приложений достаточно запустить эту программу, чтобы использовать компоненты EJB.

В отличие от JavaBeans у компонентов EJB не может быть графического интерфейса и ввода с клавиатуры. У них может отсутствовать даже консольный вывод. Контейнер EJB занимается не размещением компонентов, а созданием и удалением их объектов, связью с клиентами и другими компонентами, проверкой прав доступа и обеспечением транзакций.

Программы, оформляемые как EJB, могут быть двух типов: EntityBean и sessionBean. Они реализуют соответствующие интерфейсы из пакета javax.ejb. Первый тип EJB-компонентов удобен для создания программ, обращающихся к базам данных и выполняющих сложную обработку полученной информации. Компоненты этого типа могут работать сразу с несколькими клиентами. Второй тип EJB-компонентов более удобен для организации взаимодействия с клиентом. Компоненты этого типа бывают двух видов: сохраняющие свое состояние от запроса к запросу (stateful) и теряющие это состояние (stateless). Методами интерфейсов EntityBean и SessionBean контейнер EJB управляет поведением экземпляров класса. Если достаточно стандартного управления, то можно сделать пустую реализацию этих методов.

Кроме класса, реализующего интерфейс EntityBean или SessionBean, для создания компонента EJB необходимо создать еще два интерфейса, расширяющие интерфейсы вовноте и EjBObject. Первый интерфейс (home interface) служит для создания объекта EJB своими методами create (), для поиска и связи с этим объектом в процессе работы, и удаления его методом remove о. Второй интерфейс (remote interface) описывает методы компонента EJB. Интересная особенность технологии EJB — клиентская программа не образует объекты компонента EJB. Вместо этого она создает объекты home-и remote-интерфейсов и работает с этими объектами. Реализация home- и remote-интерфейсов, создание объектов компонента EJB и взаимодействие с ними остается на долю контейнера EJB.

Приведем простейший пример. Пусть мы решили обработать выборку из базы данных, занесенную в объект rs в сервлете листинга П.З, с помощью компонента EJB. Для простоты пусть обработка заключается в слиянии двух столбцов методом merge (). Напишем программу:

import java.rmi.RemoteException; 

import javax.ejb.*;

public class MergeBean implements SessionBean{ 

public String merge(String si, String s2){

String s = si + " " + s2; 

return s; 

}

// Выполняется при обращении к методу create() 

// интерфейса MergeHome. Играет роль конструктора класса 

public void ejbCreate() {}

// Пустая реализация методов интерфейса 

public void setSessionContext(SessionContext ctx){} 

public void ejbRemoveO {} 

public void ejbActivate()(} 

public void ejbPassivate(){} }

public interface MergeHome extends EJBHome{

// Реализуется методом ejbCreate() класса MergeBean 

Merge create)} throws CreateException, RemoteException;

}

public interface Merge extends EJBObject{ 

public double merge(String si, String s2)

throws RemoteException; 

}

В сервлете листинга П.3 создаем объекты типа MergeHome и Merge и обращаемся к их методам:

import j avax.servlet.*;

import j avax.servlet.http.*;

import java.io.*;

import j avax.naming.*;

import j avax.rmi.PortableRemoteObj ect;

public class JDBCServlet extends HttpServlet { 

MergeHome mh; 

Merge m;

// Следующие определения 

//.......

public void init(ServletConfig conf) throws ServletExceptionf 

try( // Поиск объекта merge, реализующего MergeHome 

InitialContext ic = new InitialContext(}; 

Object ref = ic.lookup("merge"); 

mh = (MergeHome)PortableRemoteObject.narrow(

ref, MergeHome.class); }catch(Exception e){

e.printStackTrace(); } 

public void doPost(HttpServletRequest req, HttpServletResponse resp)

throws IOException, ServletExceptionf 

// Начало метода

//........

m = mh.create(); 

String s = m.merge(si, s2); 

// и т. д. 

}

После компиляции получаем EJB-приложение, состоящее из пяти файлов: JdbcServlet.html, JdbcServlet.class, MergeBean.class, MergeHome.class и Merge.class. Осталось правильно установить (deploy) его в контейнер EJB. Файлы jdbcserviet.html и JdbcServlet.class надо упаковать в один war-файл, остальные файлы — в один jar-файл, потом оба получившихся файла упаковать в один ear-файл (Enterprise ARchive). Кроме того, надо создать еще файл описания установки (deployment descriptor) в формате XML и занести его в архив. В этот файл, в частности, записывается имя "merge", по которому компонент отыскивается методом lookup ().

Все это можно сделать утилитой depioytool, входящей в состав Java 2 SDK Enterprise Edition. Эта же утилита позволяет проверить работу приложения и установить его в контейнер EJB. Надо только предварительно запустить EJB-сервер командой j2ee.

Впрочем, все файлы EJB-приложения можно упаковать в один jar-файл.

Многие серверы приложений и средства разработки, такие как Borland JBuilder и IBM Visual Age for Java, имеют в своем составе утилиты для установки EJB-приложений.

EJB-приложение готово. Теперь достаточно вызвать в браузере HTML-файл и заполнить появившуюся в окне форму.

 

Заключение

Ну вот и все. Книга прочитана. Теперь вы можете уверенно чувствовать себя в мире Java, свободно разбираться в новых технологиях и создавать свои приложения на самом современном уровне. Конечно, в этой книге мы не смогли подробно разобрать все аспекты технологии Java, но, зная основные приемы, вы сможете легко освоить все ее новые методы. Успехов вам!