Оглавление
Сетевые приложения |
На примере аплета Form мы покажем, как приложения Java могут взаимодействовать с расширениями сервера Web, такими как программы CGI или приложения ISAPI.
В окне нашего аплета находится форма, содержащая два однострочных поля редактирования, кнопку и многострочное поле редактирования (рис. 5).
Рис. 5. Окно аплета Form
Эта форма предназначена для добавления записей в базу данных, содержащую электронные почтовые адреса. Заполнив поля имени и адреса E-Mail, пользователь должен нажать кнопку Send. При этом введенная информация будет передана расширению сервера CGI, который запишет ее в базу данных, а затем отправит обратно аплету. Сохраненные записи, полученные от программы CGI, аплет FORM отобразит в многострочном поле редактирования, как это показано на рис. 5.
Исходные тексты аплета Form представлены в листинге 5.
Листинг 5. Файл Form.java
import java.applet.*; import java.awt.*; import java.net.*; import java.io.*; import java.util.*;
public class Form extends Applet implements Runnable { private Thread m_store = null;
TextField txtName; TextField txtEMail; TextArea txta; Button btnGetText;
public void init() { Label lbName; Label lbEMail; Label lbPress;
lbName = new Label("Enter your name:"); lbEMail = new Label( "Enter your E-Mail address:");
add(lbName);
txtName = new TextField("Your name", 40); add(txtName);
add(lbEMail);
txtEMail = new TextField("your@email", 40); add(txtEMail);
btnGetText = new Button("Send!"); add(btnGetText);
txta = new TextArea(8, 65); add(txta);
setBackground(Color.yellow); }
public void paint(Graphics g) { setBackground(Color.yellow);
Dimension dimAppWndDimension = getSize(); g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); }
public boolean action(Event evt, Object obj) { Button btn;
if(evt.target instanceof Button) { btn = (Button)evt.target;
if(evt.target.equals(btnGetText)) { startTransaction(); } else return false; return true; } return false; }
void startTransaction() { m_store = new Thread(this); m_store.start(); }
public void stop() { if (m_store != null) { m_store.stop(); m_store = null; } }
public void run() { URL u; URLConnection c; PrintStream ps; DataInputStream is;
try { String szSourceStr = txtName.getText() + ", " + txtEMail.getText();
String szReceived; String szURL = "http://frolov/scripts/store.exe";
u = new URL(szURL); c = u.openConnection();
ps = new PrintStream( c.getOutputStream()); ps.println(szSourceStr); ps.close();
is = new DataInputStream( c.getInputStream());
szReceived = is.readLine(); is.close();
txta.appendText(szReceived + "\r\n"); repaint(); } catch (Exception ioe) { showStatus(ioe.toString()); stop(); } } }
Исходный текст документа HTML, который был подготовлен для нас системой Java Workshop, мы немного отредактировали, изменив параметр CODEBASE (листинг 6).
Листинг 6. Файл Form.tmp.html
<applet name="Form" code="Form.class" codebase="http://frolov/" width="500" height="200" align="Top" alt="If you had a java-enabled browser, you would see an applet here."> <hr>If your browser recognized the applet tag, you would see an applet here.<hr> </applet>
В этом параметре следует указать путь к каталогу, в котором располагается байт-код аплета.
При инициализации метод init создает все необходимые органы управления и добавляет их в окно аплета.
Когда пользователь заполняет форму и нажимает кнопку Send, обработчик соответствующего события вызывает метод startTransaction, запускающий процесс обмена данными с расширением сервера Web:
if(evt.target.equals(btnGetText)) { startTransaction(); }
Метод startTransaction, определенный в нашем приложении, создает и запускает на выполнение поток, который и будет взаимодействовать с программой CGI:
void startTransaction() { m_store = new Thread(this); m_store.start(); }
При этом в качестве отдельного потока, работающего одновременно с кодом аплета, выступает метод run. Именно в нем сосредоточена вся логика обмена данными с сервером Web.
Так как в процессе взаимодействия могут возникать различные исключения, мы предусмотрели их обработку при помощи блока try-catch:
URL u; URLConnection c; PrintStream ps; DataInputStream is;
try { . . . } catch (Exception ioe) { showStatus(ioe.toString()); stop(); }
Название возникшего исключения будет отображено в строке состояния браузера.
Теперь о том, что делает метод run после получения управления.
Первым делом он извлекает из однострочных текстовых полей имя и электронный адрес, объединяя их и записывая полученную текстовую строку в поле szSourceStr:
String szSourceStr = txtName.getText() + ", " + txtEMail.getText();
В строке szURL находится адрес URL программы CGI:
String szURL = "http://frolov/scripts/store.exe";
В реальном приложении этот адрес необходимо передавать аплету через параметр. Мы использовали непосредственное кодирование только для упрощения исходного текста.
На следующем этапе метод run создает для программы CGI объект класса URL и открывает с ним соединение:
u = new URL(szURL); c = u.openConnection();
Пользуясь этим соединением, метод run создает форматированный поток вывода, записывает в него строку имени и электронного адреса, а затем закрывает поток:
ps = new PrintStream(c.getOutputStream()); ps.println(szSourceStr); ps.close();
Переданные таким образом данные попадут в стандартный поток ввода программы CGI, откуда она их и прочитает.
Сделав это, программа CGI запишет в стандартный выходной поток строку ответа, которую необходимо прочитать в методе run нашего аплета. Для этого мы открываем входной поток, создаем на его основе форматированный входной поток данных, читаем одну строку текста и закрываем входной поток:
is = new DataInputStream(c.getInputStream()); String szReceived; szReceived = is.readLine(); is.close();
Сразу после этого программа CGI завершит свою работу и будет готова к обработке новых запросов на добавление записей. Что же касается метода run, то он добавит полученную от расширения сервера текстовую строку в многострочное окно редактирования, как это показано ниже, а затем инициирует перерисовку окна аплета:
txta.appendText(szReceived + "\r\n"); repaint();
Заметим, что использованный нами способ передачи данных подходит только для латинских символов. Если вам нужно передавать символы кириллицы, следует преобразовывать их из кодировки UNICODE, например, в гексадецимальную кодировку, а в программе CGI выполнять обратное преобразование. Аналогичную методику можно применять и для передачи произвольных двоичных данных.
Исходный текст программы CGI store.exe очень прост и показан в листинге 7.
Листинг 7. Файл store.c
#include <windows.h> #include <tchar.h> #include <wchar.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
void main(int argc, char *argv[]) { int nInDatasize; char * szMethod; char szBuf[2000];
FILE *fDatabase; CRITICAL_SECTION csAddRecord;
szMethod = getenv("REQUEST_METHOD"); if(!strcmp(szMethod, "POST")); { nInDatasize = atoi( getenv("CONTENT_LENGTH"));
fread(szBuf, nInDatasize, 1, stdin); szBuf[nInDatasize] = '\0';
InitializeCriticalSection(&csAddRecord); EnterCriticalSection(&csAddRecord);
fDatabase = fopen("c:\\EMAIL.DAT", "a+"); if(fDatabase != NULL) { fputs(szBuf, fDatabase); fclose(fDatabase); }
LeaveCriticalSection(&csAddRecord); DeleteCriticalSection(&csAddRecord);
printf( "Content-type: text/plain\r\n\r\n"); printf("Stored information: %s", szBuf); } }
Этот текст подготовлен для работы в среде Windows 95 или Windows NT, так как для синхронизации доступа к файлу мы использовали специфические для этих операционных систем функции работы с критическими секциями.
Свою работу программа CGI начинает с анализа переменной среды REQUEST_METHOD. Убедившись, что при запуске программы ей передали данные методом POST, программа определяет размер этих данных исходя из содержимого переменной среды CONTENT_LENGTH.
Далее программа считывает соответствующее количество байт данных из стандартного потока ввода, записывает их в файл. Затем, после добавления заголовка "Stored information:", программа CGI записывает полученную строку в стандартный выходной поток, передавая ее таким образом аплету Form.
Так как при реальной работе в сети Internet вашу программу CGI могут одновременно запустить несколько пользователей, для синхронизации обновления файла базы данных мы применили критическую секцию. В результате с файлом может работать в любой момент времени только одна копия программы CGI.
Еще одно замечание касается пути к файлу, который в нашем случае создается в корневом каталоге диска C:. При установке программы CGI на сервер вам необходимо обеспечить доступ на запись к каталогу, в котором располагается файл, для удаленных пользователей. О том, как это сделать, вы можете узнать из документации на ваш сервер Web.