Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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;
}
[Назад] [Содеожание] [Дальше]