IT • archiv

rus / eng | Логин | Комментарий к колонке | Печать | Почта | Клуб




Колонки


AWT клиент для веб-приложения. Часть 4

 
(Николай Зайкин)
3. Клиентская часть (окончание).

Итак, что происходит между клиентом (нашим приложением) и сервером, когда пользователь нажимает кнопку "Translate !" или "Enter"? Рассмотрим по шагам.

Сначала выводим в текстовую область, которую в дальнейшем используем для вывода результата, сообщение для пользователя, что начат поиск перевода, чтобы тушка чувствовала себя комфортно, а также выводим служебную информацию, такую как имя используемого хоста, на котором ведется поиск и имя HTTP прокси-сервера (если выбран через Edit->Settings...).

public void performTranslation() {
   try {
       int c;
       resultArea.setText
        ("Searching ... Please, wait ...\n"+
       "Host : " + hostURL + "\n" +
       ((useProxy) ? "With proxy : " +
                      proxyURL + ":" +
                      proxyPort :
                      "Without proxy. Direct connection."));

Далее, если выбрана опция использовать HTTP прокси-сервер, то устанавливаем соответствующие системные свойства. Дело в том, что Java на высоком уровне уже имеет поддержку работы через HTTP (например, для класса HttpURLConnection) и socks (например, для класса Socket) прокси-сервера, и для включения этой поддержки надо выставить два системных свойства: http.proxyHost - имя (URL) HTTP прокси-сервера, http.proxyPort - используемый порт на HTTP прокси-сервере и socksProxyHost, socksProxyHost соответственно. Кстати, этот вопрос освещен в документации к JDK очень туманно, посему у многих возникают трудности. В Java 1 (JDK 1.0) использовался похожий способ задания прокси-сервера, но отличался устанавливаемыми свойствами: proxyHost, proxyPort, и proxySet (boolean). этот способ (за исключением свойства proxySet, оно сейчас не влияет ни на что) работает до сих пор (для сохранения обратной совместимости старых программ), но не рекомендуется для использования при написании современных программ.

 Properties sprop = System.getProperties();
  sprop.put("http.proxyHost", useProxy ? proxyURL : "");
  sprop.put("http.proxyPort", useProxy ? proxyPort : "");
  System.setProperties( sprop );

Далее, создаем объект класса URL, который описывает в Java не что иное, как универсальный локатор ресурсов (URL, Uniform Resource Locator). В своем составе URL имеет 4 поля:

  1. Наименование протокола для доступа (http, ftp, whois, gopher, пр.), отделенное от остальной части url двоеточием и двойным слэшем. В нашем случае - протокол http ("http://").
  2. Имя хоста, ограниченное справа либо слэшем (если используется порт по умолчанию), либо двоеточием (если используется нестандартный порт).
  3. Необязательный номер порта, отделенный справа слэшем.
  4. Путь к требуемому ресурсу на хосте. В нашем случае этот путь выглядит так: /cgi-bin/dict.cgi?word=AAAA&type=BBBB&app=java
  • /cgi-bin/ - это название специальной директории на web-сервере, из которой можно запускать скрипты. В ней и только в ней в 99% случаев можно размещать скрипты.
  • dict.cgi - это название скрипта, описанного в 1-й части статьи. Расширение .cgi происходит от Common Gateway Interface - стандарта, с помощью которого происходит общение между клиентом (чаще всего web-браузером или, например, Java приложением, как в нашем случае) и сервером (Apache, IIS, Lotus Domino). Расширение .cgi может быть изменено, это зависит от настроек сервера (как и имя директории для cgi-программ). CGI-программа может быть написана практически на любом языке, начиная bash и заканчивая C++. Главное, чтобы она соответствовала стандарту CGI, который вкратце состоит в том, что ее ответ состоит из 2-частей. В 1-й части программа говорит о том, что она возвращает (HTML документ, GIF картинку, или просто перенаправляет по другому адресу), а во 2-й части ответа собственно отправляет обещанные данные (HTML документ, GIF картинку, или ничего в случае перенаправления (redirection)).
  • ? - вопросительный знак по стандарту CGI отделяет строку запроса (query string) от имени скрипта. Когда сервер видит такую запись, он запускает скрипт на исполнение, а все данные после ? заносит в специальную переменную окружения QUERY_STRING, например, из перла это будет выглядеть как $query_string = $ENV{'QUERY_STRING'};

    С помощью строки запроса мы можем передавать в скрипт свои данные - этот метод (передача параметров в строке запроса URL) называется метод GET. Он имеет определенные ограничения по размеру (в некоторых ОС размер переменной окружения не должен превышать 256 байт). На практике размер данных ограничивается 1-2 kB (ограничение конкретного web-сервера). Спецификация HTTP протокола не накладывает оговаривает верхний предел строки запроса. Стоит еще упомянуть, что ограничения метода GET позволяет обойти использование метода POST. При использовании метода POST извлечение данных на сервере будет выглядеть по-другому (чтение из дескриптора STDIN количества байт, хранящегося в переменной окружения CONTENT_LENGTH).

  • word=AAAA&type=BBBB&app=java - рассмотрим формат строки запроса. По стандарту CGI, query string состоит из пар "имя"="значение", разделенных знаком амперсанда. Но это не является жестким правилом, и вы можете задавать свой формат передаваемых данных и последующим его разбором на стороне сервера. В нашем случае мы придерживаемся стандарта, так как разбор данных от клиента на сервере производится с помощью модуля CGI.pm
Еще раз напомню назначение параметров (они уже были рассмотрены в 1-й части статьи).
  • word - переводимое слово.
  • type - тип поиска по словарям.
    • type=1 Слово начинается с шаблона (для sad будут найдены sad, sadness и т.д.);
    • type=2 Слово строго совпадает с шаблоном (для sad будет найдено только sad);
    • type=3 Слово содержит в своем составе шаблон (для sad будут найдены sad, ambassador, disadvantages и т.д.).
  • app=java - сообщает скрипту о том, что обратившийся клиент является приложением Java. Подавляет вывод HTML тэгов, вывод ведется в plain-text.

После создания URL, мы создаем объект для доступа к удаленному ресурсу. Так как используемый протокол - HTTP, то и создаем объект класса HttpURLConnection, который поддерживает специфичные для этого протокола свойства.

Получаем от сервера информацию о размере отправляемых данных. Если данные есть, то открываем входной поток, связанный с удаленным ресурсом. Так как принимаемые данные имеют символы кириллицы, это надо дать понять Java. В Java для представления символов используется кодировка Unicode (два байта на символ), символы кириллицы расположены в диапазоне значений 0x0400-0x04ff. При простом приведении типа от int к char мы можем получить совсем не те символы, из-за того, что Javа подразумевает использование другой кодовой страницы (для Windows - зависит от того, русская или английская версия). Поэтому нам надо использовать класс InputStreamReader, ориентированный (как и все Reader'ы) не на чтение байтов (как все Stream'ы), а на чтение символов, и который позволяет задавать кодовую страницу данных для правильного преобразования в char. Используется стандартная кодовая страница ОС Windows для кириллицы - Cp1251, так как словари содержат данные именно в этой кодировке; но ничто не мешает вам использовать словари в KOI-8, предварительно перекомпилировав программу с поддержкой KOI-8, для использования приложения под ОС Linux.

 HttpURLConnection hpCon = (HttpURLConnection) hp.openConnection();
  int len = hpCon.getContentLength();
  if (len > 0) {
      resultArea.setText("Connected ... Please, wait ...");
      InputStream input = hpCon.getInputStream();
      InputStreamReader isr = new InputStreamReader(input, "Cp1251");

      int i = len;
      char ch;
      String tr = "";
      while (((c = isr.read()) != -1) && (--i > 0)) {
          tr += (char) c;
      }

Следующий фрагмент кода отрезает весь текст после финальной строки, выводимой скриптом. Это нужно для того, что иногда бесплатные хостинги (например, www.virtualave.net) выводят свою рекламу в STDOUT после вывода пользовательского документа :)). И, выводим перевод слова на экран, в текстовую область.

 int eod = tr.indexOf("searching time =");
  eod = tr.indexOf("sec", eod) + 3;
  tr = tr.substring(0, eod);
  resultArea.setText(tr);

В случае ошибки от сервера, выводим сообщение о невозможности соединения. В случае обрыва связи во время чтения выбрасываем исключение.

   } else {
        resultArea.setText("Error ! Connection to host failed !");
    }
} catch (IOException e) {
    System.out.print(e);
}
}

На этом хотелось бы завершить описание web-приложения. Скромный автор не претендует быть догмой и будет рад замечениям и пожеланиям от благодарных читателей. Amen !

TOC




Справка | Условия Copyright © 1999 — 2010, IT • archiv.
В начало | Логин | Комментарий к колонке | Поиск | Почта