Учим Java. Апплеты. Взаимодействие с CGI
Учим Java. Апплеты. Взаимодействие с CGI Одно из самых серьезных ограничений, накладываемых на работу Java-апплетов, это запрет на чтение/запись файлов на диск. Здесь подразумевается диск локального компьютера/сервера. Казалось, это напрочь выбивает почву из-под ног Java-апплетов, потому что с их помощью мы не сможем организовать ни счетчик, ни форум, ни статистику, ни голосование. Однако не все потеряно. В качестве "компенсации" в Java была реализована возможность взаимодействия с CGI-приложениями.
Что сие означает? Все очень просто. Как многим, я надеюсь, уже известно, CGI (Common Gateway Interface)-приложениями могут быть любые программы, скрипты на языках программирования, которые умеют работать со стандартным устройством ввода и стандартным устройством вывода (STDIN, STDOUT). С помощью протокола CGI они могут принимать параметры и возвращать результат запроса. Поскольку в роли CGI-приложения может выступать практически любое приложение, то естественно, что оно вправе выполнять не только чтение/запись на диск, но и совершать действия, которые соответствуют правам пользователя этой программы.
Собственно, каким образом можно организовать мостик между Java-апплетом и CGI? И каким образом эта связь осуществляется? В этом нам помогут классы URL и URLConnection из стандартной библиотеки классов Java. Их методами getInputStream() и getOutpubStream() мы можем открывать каналы между CGI-приложением и Java-апплетом для получения результата от CGI, предварительно отправив в канал записи параметров для CGI-приложения. Передача параметров осуществляется эмуляцией метода POST. Т.е. параметры в CGI-приложении будут считываться с STDIN (стандартного потока ввода).
Умея связывать свои Java-апплеты с CGI-приложениями, мы можем реально иметь посредника, который будет выполнять чтение/запись данных на диск. Единственная проблема — это необходимость уметь писать эти самые CGI-приложения. Но я не считаю это невыполнимой задачей, тем более что вам не нужно учить еще какой-нибудь язык программирования, достаточно того, что вы будете знать Java. И все же я твердо убежден, что легче, удобнее и приятнее программировать CGI на Perl. Но это мое личное мнение.
Перед тем как приступать к практической части, логично было бы небольшое отступление: а зачем нам вообще эти Java-ап-
плеты, если все равно придется прибегнуть к написанию CGI? Дело в том, что при использовании CGI-скриптов вам приходится перегружать всю страницу заново, чтобы увидеть результаты работы того или иного скрипта. Это не всегда удобно. В случае же с Java-апплетами перегружаться ничего не будет. Апплеты собственными силами (на стороне клиента) будут работать с CGI, и вы не будете видеть, как происходит их связь, потому что страница не будет перегружаться, если только этого не захочет автор апплета. Иными словами, при обращении апплетов к CGI-приложениям страница не перегружается, вот и все.
Итак, поставим задачу для того, чтобы увидеть, как вышесказанное реализуется на примере. Для этого можно рассмотреть две задачи. Первая — это счетчик. А вторая — голосование с автоматическим построением графика. Голосования я планирую рассмотреть в отдельной статье, потому что это относительно трудоемкий процесс. Посему давайте напишем свой Java-счетчик. Здесь мы не будем его украшать, этим вы можете заняться сами. Остановимся исключительно на интересующем нас взаимодействии.
Правильнее было бы начать с программирования CGI. Не буду останавливаться на мелочах, просто приведу часть кода, который будет работать с Java-апплетом.
#!/usr/bin/perl -w
# CGI-application, 4 Java-counter by Alexey Litvinuke
use strict;
read(STDIN,$str,1);
if ($str eq "1")
{
open(FILE, "counter");
$counter = <FILE> ;
close(FILE);
}
print "Content-type: text/plain\n\n";
print $counter;
Этот скрипт всего-навсего считывает значение счетчика из файла и выводит это число на стандартное устройство вывода (STDOUT), при условии, что переданный скрипту параметр a равен 1. Параметр a апплет передает CGI-приложению методом POST, т.е. записывает его в канал записи, связанный с нашим CGI-скриптом. Для увеличения значения этого счетчика необходимо писать дополнительный код, но нам это не интересно.
Сейчас, когда у нас есть рабочая часть CGI, можно приступать к реализации Java-апплета. Как уже упоминалось ранее, связь между апплетом и CGI-приложением осуществляется при помощи классов URL и URLConnection.
Для того чтобы можно было воспользоваться этими классами, необходимо включить набор классов java.net.*. Вместе с тем нам понадобятся также пакеты для работы с потоками ввода/вывода. В общем, подключим необходимые нам пакеты:
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
Перед тем как приступить к написанию кода дальше, необходимо познакомиться с еще одним методом класса Applet — init(). Этот метод используется для выполнения подготовительных (если так можно сказать) действий, т.е. какой-либо инициализации.
Объявим основной класс программы и унаследуем свойства и методы класса Applet:
public class Java2CGIEx extends Applet
{
И объявим необходимые свойства (глобальные переменные).
URL URL_CGI;
URLConnection c;
String counter_value;
После этого переопределим (перегрузим) метод init():
public void init()
{
try
{
URL_CGI = new URL("http://127.0.0.1/cgi-bin/counter.pl");
c = URL_CGI.openConnection();
PrintStream oStream = new PrintStream(c.getOutputStream());
oStream.println("1");
oStream.close();
DataInputStream iStream = new DataInputStream(c.getInputStream());
counter_value = iStream.readLine();
iStream.close();
}
catch (IOException e) showStatus("Error"+e.toString());
}
При написании этого метода были использованы конструкции try {} catch {}. Они носят название исключений, описание которых я планирую вынести в отдельную статью. Сейчас же важно понимать смысл: в случае возникновения какой-нибудь аварийной ситуации при выполнении кода блока try будут выполнены операторы из catch.
Для того чтобы открыть потоки ввода/вывода, мы использовали два новых класса: PrintStream и DataInputStream. Конструкторы этих классов принимают в качестве параметра экземпляры объектов потоков ввода и вывода, которые возвращают методы класса URLConnetion getInputStream() и getOutputStream(). После их создания мы выполняем необходимые нам действия по записи/чтению и закрываем их методом close(). Естественно, по отдельности.
Работа с потоками в Java очень походит на работу с сокетами. В результате работы этого кода значение счетчика будет иметь переменная counter_value.
После этого мы переопределяем метод pain(), откуда и выводим значение счетчика на экран.
public void pain(Graphics g)
{
// просто выводим значение счетчика
g.drawString(counter_value, 10, 10)
}
После того, как апплет готов и скомпилирован, напишем HTML-документ, в который и включим наш апплет. Он будет примерно следующего содержания:
<html>
<head> <title> Пример взаимодействия Java и CGI-приложений</title> </head>
<body>
<center> Пример апплета</center>
<applet name="Java2CGIEx"
code="Java2CGIEx.class"
width="300"
height="200"
align="Top"> </applet>
</body>
</html>
Как результат в окне апплета будет выведена строка с текущим значением счетчика.
Итак, подводя итоги, можно выделить замечательную возможность Java-апплетов работать с CGI-приложениями. Это открывает невообразимые просторы для творчества программистам: от онлайновых многопользовательских игр до больших и сложных апплетов, использующих базы данных.
Алексей Литвинюк (с)
Litvinuke@tut.by
www.litvinuke.hut.ru
Что сие означает? Все очень просто. Как многим, я надеюсь, уже известно, CGI (Common Gateway Interface)-приложениями могут быть любые программы, скрипты на языках программирования, которые умеют работать со стандартным устройством ввода и стандартным устройством вывода (STDIN, STDOUT). С помощью протокола CGI они могут принимать параметры и возвращать результат запроса. Поскольку в роли CGI-приложения может выступать практически любое приложение, то естественно, что оно вправе выполнять не только чтение/запись на диск, но и совершать действия, которые соответствуют правам пользователя этой программы.
Собственно, каким образом можно организовать мостик между Java-апплетом и CGI? И каким образом эта связь осуществляется? В этом нам помогут классы URL и URLConnection из стандартной библиотеки классов Java. Их методами getInputStream() и getOutpubStream() мы можем открывать каналы между CGI-приложением и Java-апплетом для получения результата от CGI, предварительно отправив в канал записи параметров для CGI-приложения. Передача параметров осуществляется эмуляцией метода POST. Т.е. параметры в CGI-приложении будут считываться с STDIN (стандартного потока ввода).
Умея связывать свои Java-апплеты с CGI-приложениями, мы можем реально иметь посредника, который будет выполнять чтение/запись данных на диск. Единственная проблема — это необходимость уметь писать эти самые CGI-приложения. Но я не считаю это невыполнимой задачей, тем более что вам не нужно учить еще какой-нибудь язык программирования, достаточно того, что вы будете знать Java. И все же я твердо убежден, что легче, удобнее и приятнее программировать CGI на Perl. Но это мое личное мнение.
Перед тем как приступать к практической части, логично было бы небольшое отступление: а зачем нам вообще эти Java-ап-
плеты, если все равно придется прибегнуть к написанию CGI? Дело в том, что при использовании CGI-скриптов вам приходится перегружать всю страницу заново, чтобы увидеть результаты работы того или иного скрипта. Это не всегда удобно. В случае же с Java-апплетами перегружаться ничего не будет. Апплеты собственными силами (на стороне клиента) будут работать с CGI, и вы не будете видеть, как происходит их связь, потому что страница не будет перегружаться, если только этого не захочет автор апплета. Иными словами, при обращении апплетов к CGI-приложениям страница не перегружается, вот и все.
Итак, поставим задачу для того, чтобы увидеть, как вышесказанное реализуется на примере. Для этого можно рассмотреть две задачи. Первая — это счетчик. А вторая — голосование с автоматическим построением графика. Голосования я планирую рассмотреть в отдельной статье, потому что это относительно трудоемкий процесс. Посему давайте напишем свой Java-счетчик. Здесь мы не будем его украшать, этим вы можете заняться сами. Остановимся исключительно на интересующем нас взаимодействии.
Правильнее было бы начать с программирования CGI. Не буду останавливаться на мелочах, просто приведу часть кода, который будет работать с Java-апплетом.
#!/usr/bin/perl -w
# CGI-application, 4 Java-counter by Alexey Litvinuke
use strict;
read(STDIN,$str,1);
if ($str eq "1")
{
open(FILE, "counter");
$counter = <FILE> ;
close(FILE);
}
print "Content-type: text/plain\n\n";
print $counter;
Этот скрипт всего-навсего считывает значение счетчика из файла и выводит это число на стандартное устройство вывода (STDOUT), при условии, что переданный скрипту параметр a равен 1. Параметр a апплет передает CGI-приложению методом POST, т.е. записывает его в канал записи, связанный с нашим CGI-скриптом. Для увеличения значения этого счетчика необходимо писать дополнительный код, но нам это не интересно.
Сейчас, когда у нас есть рабочая часть CGI, можно приступать к реализации Java-апплета. Как уже упоминалось ранее, связь между апплетом и CGI-приложением осуществляется при помощи классов URL и URLConnection.
Для того чтобы можно было воспользоваться этими классами, необходимо включить набор классов java.net.*. Вместе с тем нам понадобятся также пакеты для работы с потоками ввода/вывода. В общем, подключим необходимые нам пакеты:
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
Перед тем как приступить к написанию кода дальше, необходимо познакомиться с еще одним методом класса Applet — init(). Этот метод используется для выполнения подготовительных (если так можно сказать) действий, т.е. какой-либо инициализации.
Объявим основной класс программы и унаследуем свойства и методы класса Applet:
public class Java2CGIEx extends Applet
{
И объявим необходимые свойства (глобальные переменные).
URL URL_CGI;
URLConnection c;
String counter_value;
После этого переопределим (перегрузим) метод init():
public void init()
{
try
{
URL_CGI = new URL("http://127.0.0.1/cgi-bin/counter.pl");
c = URL_CGI.openConnection();
PrintStream oStream = new PrintStream(c.getOutputStream());
oStream.println("1");
oStream.close();
DataInputStream iStream = new DataInputStream(c.getInputStream());
counter_value = iStream.readLine();
iStream.close();
}
catch (IOException e) showStatus("Error"+e.toString());
}
При написании этого метода были использованы конструкции try {} catch {}. Они носят название исключений, описание которых я планирую вынести в отдельную статью. Сейчас же важно понимать смысл: в случае возникновения какой-нибудь аварийной ситуации при выполнении кода блока try будут выполнены операторы из catch.
Для того чтобы открыть потоки ввода/вывода, мы использовали два новых класса: PrintStream и DataInputStream. Конструкторы этих классов принимают в качестве параметра экземпляры объектов потоков ввода и вывода, которые возвращают методы класса URLConnetion getInputStream() и getOutputStream(). После их создания мы выполняем необходимые нам действия по записи/чтению и закрываем их методом close(). Естественно, по отдельности.
Работа с потоками в Java очень походит на работу с сокетами. В результате работы этого кода значение счетчика будет иметь переменная counter_value.
После этого мы переопределяем метод pain(), откуда и выводим значение счетчика на экран.
public void pain(Graphics g)
{
// просто выводим значение счетчика
g.drawString(counter_value, 10, 10)
}
После того, как апплет готов и скомпилирован, напишем HTML-документ, в который и включим наш апплет. Он будет примерно следующего содержания:
<html>
<head> <title> Пример взаимодействия Java и CGI-приложений</title> </head>
<body>
<center> Пример апплета</center>
<applet name="Java2CGIEx"
code="Java2CGIEx.class"
width="300"
height="200"
align="Top"> </applet>
</body>
</html>
Как результат в окне апплета будет выведена строка с текущим значением счетчика.
Итак, подводя итоги, можно выделить замечательную возможность Java-апплетов работать с CGI-приложениями. Это открывает невообразимые просторы для творчества программистам: от онлайновых многопользовательских игр до больших и сложных апплетов, использующих базы данных.
Алексей Литвинюк (с)
Litvinuke@tut.by
www.litvinuke.hut.ru
Компьютерная газета. Статья была опубликована в номере 13 за 2002 год в рубрике программирование :: java