Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2© Александр Фролов, Григорий ФроловТом 32, М.: Диалог-МИФИ, 1997, 288 стр. Приложение CallCGIВ 29 томе “Библиотеки системного программиста” мы рассказывали о том, как с помощью расширений сервера Web, выполненных на основе интерфейса CGI и ISAPI можно обрабатывать данные из форм, расположенных в документах HTML. В частности, мы привели там исходные тексты программы controls.exe (составленной на языке программирования С), которая динамически создавала и отображала данные, введенные в форме. Внешний вид этой формы показан на рис. 3.6, воспроизведенном нами из указанного тома. Рис. 3.6. Форма для ввода данных Программа CGI controls.exe получала данные, введенные пользователем в этой форме, после чего динамически создавала документ HTML, в котором отображала состояние переменных серды, полученные данные в исходном и раскодированном виде, а также список значений полей (рис. 3.7). Рис. 3.7. Документ HTML, сформрованный динамически программой CGI control.exe Создавая приложение CallCGI, мы поставили перед собой задачу заменить форму приложением Java, которое вводит с клавиатуры текстовую строку полей и передает ее программе CGI controls.exe. Содержимое динамически сформированного программой CGI документа HTML приложение CallCGI отображает в своем консольном окне, как это показано на рис. 3.8. Рис. 3.8. Отображение в окне приложения Java содержимого документа HTML, полученного от программы CGI Исходный текст приложения CallCGIИсходный текст приложения CallCGI приведен в листинге 3.9. Листинг 3.9. Файл CallCGI\CallCGI.java // ========================================================= // Вызов расширения сервера Web на базе интерфейса CGI // из приложения Java // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.io.*; import java.net.*; import java.util.*; public class CallCGI { // ------------------------------------------------------- // main // Метод, получающий управление при запуске приложения // ------------------------------------------------------- public static void main(String args[]) { // Массив для ввода строки с клавиатуры byte bKbdInput[] = new byte[256]; // Размер принятого блока данных int length; // Рабочая строка String str; // Адрес URL вызываемой программы CGI URL u; // Канал связи с расширением CGI URLConnection c; // Выходной поток для передачи данных расширению CGI PrintStream ps; // Входной поток для получения данных от расширения CGI DataInputStream is; try { // Выводим строку приглашения System.out.println("CGI extension call" + "\nEnter any string for send to CGI..."); // Читаем строку, передаваемую расширению, // с клавиатуры length = System.in.read(bKbdInput); // Если строка не пустая, обрабатываем ее if(length != 1) { // Преобразуем строку в формат String str = new String(bKbdInput, 0); // Обрезаем строку, удаляя символ конца строки StringTokenizer st; st = new StringTokenizer(str, "\n"); str = new String((String)st.nextElement()); // Выполняем кодировку URL для передаваемой строки String StrEncoded = URLEncoder.encode(str); // Отображаем перекодированную строку System.out.println("Encoded string: >" + StrEncoded + "<"); // Создаем объект класса URL для расширения CGI u = new URL( "http://frolov/frolov-cgi/controls.exe"); // Открываем канал связи с расширением CGI c = u.openConnection(); // Создаем выходной поток данных для передачи // введенной строки серверу CGI ps = new PrintStream(c.getOutputStream()); // Передаем закодированную строку расширению CGI ps.println(StrEncoded); // Закрываем выходной поток ps.close(); // Создаем входной поток для приема данных от // расширения CGI is = new DataInputStream(c.getInputStream()); System.out.println( "\n------------------------------------------" + "\n Data from CGI extension" + "\n------------------------------------------\n"); // Прием данных выполняем в цикле while (true) { // Получаем очередную строку str = is.readLine(); // Если последняя строка, прерываем цикл if(str == null) break; // Отображаем принятую строку System.out.println(str); } // Закрываем входной поток is.close(); } } catch(Exception ioe) { System.out.println(ioe.toString()); } try { System.out.println( "Press <Enter> to terminate application..."); System.in.read(bKbdInput); } catch(Exception ioe) { System.out.println(ioe.toString()); } } } Описание исходного текста приложения CallCGIВнутри метода main мы определили несколько переменных. Массив bKbdInput предназначен для хранения строки, введенной с помощью клавиатуры. В переменную length записывается длина этой строки. Строка str класса String используется в качестве рабочей. Переменная u класса URL предназначена для хранения ссылки на объект URL, созданный для загрузочного файла программы CGI. Ссылка на канал связи с программой CGI хранится в переменной с именем c класса URLConnection. Переменные ps класса PrintStream и is класса DataInputStream хранят ссылки, соответственно, на выходной и входной потоки, через которые наше приложение обменивается данными с программой CGI. После вывода приглашения на консоль наша программа вводит строку, которая будет передана программе CGI: length = System.in.read(bKbdInput); Далее массив bKbdInput преобразуется в строку str и перекодируется в кодировку URL. Эта кодировка была описана нами в 29 томе “Библиотеки системного программиста”. Она выполняется с помощью статического метода encode, определенного в классе URLEncoder: String StrEncoded = URLEncoder.encode(str); Перекодированная строка отображается на консоли: System.out.println("Encoded string: >" + StrEncoded + "<"); На следующем этапе наше приложение создает объект класса URL для загрузочного файла программы CGI: u = new URL("http://frolov/frolov-cgi/controls.exe"); Здесь предполагается, что программа CGI находится в файле controls.exe, который записан в виртуальный каталог frolov-cgi на сервере Web с адресом http://frolov). Про создание и настройку виртуальных каталогов для размещения расширений сервера Web мы рассказали в 29 томе “Библиотеки системного программиста”. После создания объекта класса URL мы создаем канал с программой CGI как объект класса URLConnection: c = u.openConnection(); Пользуясь этим каналом, мы вначале получаем выходной поток методом getOutputStream, а затем на его базе создаем форматированный выходной поток класса PrintStream, удобный для записи в него текстовых строк: ps = new PrintStream(c.getOutputStream()); Через канал ps наше приложение передает программе CGI строку StrEncoded, а затем закрывает выходной поток, как это показано ниже: ps.println(StrEncoded); ps.close(); В этот момент на сервере Web уже запущена программа CGI, и она приняла переданные ей данные. Обработав эти данные, программа CGI записала в стандартный выходной поток динамически созданный ей документ HTML. Для получения документа наше приложение CallCGI создала входной форматированный поток данных: is = new DataInputStream(c.getInputStream()); Входной поток создан в два приема. Вначале с помощью метода getInputStream приложение создала обычный входной поток, а затем, на его базе, форматированный входной поток класса DataInputStream. Получение от программы CGI динамически сформированного ей документа HTML наше приложение выполняет в цикле по строкам. Строка документа HTML читается из входного форматированного потока методом readLine и записывается в переменную str: str = is.readLine(); Если в процессе чтения был достигнут конец потока, цикл прерывается: if(str == null) break; Строка, полученная методом readLine, отображается на консоли пиложения: System.out.println(str); После завершения цикла входной поток закрывается методом close: is.close(); Исходные тексты программы CGIВ лситинге 3.10 мы привели исходный текст программы CGI с именем controls. Он несколько упрощен по сравнению с исходным текстом одноименного приложения, описанного в 29 томе “Библиотеки системного программиста” - мы выбросили обработку метода передачи данных GET, так как наше приложение CallCGI передает данные только методом POST. Описание этой программы вы найдете в упомянутом 29 томе. Листинг 3.10. Файл controls\controls.c // =============================================== // Программа CGI controls.c // Демонстрирует методы получения и обработки // данных от форм, расположенных в документах HTML // // (C) Фролов А.В., 1997 // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // =============================================== #include <stdio.h> #include <stdlib.h> #include <string.h> // Прототипы функций перекодировки void DecodeStr(char *szString); char DecodeHex(char *str); // ------------------------------------------------ // Функция main // Точка входа программы CGI // ------------------------------------------------ void main(int argc, char *argv[]) { int lSize; FILE * fileReceived; char * szMethod; char szBuf[8196]; char szSrcBuf[8196]; char * szPtr; char * szParam; // Вывод заголовка HTTP и разделительной строки printf("Content-type: text/html\n\n"); // Вывод начального форагмента документа HTML, // формируемого динамически printf("<!DOCTYPE HTML PUBLIC" " \"-//W3C//DTD HTML 3.2//EN\">"); printf("<HTML><HEAD><TITLE>Call CGI from Java" "</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>"); // Определяем метод передачи данных szMethod = getenv("REQUEST_METHOD"); // Обработка метода POST if(!strcmp(szMethod, "POST")) { // Определяем размер данных, полученных от навигатора // при передаче данных из полей формы lSize = atoi(getenv("CONTENT_LENGTH")); // Читаем эти данные в буфер szBuf из // стандартного потока ввода STDIN fread(szBuf, lSize, 1, stdin); // Создаем файл, в который будут записаны // принятые данные fileReceived = fopen("received.dat", "w"); // Выполняем запись принятых данных fwrite(szBuf, lSize, 1, fileReceived); // Закрываем файл принятых данных fclose(fileReceived); // Отображаем значения некоторых переменных среды printf("<H2>Environment variables</H2>"); // Метод доступа printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD")); // Размер полученных данных в байтах printf("<BR>CONTENT_LENGTH = %ld", lSize); // Тип полученных данных printf("<BR>CONTENT_TYPE = %s", getenv("CONTENT_TYPE")); // Закрываем буфер данных двоичным нулем, // превращая его таким образом в строку szBuf[lSize] = '\0'; // Делаем копию принятых данных в буфер szSrcBuf strcpy(szSrcBuf, szBuf); // Отображаем принятые данные без обработки printf("<H2>Received data</H2>"); printf("<P>%s", szSrcBuf); // Выполняем перекодировку принятых данных DecodeStr(szSrcBuf); // Отображаем результат перекодировки printf("<H2>Decoded data</H2>"); printf("<P>%s", szSrcBuf); // Выводим список значений полей формы printf("<H2>Filds list</H2>"); // Дописываем в конец буфера принятых данных // символ "&", который используется в качестве // разделителя значений полей szBuf[lSize] = '&'; szBuf[lSize + 1] = '\0'; // Цикл по полям формы for(szParam = szBuf;;) { // Ищем очередной разделитель szPtr = strchr(szParam, '&'); // Если он найден, раскодируем строку параметров if(szPtr != NULL) { *szPtr = '\0'; DecodeStr(szParam); // Выводим в документ значение параметра printf("%s<BR>", szParam); // Переходим к следующему параметру szParam = szPtr + 1; // Если достигнут конец буфера, завершаем цикл if(szParam >= (szBuf + lSize)) break; } else break; } // Выводим завершающий фрагмент документа HTML printf("</BODY></HTML>"); return; } } // ------------------------------------------------ // Функция DecodeStr // Раскодирование строки из кодировки URL // ------------------------------------------------ void DecodeStr(char *szString) { int src; int dst; char ch; // Цикл по строке for(src=0, dst=0; szString[src]; src++, dst++) { // Получаем очередной символ перекодируемой строки ch = szString[src]; // Заменяем символ "+" на пробел ch = (ch == '+') ? ' ' : ch; // Сохраняем результат szString[dst] = ch; // Обработка шестнадцатеричных кодов вида "%xx" if(ch == '%') { // Выполняем преобразование строки "%xx" // в код символа szString[dst] = DecodeHex(&szString[src + 1]); src += 2; } } // Закрываем строку двоичным нулем szString[dst] = '\0'; } // ------------------------------------------------ // Функция DecodeHex // Раскодирование строки "%xx" // ------------------------------------------------ char DecodeHex(char *str) { char ch; // Обрабатываем старший разряд if(str[0] >= 'A') ch = ((str[0] & 0xdf) - 'A') + 10; else ch = str[0] - '0'; // Сдвигаем его влево на 4 бита ch <<= 4; // Обрабатываем младший разряд и складываем // его со старшим if(str[1] >= 'A') ch += ((str[1] & 0xdf) - 'A') + 10; else ch += str[1] - '0'; // Возвращаем результат перекодировки return ch; } |