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

Архиватор jar

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

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

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

width = "100%" height = "100%">

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

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

java -jar appl.jar

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

При установке JDK на MS Windows автоматически создается ассоциация расширения имени файла jar с интерпретатором javaw, которая действует при двойном щелчке мыши по имени файла, а именно:

"C:\jre1.6.0 02\bin\javaw.exe" -jar "%1" %*

Если такой ассоциации нет, то ее легко создать средствами Windows.

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

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

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

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

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

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

jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files...

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

□ c (create) — создать новый архив;

□ t (table of contents) — направить в стандартный вывод список содержимого архива;

□ x (extract) — извлечь из архива один или несколько файлов;

□ u (update) — обновить архив, заменив или добавив один или несколько файлов.

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

□ v (verbose) — выводить сообщения о процессе работы с архивом в стандартный вывод;

□ f (file) — записанный далее параметр jar-file показывает имя архивного файла;

□ m (manifest) — записанный далее параметр manifest-file показывает имя файла описания;

□ 0 (нуль) — не сжимать файлы, записывая их в архив;

□ m (manifest) — не создавать файл описания;

□ e (entry) — используется при создании архива. Записанный далее параметр entry-point означает имя основного класса, содержащего метод main(), с которого начинается выполнение программы. Это имя будет занесено в создаваемый файл MANIFEST.MF (см. далее).

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

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

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

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

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

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

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

На рис. 25.2 показан процесс создания архива Base.jar в каталоге ch3.

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

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

Рис. 25.2. Работа с утилитой j ar

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

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

□ перечисления файлов из архива, снабженных цифровой подписью;

□ перечисления компонентов JavaBeans, расположенных в архиве;

□ указания имени основного класса для выполнения приложения из архива;

□ указания имени файла, содержащего изображение загрузочного окна;

□ записи сведений о версии пакета.

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

jar cmf manif Base.jar classes Base.class

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

Файл описания manif должен быть написан по строгим правилам, изложенным в спецификации JAR File Specification. Ее можно найти в документации Java SE, в файле docs/technotes/guides/j ar/j ar.html.

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

Main-Class: Base

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

Имя файла, например name.gif, с изображением для загрузочного окна (splash screen) указывается строкой

SplashScreen-Image: name.gif

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

java -jar Base.jar

Файл INDEX.LIST

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

jar -i Base.jar

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

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

Компоненты JavaBeans

Многие программисты предпочитают разрабатывать приложения с графическим интерфейсом пользователя с помощью визуальных средств разработки IDE (Integrated Development Environment), таких как NetBeans, IntelliJ IDEA, Eclipse, JBuilder и др. Эти средства позволяют помещать компоненты в контейнер графически, с помощью мыши.

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

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

Далее следует определить свойства (properties) компонента: текст, цвет текста и фона, вид курсора мыши, когда он появляется над компонентом. Свойства определяются в окне свойств, расположенном обычно справа от формы. Окно свойств появляется чаще всего при выборе пункта меню Properties из контекстного меню, появляющегося при щелчке правой кнопкой мыши на компоненте. В левой колонке окна свойств перечислены имена свойств, в правую колонку надо записать их значения.

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

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

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

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

Последние изменения правил создания JavaBeans и примеры даны в документации Java SE, в каталоге technotes/guides/beans.

Правила оформления компонентов JavaBeans изложены в спецификации JavaBeans API Specification, которую можно найти по адресу:

.

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

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

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

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

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

Компания Sun подхватила эту идею и разработала набор интерфейсов и классов, названный JDBC, предназначенный для работы с базами данных. Эти интерфейсы и классы составили пакет java.sql, а также пакет javax.sql и его подпакеты, входящие в Java SE.

"JDBC" — это не аббревиатура, а самостоятельное слово, хотя иногда расшифровывается как "Java Database Connectivity".

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

Существуют четыре типа драйверов JDBC:

□ драйвер, реализующий методы JDBC вызовами функций ODBC. Это так называемый мост (bridge) JDBC—ODBC. Непосредственную связь с базой при этом осуществляет драйвер ODBC, который должен быть установлен на той машине, на которой работает программа;

□ драйвер, реализующий методы JDBC вызовами функций API самой СУБД. В этом случае на машине должен быть установлен клиент СУБД;

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

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

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

try{

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

}catch(ClassNotFoundException e){

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

}

Объект dr не понадобится в программе, но таков синтаксис.

Другой способ установки драйвера показан в листинге 25.1.

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

String url = "jdbc:odbc:mydb";

String login = "admin";

String password = "1nF4vb";

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

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

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

Statement st = con.createStatement();

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

executeQuery(), например:

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

Здесь из таблицы tbl1 извлекается содержимое двух столбцов 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.getInt("code"); i++;

}

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

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

Statement st = con.createStatement(ResultSet.TYPE SCROLL SENSITIVE,

ResultSet.CONCUR_UPDATABLE) ;

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

rs.getConcurrency().

Интерфейс Statement расширен интерфейсом PreparedStatement, позволяющим создавать предварительно откомпилированный запрос, перед выполнением которого можно задавать аргументы методами setXxx ().

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

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

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

import java.awt.*; import java.awt.event.*; import java.applet.*; import java.util.*; import java.sql.*;

public class JdbcApplet extends Applet implements ActionListener, Runnable{ private TextField tf1, tf2, tf3; private TextArea ta; private Button b1, b2;

private String url = "jdbc:oracle:thin:@localhost: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.jdbc.driver.OracleDriver()); }catch(SQLException e){

System.err.println(e);

}

setLayout(null);

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

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

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

Label l2 = new Labe1(пИмя:п, Label.RIGHT);

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

Label l3 = new Labe1(пПароль:п, Label.RIGHT);

13. setBounds(20, 90, 70, 25); add(l3); tf1 = new TextField(url, 30);

tf1.setBounds(100, 30, 280, 25); add(tf1); 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 l4 = new Labe1(,,Запрос:,,, Label.LEFT);

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

ta = new TextArea(query, 5, 50, TextArea.SCROLLBARS NONE); ta.setBounds(10, 150, 370, 100); add(ta);

Button b1 = new Button(,,Отправить,,); b1.setBounds(280, 260, 100, 30); add(b1); b1.addActionListener(this);

}

public void actionPerformed(ActionEvent ae){ url = tf1.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.next()){

String s = " ";

// Номера столбцов начинаются с 1! for (int i = 1; i <= n; i++) s += " " + rs.getObject(i);

results.addElement(s);

}

rs.close(); st.close(); con.close(); 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 y = 30, n = results.size();

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

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

}

}

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

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

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

Таким образом, JDBC позволяет проделать весь цикл работы с базой данных. Подробно со всеми возможностями JDBC можно познакомиться, прочитав спецификацию JDBC, имеющуюся по адресу: http://java.sun.com/products/jdbc/ .

В документации Java SE, в каталоге technotes/guides/jdbc/, есть ссылки на пособия по использованию JDBC.

Вопросы для самопроверки

1. Почему в Java создан свой формат архивирования JAR?

2. Нужно ли распаковывать jar-архивы для использования классов, содержащихся в них?

3. Можно ли упаковывать апплеты в j ar-архив?

4. Куда надо помещать jar-файлы для использования их в приложении?

5. Что такое JavaBeans: классы, интерфейсы, пакеты, правила оформления классов?

6. Что должно быть в классе, называемом JavaBean?

7. Где применяются JavaBeans?

8. Что такое JDBC?

9. Какие существуют типы драйверов JDBC?

10. Какие фирмы разрабатывают драйверы JDBC?

ГЛАВА 26