Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Секреты программирования для Internet на Java

.pdf
Скачиваний:
181
Добавлен:
02.05.2014
Размер:
3.59 Mб
Скачать

motherApplet.showStatus("going to "); return false;}

public boolean mouseMove(int x, int y,Graphics g) { motherApplet.showStatus("Go to: "+doc.toExternalForm()); return false;}

}

Теперь давайте напишем класс ActionArea для проигрывания звуковых фрагментов:

package ventana.aia; import java.applet.*; import java.awt.*; import java.util.*; import java.net.*; import ventana.util.*;

public class SoundActionArea extends ActionArea implements Configurable { AudioClip sound;

public void configureObject(String param, String value) throws ConfigurationInvalidParameterException, ConfigurationInvalidValueException {

param=param.toLowerCase();

if(param.equals("sound"))

parseSound(value);

super.configureObject(param,value);} public void parseSound(String value) throws

ConfigurationInvalidValueException {

try {

URL U=new URL(value); sound=motherApplet.getAudioClip(U); if (sound==null)

throw new ConfigurationInvalidValueException(value+" not a

valid file");

}catch (MalformedURLException e) {

throw new ConfigurationInvalidValueException(value+" not a URL");}

}

public String toString() {

String S="SoundActionArea\n"; S=S+sound.toString()+"\n"; return S;}

public void completeConfiguration() throws ConfigurationFailedException {

if (sound==null)

throw (new ConfigurationFailedException ("no sound described"));}

public boolean mouseMove(int x, int y,Graphics g) { sound.play();

return super.mouseMove(x,y,g);}

}

Возможные улучшения

Благодаря выбранному нами способу структурирования апплета мы можем расширить его применение в различных направлениях. Самый простой путь состоит в добавлении новых ActionArea, но можно еще создать подклассы в классе ActionImageAnimation и добавить новые возможности, такие как изменение изображений или поддержка фоновых изображений прямо на рабочей области. Не проделывая дополнительной работы, мы можем получить несколько экземпляров рабочих областей в одном апплете и несколько анимаций внутри рабочей области.

Ⱦɚɧɧɚɹ ɜɟɪɫɢɹ ɤɧɢɝɢ ɜɵɩɭɳɟɧɚ ɷɥɟɤɬɪɨɧɧɵɦ ɢɡɞɚɬɟɥɶɫɬɜɨɦ %RRNV VKRS Ɋɚɫɩɪɨɫɬɪɚɧɟɧɢɟ ɩɪɨɞɚɠɚ ɩɟɪɟɡɚɩɢɫɶ ɞɚɧɧɨɣ ɤɧɢɝɢ ɢɥɢ ɟɟ ɱɚɫɬɟɣ ɁȺɉɊȿɓȿɇɕ Ɉ ɜɫɟɯ ɧɚɪɭɲɟɧɢɹɯ ɩɪɨɫɶɛɚ ɫɨɨɛɳɚɬɶ ɩɨ ɚɞɪɟɫɭ piracy@books-shop.com

Глава 17

Взаимодействие с CGI: Java-магазин

Контракт

Свойства

Конструкция

Реализация HTTP-запросы

Размещение информации о товарах Класс FIFO

Получение изображений и описаний Обработка действий пользователя

Считывание данных о конфигурации и инициализация Объединяем все вместе

Передача выбора пользователя на Web-сервер Обработка принятых данных при помощи CGI-программы

Возможные улучшения

Что вы узнаете из этой главы

В этой главе мы создадим апплет "Java-магазин" - программу "тележки для покупок", которая позволит посетителям виртуального магазина просматривать и выбирать товары. Вот некоторые вопросы, которые мы будем обсуждать:

Использование HTTP для получения данных для апплета.

Связь с программами CGI, выполняющимися на Web-сервере.

Проектирование удобного интерфейса пользователя.

Динамическое порождение интерфейса пользователя.

Создание компонентов многократного использования.

Полный исходный текст для классов, обсуждаемых в этой главе, помещен на диск CD-ROM, прилагаемый к книге. Этим диском могут пользоваться те из читателей, кто работает с Windows 95/NT или Macintosh; пользователи UNIX должны обращаться к Web-странице Online Companion, на которой собраны сопроводительные материалы к этой книге (адрес http://www.vmedia.com/ java.html).

Контракт

Представьте, что ваш заказчик хочет улучшить интерактивный книжный магазин. В реализации этого магазина используется притягательная графика, последние приемы HTML и система "тележки для покупок", в которую покупатели могут отбирать приобретаемые книги. Система тележки для покупок следит за тем, что пользователь выбрал при просмотре интерактивного каталога магазина. Просматривая каталог, пользователь может нажать кнопку, чтобы добавить товар к своему списку приобретаемых товаров. Существующая система тележки для покупок выполнена в виде ряда сценариев CGI, которые производят HTML-бланки из интерактивного каталога и обрабатывают выбор товаров для приобретения. Однако штат, обслуживающий этот магазин, жалуется на следующие ограничения HTML-бланков:

www.books-shop.com

Отсутствие реального взаимодействия с пользователем. Пользователь может только нажать кнопку, чтобы добавить или удалить товар из тележки для покупок, что не так удобно, как хотелось бы.

Web-сервер сильно загружен постоянными запросами, которые выдает программа тележки для покупок, поскольку пользователь просматривает множество товаров. Каждое действие, производимое пользователем (например, выбор товара для приобретения), должно вызвать программу CGI на Web-сервере, которая, в свою очередь, должна следить за сохранением предыдущего выбора.

Изменение Web-страницы интерактивного магазина - трудоемкая задача. Появление новых товаров и перемещение товаров с одной страницы на другую требуют добавления

иизменения многих связей; по мере роста магазина эти связи все больше усложняются -

иу персонала уже не хватает времени на его обслуживание.

Рост интерактивного магазина является обязательным условием нашего заказчика, поэтому текущие ограничения должны быть сняты. Мы предлагаем Java-апплет, который может заняться этими проблемами. Он будет работать с существующим интерактивным магазином, так что переход от магазина, построенного на HTML, к магазину, базирующемуся на Java, будет гладким. Пользователи, Web-броузеры которых не способны выполнять Java-апплеты, все равно смогут получить доступ к товарам компании. Происхождение заказов, предназначенных существующему интерактивному магазину или новому магазину, основанному на Java, будет безразлично штату, который обрабатывает заказы, так что никакого дополнительного обучения не потребуется.

Свойства

Java-апплет позволит пользователю управлять магазином с помощью меню. Каждый пункт меню будет представлять новый отдел магазина. Вложенные меню позволят штату организовывать магазин в подотделы. Для примера, в отделе "Программирование" возможны подотделы "Книги по Java", "Книги по C++" и "Книги по Visual Basic". В Java-магазине будет "корзина для покупок". В дополнение к списку, содержащему текущий выбор пользователя, эта программа позволит пользователю удалять товары из списка приобретения.

ВJava-магазине будет и справочное меню, чтобы пользователь мог, к примеру, послать письмо

свопросом к владельцу магазина или получить детализированную упорядоченную информацию. Java-магазин будет состоять из двух основных панелей: левая панель будет содержать изображение товаров в текущих отделах магазина, а правая - детализированное описание товара, который выбран на левой панели.

Выбрав товар, пользователь может нажать на кнопку "Check out of Store", по которой это приобретение будет внесено в список и послано сценарию CGI на Web-сервере. Эта информация может быть упакована так, чтобы она была идентична ныне существующим интерактивным данным, основанным на HTML-бланках. Это позволит нам вызывать ту же самую CGI-программу.

Внутреннее устройство Java-магазина является более сложным, чем вышеупомянутый внешний интерфейс пользователя. Меню организованы динамически - они формируются, основываясь на файлах, постоянно находящихся на Web-сервере. Это позволяет настраивать Java-магазин без какого-либо программирования. Штат магазина может заменять предлагаемые товары, их описания и организацию отделов, изменяя простые текстовые файлы. Java-магазин читает эти файлы и формирует интерфейс пользователя согласно их указаниям.

Конструкция

Сначала мы сделаем грубый набросок интерфейса пользователя. Внешний интерфейс должен быть тщательно разработан, чтобы быть компактным и интуитивно понятным. Мы решили использовать справочное меню и меню, которые позволят пользователю передвигаться между отделами магазина. На экране будут находиться большая панель, содержащая изображения товаров в текущих отделах магазина, панель, в которой можно показывать описание товара, и панель для списка выбранных товаров (то есть корзина для покупок). На рис. 17-1 показан общий вид и размещение элементов, которое мы хотели бы получить.

www.books-shop.com

Рис. 17.1.

Мы используем прямое взаимодействие между Web-сервером и апплетом на Java. Как вы знаете, Java-апплеты могут открывать соединения с портами, отличными от порта HTTP Webсервера. Однако мы хотим добраться и до пользователей, которые должны пройти через firewall, чтобы обратиться к ресурсам Web. Мы используем встроенный HTTP Web-броузера, гарантирующий, что любой обратившийся к Web-странице, из которой вызывается наш Javaмагазин, может также обращаться к информации, сгенерированной в Java-магазине (например, к изображению товара и его описанию). Кроме того, мы должны использовать стандартное HTTPсоединение для вызова существующей CGI-программы, используемой сейчас интерактивным магазином. Эту задачу можно решить с помощью двух классов Java API - URL и URLConnection. Мы делаем запросы непосредственно к Web-серверу, чтобы отыскать все данные, которые мы должны сформировать в Java-магазине.

Файлы данных, по которым мы будем формировать интерфейс пользователя, должны быть организованы так, чтобы штат магазина мог легко вносить изменения. Пусть основные пункты меню будут содержаться в файле Store.idx. Этот файл будет включать имена каталогов, сопровождаемые именем меню. В нашей программе этот файл будет выглядеть так:

Internet/

Книги по Интернет

Windows/

Книги по Windows Programming/

Книги по программированию

Каталоги будут отделами магазина. Внутри каталога будет файл имя каталога.idx, который будет содержать имена файлов изображений и описаний и уникальное имя, добавляемое в корзину для покупок, когда пользователь выбирает этот товар. Если имя заканчивается на "/", это означает вложенное меню и соответствующий подотдел магазина. Отделы по-прежнему физически представлены как каталоги; в каждом из них будет находиться свой idx-файл. Вот как выглядит типовой idx-файл для отдела "Интернет":

Webserver.gif

Webserver.txt,

Книга о Web Сервере

Java.gif

Java.txt

Программирование на Java для Интернет

Используя эту структуру каталогов, мы устанавливаем жесткую организацию, которая достаточно проста для того, чтобы ей следовать. Чтобы сделать точные изменения, вам нужно будет только определить, товар из какого отдела должен быть добавлен или удален, перейти в соответствующий каталог и изменить в нем idx-файл. Групповые изменения будут более трудоемкими, но мы идем на этот компромисс, так как малые изменения будут происходить намного более часто, чем удаление всех отделов магазина. Поиск всех idx-файлов выполнен в основном классе Store, a составление интерфейса пользователя из данных, найденных в классе Store, будет генерироваться в классе StoreWindow.

Мы выполним Java-магазин в двух основных классах. Первый называется просто Store (магазин). Этот модуль выбирает данные для формирования интерфейса пользователя. Он также

www.books-shop.com

вызывает класс StoreWindow. StoreWindow - экран, который появляется после того, как данные интерфейса пользователя были найдены. StoreWindow содержит корзину для покупок, изображения товаров, меню магазина и описания товаров. Он также вызывает другие модули, чтобы решить задачи низкого уровня, которые необходимы, чтобы сформировать интерфейс пользователя. На рис. 17-2 показана полная иерархия классов для нашего Java-магазина.

Рис. 17.2.

Мы разделим Java-магазин на несколько ключевых модулей, каждый из которых разработан для выполнения специфической задачи. Задачи мы включим в классы:

HTTPget и HTTPpost - подпрограммы HTTP низкого уровня, которые используются, чтобы выбрать idx-файлы и файлы описания.

Productlmage - специальный класс, который выводит изображения товара.

FIFO - класс, основанный на принципе "первый вошел - первый вышел", который облегчает формирование интерфейса пользователя.

ImagePanel - класс панели, который отыскивает изображение товара и помещает их в одну панель вместе с поиском описания товара.

Basket - класс другой панели, который содержит весь список выбранных товаров с их методом.

Store - основной класс апплета, который отыскивает вышеупомянутые файлы конфигурации.

StoreWindow - класс окна, который соединяет компоненты и добавляет высокоуровневые функциональные возможности.

CheckOutFrame - класс, который вызывается, когда пользователь выходит из магазина. Он получает некоторую информацию от пользователя и передает ее CGI-программе через класс HTTPpost.

За исключением основных модулей Store и StoreWindow классы разработаны так, чтобы быть настолько общими, насколько возможно при их многократном использовании. Действительно, класс HTTPget используется и в классе Store, и в классе ImagePanel, а объект PtoductImage - многократно используется в классе ImagePanel.

Нашему апплету не нужны сложные обработчики событий. Мы хотим позволить пользователям выбирать пункты меню, для которых мы должны выбрать метод handleEvent или action, и затем соответственно отвечать. Мы также хотим показать описание товара, когда пользователь щелкает на его изображении. Когда пользователь дважды щелкает на изображении товара, мы добавляем его в корзину для покупок. Как вы можете видеть, у нас есть только два главных класса, обрабатывающих специфические события высокого уровня. Модуль StoreWindow обрабатывает события, перечисленные выше. Класс Basket обрабатывает три кнопки, названия которых соответствуют выполняемым ими операциям: Remove (удаление), Clear (удаление всего) и Check out of Store (выход из магазина). В действительности событие, отвечающее на щелчок пользователя по кнопке Check out of Store, передается родительскому классу StoreWindow, как показано на рис. 17-3.

www.books-shop.com

Рис. 17.3.

Реализация

Давайте начнем нашу реализацию классов с классов-помощников. Когда мы будем обсуждать высокоуровневый класс StoreWindow, вы увидите, что эти классы легко приспособить для совместной работы.

Мы сформируем и проверим каждый класс-помощник прежде, чем начинать работу над классом StoreWindow. Дополнительное время, которое мы будем тратить на строгую проверку каждого модуля, позволит нам быстро и без неожиданностей собрать компоненты воедино. На рис. 17-4 показан Java-магазин, каким его будут видеть покупатели.

Рис. 17.4.

СОВЕТ В следующем разделе мы предполагаем, что читатель имеет некоторое представление о HTTP, POST, GET и роли CGI-программ. Если вы еще не владеете этими понятиями, проконсультируйтесь по книге "The Web Server Book" (издательство Ventana Press) или странице Online Companion, чтобы получить больше информации по этой теме.

HTTP-запросы

www.books-shop.com

Впервую очередь нам нужны модули, которые получают установочные данные магазина, чтобы мы могли сформировать панели товаров и меню. Давайте будем использовать HTTPзапросы, чтобы получить файлы установки .idx. У нас будут два родовых класса - один для выполнения запроса GET и другой для взаимодействия с CGI-программой на Web-сервере, то есть запроса POST. Класс HTTPpost будет обсуждаться ниже. Мы представим вариант класса HTTPget, чтобы показать использование сетевых сокетов для связи с Web-сервером напрямую. Полный исходный текст обоих классов доступен на диске CD-ROM, прилагаемом к книге (для пользователей Windows 95/NT и Macintosh), и на Web-странице Online Companion (для пользователей UNIX).

ВJava API есть хороший набор классов, выполняющих работу по созданию URL и соединению

сним: классы URL, URLConnection и URLEncoder. В главе 14, "Связь по сети с помощью URL", об этих классах говорится более подробно. Давайте посмотрим на класс HTTPpost, используемый в Java-магазине:

import java.net.*; import java.io.*; public class HTTPpost {

String CGIresults="";

public HTTPpost(String URLline , String PostData){ String inputLine;

String PostDataEncoded = "result="+URLEncoder.encode(PostData);

Мы устанавливаем HTTPpost, чтобы выбрать два параметра - сначала строковый параметр URLline, который содержит имя CGI-программы, которую мы хотим выполнить на Web-сервере, и полный URL к этой программе. Параметр PostData содержит данные, которые мы хотим передать CGI. Нам нужно удостовериться, что все эти данные передаются Web-серверу в соответствующем формате, так что для кодировки мы используем стандартизированный формат x-www-form- urlencoded с помощью метода URLEncoder.encoder. Мы приписываем это значение к строке "result". CGI будет видеть эти данные как "result=(PostData)", что означает, что значение "result" находится в PostData. Это важно помнить при создании CGI-программы, поскольку мы будем перемещать эту пару значений наружу для обработки. В следующем фрагменте кода устанавливается соединение с Web-сервером:

try {

URL url = new URL(URLline);

URLConnection connection = url.openConnection(); PrintStream outStream = new PrintStream(connection.getOutputStream()); DataInputStream inStream;

Сначала мы создаем новый экземпляр из URL, используя параметр URLline. Затем мы создаем новый экземпляр URLConnection, используя объект, возвращаемый методом url.connection. Это дает нам основу для формирования входного и выходного потоков. Мы создаем PrintStream для вывода PostData CGI-программы с помощью URLconnection как канал для фактического соединения. Аналогично ниже мы создаем inStream из экземпляра URLConnection, чтобы получить данные с Web-сервера или CGI-программы:

outStream.println(PostDataEncoded);

outStream.close();

inStream = new DataInputStream(connection.getInputStream()); while (null != (inputLine = inStream.readLine())) {

CGIresults = CGIresults + inputLine +"\n";

}

inStream.close();

}catch (MalformedURLException me) { System.err.println("MalformedURLException: " + me);

}catch (IOException ioe) { System.err.println("IOException: " + ioe);

}

}

Мы теперь готовы добраться и до бизнеса. Сначала мы посылаем параметр PostData Webсерверу, который передает его CGI-программе. Затем мы начинаем получать данные из CGI, пока

www.books-shop.com

буфер не станет пустым. Эти данные сохраняются в глобальных переменных CGIresults. Мы должны перехватить все исключения, сгенерированные при создании нового URL и вызванные методом URL.openConnection. Ниже приведен метод, который мы вызываем, чтобы возвратить результат HTTPpost:

public String results() { return CGIresults;

};

}

Метод results просто возвращает вывод из CGI. Он используется для того, чтобы удостовериться, что CGI-программа получила данные, которые мы ей послали, и правильно их обработала. Мы также можем использовать этот метод, чтобы показать сообщение пользователю в нашем апплете. Кроме того, мы можем передавать URL обратно из CGI-программы и затем вызывать showDocument, чтобы заставить Web-броузер показать страницу. Это может использоваться для того, чтобы послать URL, содержащий страницу "Спасибо за покупку", или страницу, сообщающую об ошибке, или еще что-нибудь подобное.

СОВЕТ Класс HTTPget не принимает параметр POST; взамен ему требуется параметр QUERY LINE. Он также выбирает данные от Web-сервера. Мы хотим использовать его, чтобы получать Store.idx и другие idx-файлы, содержащие структуру магазина. Если бы нам нужно было получить HTML-файл, мы бы увидели все теги HTML. Так как idx-файлы - простые текстовые файлы, нам не нужно волноваться о форматировании HTML-файлов. Теоретически мы могли бы даже получать двоичные данные, но это потребовало бы изменения входного потока, чтобы читать байты вместо строк. Это не особенно полезно, так как у нас уже есть метод getImage.

Ниже представлен другой путь выполнения HTTP-запросов в классе HTTPget. Он почти полностью идентичен классу HTTPpost за исключением того, что для обеспечения связи мы используем непосредственно соединения сокетов. Вы можете пропустить чтение кода, если чувствуете, что вам удобнее работать с HTTPpost, обсужденным выше.

import java.awt.*; import java.net.*; import java.io.*; import java.util.*; public class HTTPget

{

private final String CONTENTtype = "application/octet-stream"; private String RECIEVEdata = "";

private String home; private int port;

public HTTPget(String URLbase, int URLport) {

//Приведенный здесь конструктор довольно сложный.

//Мы посылаем URL в качестве имени машины в URLbase

//и порт Web-сервера в URLport.

//Путь к сценарию посылается описываемым ниже методом

//в параметре Script.

home = URLbase; port = URLport;

if (port == -1) port = 80;

}

public void submit(String Script, String PostData)

{

Socket sock; OutputStream outp; InputStream inp; DataOutputStream dataout; DataInputStream datain; String URLline; RECIEVEdata = "";

URLline = Script+"?"+PostData;

www.books-shop.com

// создаем сокет клиента try

sock = new Socket(home, port); catch (Exception e)

{

System.out.println("Error with HTTP GET "+e);

}

// получаем выходной поток для связи с сервером try

{

outp = sock.getOutputStream(); inp = sock.getInputStream();

}

catch (Exception e)

{

RECIEVEdata = e+" (getstream)"; try sock.close();

catch (IOException ee) ; return;

}

try

{

dataout = new DataOutputStream(outp); datain = new DataInputStream(inp);

}

catch (Exception e)

{

RECIEVEdata = e+" (Dstream)"; try sock.close();

catch (IOException ee) ; return;

}

// посылаем запрос HTTP-серверу и получаем возвращаемые данные try

{

dataout.writeBytes("GET " + URLline + " HTTP/1.0\r\n"); dataout.writeBytes("Content-type: " + CONTENTtype +

"\r\n");

dataout.writeBytes("Content-length: 0 \r\n"); dataout.writeBytes("\r\n");

dataout.flush(); boolean body = false; String line;

while ((line = datain.readLine()) != null)

{

if (body)

<_>RECIEVEdata += "\n" + line; else if (line.equals("")) <_>body = true;

}

}

catch (Exception e)

{

RECIEVEdata = e+" (write)"; try

sock.close();

catch (IOException ee) ; return;

}

// закрытие магазина try

{

dataout.close();

datain.close();

www.books-shop.com

}

catch (IOException e) ; try

sock.close();

catch (IOException e) ;

}

public String Results() { return RECIEVEdata;

}

}

Размещение информации о товарах

Класс Productlmage хранит не только изображение товара, но также метку и описание. Класс выполнен как расширение класса Canvas, так что мы можем присваивать ему значение и хранить товар со специфическими данными, содержащимися в самом объекте. Мы используем его в классе ImagePanel, поскольку мы добавляем к нему изображение товара. К описанию и метке можно обращаться непосредственно, создавая ссылку к специфическому Productlmage и затем обращаясь к описанию и метке через эту ссылку. Этот механизм реализован в классе StoreWindow, так что мы не должны использовать класс ImagePanel, чтобы добраться до данных. Кроме того, мы добавляем общие переменные height и width, чтобы позволить ImagePanel правильно устанавливать размер для всех ProductImage, который добавляет его специфическому

ImagePanel. Давайте рассмотрим класс ProductImage:

import java.awt.*;

public class ProductImage extends Canvas { private Image piece;

public String label; public int height; public int width;

public String Description; public void setLabel(String s) {

label = s;

}

public void setImage(Image i) { if (piece !=i) {

piece = i;

height= piece.getHeight(this); width= piece.getWidth(this);

}

}

Мы не затеняем метку, ширину, высоту и описание, но мы хотим затенить часть Image. Методы setLabel и setImage вызываются с соответствующими объектными параметрами, и экземпляр ProductImage хранит их для будущего использования. Описание устанавливается непосредственно ссылкой на переменную Description в экземпляре ProductImage. Этот объект тоже базируется на классе Canvas, так что мы можем добавлять его непосредственно к панели, которая показывает изображение товара:

public void paint(Graphics g) { Rectangle r = bounds(); g.setColor(getBackground()); g.fillRect(0,0,r.width,r.height); if (piece!=null) {

g.drawImage(piece,0,0,this);

}

}

Теперь мы рисуем изображение, переданное нам методом setImage в экземпляре ProductImage, так что оно будет готово к добавлению к панели, которая показывает изображения товаров.

www.books-shop.com