    Глава 6 

    Апппеты 

    Java, как мы уже говорили,— это гибкий, безопасный, 
объектно-ориентированный язык, хорошо приспособленный для работы в сети. Но 
чем еще помимо сказанного он примечателен? Конечно, существуют и другие 
объектно-ориентированные интерпретируемые языки, имеющие подобные черты. 
Например, Visual Basic компилируется в Р-код, представляющий собой набор 
интерпретируемых инструкций, похожий на байт-код Java (теперь VB-код также 
можно компилировать непосредственно в исполняемые файлы). Так почему же именно 
язык Java стал таким популярным? 
    Несомненно, одной из главных причин столь широкого его распространения 
стали апплеты. Апплеты — это вложенные выполняемые объекты, которые можно 
передавать через Internet и включать в WWW-страницы. Апплеты дали 
программистам Internet то, о чем они всегда мечтали: легко распространяемые и 
платформно-независимые выполняемые модули. 
    Апплет является объектом Java. По своей концепции он аналогичен 
OCX-объекту в Windows или элементам~управления ActiveX. Апплет существует в 
1тррг_рС1мме-контейнере и, взаимодействуя с ней, предоставляет пользователю 
возможноспГнаполнять Web-страницы интерактивным содержанием. В частности, 
апплет может быть простым интерфейсом для удаленных баз данных или отображать 
сложную анимацию. Апплет может отображать таблицы и графики (например, 
информацию об уровне воды в реке или о самой свежей информации с фондового рынка). 
    Обычно апплет встраивается в броузер, например в Web-броузер, хотя уже 
имеются и экспериментальные образцы броузеров, созданных на основе электронной 
почты, которые позволяют пересылать интерактивные апплеты по e-mail. Несмотря 
на то, что эта глава посвящена только апплетам, применяемым в Web-броузерах, 
следует помнить, что апплеты не ограничиваются работой в WW.W. Потенциально 
существует много ДрУ^Ихсп^обрв пересыдкд апплетов.	~ "~ ~~' ~~~~ 
    Поскольку ё^еможность распространять апплеты была одной из популярнейших 
особенностей Java, в среду Visual J++ добавлены новые средства, ускоряющие и 
облегчающие создание апплетов. Наиболее заметной среди них является утилита 
Applet Wizard, руководящая начальными стадиями создания апплетов. Эта утилита 
аналогична AppWizard из Visual C++: она 

    177 

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

    Анатомия апплетов 

    Что такое апплет? Класс Applet является частью ядра Java и входит в пакет 
java.applet. При создании апплета необходимо включить оператор import в код 
класса в виде одной из следующих строк: 

    import java.applet.Applet; 

    ИЛИ 

    import java.applet.*; 

    На рис. 6.1 показано, какое положение занимает класс Applet в иерархии 
классов. Апплет является контейнером. Контейнер — это такой тип компонентов, 
который может содержать другие компоненты и должен сам содержаться в другом 
контейнере. (Подробно о контейнерах говорилось в главе 4.) Апплет, как видно 
из рис. 6.1, порожден от класса Component и занимает некоторую прямоугольную 
область поверхности отображения в родительском контейнере. Для апплетов 
родительским контейнером служит Web-броузер, поэтому апплет отображается на 
странице броузера. На рис. 6.2 показана страница с апплетом, отображенная с 
помощью программы Internet Explorer. 

    java.lang.Object    j 
    !'. Java.awt.Component j 
    I   Java.awt.Container 1 
    L___..  ^ 
    I    java.applet.Applet   } Рис. 6. l. Место класса Applet в иерархии классов 


    178	Visual J++: основы программирования 


    Рис. 6.2. Окно Web-броузера с изображением апплета 


    Броузеры аля апплетов 

    Отличие апплета от простого контейнера заключается в том, что апплет 
способен передавать интерактивное содержимое по Internet. Апплет находится в 
броузере, и состояние последнего может существенно влиять на поведение апплета. 
    Представим себе, что апплет размещен в начале обширной WWW-страницы и, 
отображая сиюминутное состояние фондовой биржи, передает брокеру заказы на 
покупку и продажу. Возможно, это примитивный способ использования апплета, но 
многие люди именно так и представляют себе его предназначение. Апплет 
действительно может поддерживать связь с сервером брокера, через который 
происходят покупки и продажи. Поэтому такой ~апплет значительно снизит 
пропускную способность канала пользователя постоянно загружающейся 
информацйей.ТЭсГёсли пользователь, просматривая столь объемную WWW-страницу, 
выйдет из области отображения апплета, должауц ли апплет продолжать загрузку и 
попытки отображения информациит^По крайней мере на то время, когда апплет 
исчезнет с экрана, должен ли он занимать полосу пропускания информацией, 
которая никогда не будет использована? 
    Конечно, нет. В этом и состоит различие между апплетами и простыми 
контейнерами. Апплет должен знать о состоянии броузера и реагировать на 

    Глава 6. Апплеты 179 

него. Кроме того, апплет должен уметь взаимодействовать со своим 
Web-броузером. Например, апплет может заставить броузер отобразить в одном из 
окон указанный URL или напечатать в окне состояния некоторое сообщение. 
Другими словами, апплет является контейнером с особыми возможностями, в том 
числе и способностью встраиваться в броузер. 
    Броузер для апплетов — это броузер, способный загрузить и запустить 
апплет. Поскольку апплеты являются Java-объектами, то в броузере должна быть 
реализована виртуальна машина Java, т.е. часть броузера должна представлять 
собой ВМ, предназначенную для запуска апплетов. В некоторых операционных 
системах уже имеется встроенная ВМ, в других она появится в ближайшем будущем. 
В таком случае отпадет необходимость во внутренней ВМ для броузера — он сможет 
использовать виртуальную машину операционной системы. Но пока апплет 
запускается именно в ВМ, реализованной в броузере. *—— 

    Встраивание апплетов в WWW-странгшы 

    Для встраивания апплетов в WWW-страницы используется тег <АР-PLETX В 
Visual J++ есть множество апплетов, встроенных в HTML-страницы, и в каждом из 
них можно увидеть пример тега <APPLET>. Так, HTML-файл со встроенным апплетом 
используется в примере Animation, находящемся в разделе Samples/Sun 
Samples/Animation Applet. Его тег <АР-PLET> выглядит следующим образом: 

    Opplet code=Animator.class width=460 height=160> 
    <param name=imagesource value="images/Beans"> 
    <param name=backgroundcolor value="OxcOcOcO"> 
    <param name=endimage value=10> 
    <param name=soundsource value="audio"> 
    <param name=soundtrack value=spacemusic.au> 
    <param name=sounds value="l.au|2.au|3.au|4.au|5.au|6.au|7.au|8.au|9.au|0.au"> 
    <param name=pause value=200> </applet> 

    Весь HTML-файл представляет собой набор тегов, начинающийся с тега 
<APPLET> и заканчивающийся тегом </APPLET>. В самом теге <APPLET> имеются три 
поля: Code, Width и Height. В поле Code указан класс апплета, который надо 
встраивать; поля Width и Height задают размеры прямоугольной области, в 
которой окно апплета размещается на WWW-странице. 
    I^e^Code показывает, что апплет относится к классу Animator. Значение поля 
Code —"Animator.class" — является именем класс-файла, а не простого фаъйГа^В 
действительности можно использовать как имя класс-файла, так__и^ имя класса. 
То есть в приведенном выше примере значением поля Code может быть и 
"Animator.class" и "Animator". 
    Значения полей Width и Height определяют ширину и высоту (в пикселях) 
прямоугольника, в котором размещается апплет. Объекты размещаются на 

    180	Visual J++: основы программирования 

    HTML-страницах статически. Апплет не может сам изменить свой размер или 
передвинуться по WWW-странице. В полях Width и Height точно указывается, 
какими должны быть размеры апплета, благодаря чему броузер сможет корректно 
разместить его на WWW-странице. 
    Есть еще два поля, которые можно включить в тег <APPLET>. Поле Name задает 
строку с именем апплета на WWW-странице. Это имя позволяет апплету получать 
ссылки на другие апплеты, выполняющиеся в то же самое время на WWW-странице. 
Как будет показано в следующем параграфе этой главы, методу getAppletQ класса 
AppletContext можно передавать строку с _именем апплета. Если поле_1^ате 
апплета, который в данный момент выполняетсяТ совпадает со строкой, переданной 
методу, то метод вернет ссылку на этот^пггл'й17Т1аки1Я~образом можно 
осуществлять связь между одновременно выполняемьши Ъпплетами WWW-страницы. 

    Как броузеры загружают апплеты 

    Другим необязательным полем тега <APPLET> является поле Codebase (поле 
базового адреса). Оно указывает броузеру, где на удаленном сервере искать 
апплет. По умолчанию при загрузке апплета броузер должен загружать класс-файл 
апплета с того же сервера, с которого он получил инкапсулирующую этот апплет 
WWW-страницу. Например, на WWW-сервере "www.myser-ver.com", имеющем URL 
"http://www.myserver. com/MyAppletPage.html.", есть WWW-страница, содержащая 
апплет. Тег <APPLET> выглядит следующим образом: 

    <APPLET CODE=MyApplet WIDTH=100 HEIGHT=200> </APPLET> 

    Для того чтобы загрузить класс MyApplet в ВМ, броузер автоматически 
создает URL "http://www.myserver.com/MyApplet.class". Этот адрес является 
комбинацией URL WWW-страницы и имени класс-файла ("MyApplet.class"). Если этот 
созданный броузером адрес отсутствует на WWW-сервере, то в окне состояния 
броузера, возможно, появится следующее сообщение: 

    ClassNotFoundException: class MyApplet not found. 

    Итак, броузер создает URL для классов апплета на основе URL HTML-страницы. 
Имя класс-файла ("MyApplet.class") по существу становится относительным IJRL, 
объединяясь с абсолютным URL страницы. На рис. 6.3 показана схема поиска 
броузером класс-файла через WWW. 

    Глава 6. Апплеты	181 


    |c:.\webfilcs 
    MyApplet.class 
    Встроенный апплет MyApplet 
    WWW-броузер клиента 

    Рис. 6.3. Броузер ищет класс-файл через WWW 

    Тег <CODEBASE> позволяет указать броузеру альтернативное местонахождение 
класс-файлов апплета. В следующем примере показано, как используется поле 
базового адреса в теге <APPLET>: 

    <APPLET CODE=MyApplet CODEBASE="/java/classflies/" 
    WIDTH=100 HEIGHT=200> </APPLET> 

    Броузер, который прочитает этот тег <APPLET>, будет искать файл 
MyApplet.class не по адресу "http://www.myserver.com/MyApplet.class", а по 
адресу "http://www.myserver.com/java/classfiles/MyApplet.class". Чтобы создать 
URL, он объединит имя сервера со значением поля базового адреса и ожидаемым 
именем класс-файла. На рис. 6.4 показано, как броузер комбинирует адрес 
HTML-страницы, значение поля базового адреса и имя класс-файла для поиска 
класс-файлов через WWW. 
    Поле базового адреса может использоваться для экономии пространства на 
дисках сервера. Вместо того чтобы хранить копии часто используемых 
класс-файлов в различных местах сервера, их можно записать в одной области, 
адрес которой указывается для каждого тега <APPLET> в поле базовою адреса. В 
противном случае в каждом каталоге WWW-сервера, содержащем WWW-страницу с 
апплетом, пришлось бы хранить копии всех необходимых класс-файлов. 
    Следует заметить, что в процедуру загрузки класс-файлов апплета в ВМ 
броузера входит и загрузка всех классов, необходимых для запуска апплета. 
Например, если класс MyApplet ссылается на определенное окно класса MyPanel, 
то ВМ броузера попытается загрузить и класс-файл класса MyPanel. Она будет 
искать его в том же каталоге, что и класс-файл апплета. Если URL для 
MyApplet.class имеет вид 
"http://www.myserver.com/java./classfiles/My-Applet.class", то для класс-файла 
MyPanel.class броузер создаст URL "http://www.myserver.eom/java./classfiles/MyPanel.class". 


    182 Visual J++: основы программирования 


    'www.server.com 
    Поиск класс-файла в каталоге common code вне корневого каталога сервера 
    hltp://www.server.com/samplc/Si-imple.litnil    1 
    Встроенный апплет MyAppIet с тегом <CODEBASE>, указывающим на катало!' 
/common code 
    WWW-броузер клиента 

    Рис. 6.4. Использование поля базового адреса для поиска класс-файлов 


    Архивы апплетов 

    Для каждого класс-файла, который надо загрузить, броузер создает новый URL 
и открывает HTTP-сессию. Если апплет состоит из 20-ти классов, каждый из 
которых необходимо запустить, то броузер должен создать 20 запросов на 
HTTP-сессию. С учетом того, что на открытие HTTP-сессии в Internet требуется 
несколько секунд, броузеру придется потерять более полуминуты на отправление 
этих запросов на загрузку класс-файлов. Это может привести к значительной 
задержке при загрузке апплета. 
    Кроме того, вызывает досаду невозможность сжатия (компрессии) класс-файлов 
до загрузки. Было доказано, что в среднем класс-файл можно сжать на 50%, 
используя архиватор PKZIP или какой-либо ему подобный. 
    Необходимость установления 20-ти соединений броузера с одним и тем же 
сервером да еще вынужденная загрузка несжатых файлов (которые, в принципе, 
можно легко сжать) приводят к потерям времени. 
    Для решения этих задач созданы два различных (но по существу одинаковых) 
механизма. В среду разработки Visual J++ включен инструментарий создания 
САВ-файлов, предназначенных для пересылки апплетов через WWW. Этот_ар,хиватор 
включен в Internet Explorer 3.0 и последующие версии. ИспользованиеЧГАВ-файлов 
возможно уже сегодня. 
    Аналогичный механизм имеется в Java версии 1.1. К ядру Java добавлены 
средства дЯедаботы с JAR-фажтами ("Java ARchive"). В большинстве нынеш-них 
броузеровТеще нет классов Java 1.1, хотя в ближайшем будущем они будут туда 
включены. О классах JAR можно прочитать на WWW-страницах JavaSoft. Домашняя 
страница JavaSoft находится по адресу "http://www.javasoft.com". 
    САВ-файлы и JAR-файлы концептуально представляют собой одно и то же, 
поэтому их можно просто называть архивными. Архивный файл 

    Глава 6. Апплеты 183 

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

    Заголовок 
    Сжатые класс-файлы 
    Сжатые файлы изображений 
    Сжатые аудиофайлы 
    Прочие ресурсы 

    Рис. 6.5. Архивный файл 


    Различие между САВ-файлами и JAR-файлами заключается в их формате. В 
САВ-файлах использован САВ-формат фирмы Microsoft ("кабинетный" — cabinet), 
употребляемый с некоторого времени в пакетах инсталляции. В этом формате 
сжатие отдельных файлов в архив осуществляется по алгоритму Лева-Зимпеля 
(Lev-Zimpel). (Этот же алгоритм применяется популярным архиватором PKZIP.) В 
JAR-файлах используется другой формат сжатия, основанный на стандарте ZLIB. 
Механизмы организации отдельных файлов в один архивный файл также различаются. 
Но, имея различные схемы сжатия и формат, САВ-файлы и JAR-файлы одинаковы по сути. 
    Собрав класс-файлы апплета и другие ресурсы (аудиофайлы и файлы 
изображений) в один архивный файл, можно существенно облегчить проблему 
загрузки класс-файлов. При использовании архивного файла броузеру для загрузки 
всех необходимых классов Java надо установить только один сеанс связи с 
WWW-сервером. Вместо того чтобы загружать класс-файлы по отдельности, броузер 
загружает один архивный файл, содержащий все класс-файлы. Снимается и проблема 
сжатия файлов, ибо все они уже сжаты. Используя архивные файлы, можно 
существенно уменьшить время загрузки. 

    Использование САВ-Фаилов 

    1АК:^>айлы в настоящее время еще не готовы к использованию. К большинству 
броузеров еще не добавлены классы Java версии 1.1, и они не готовы 
испТ5)*{»зовать JAR-классы. 
    В то же время Internet Explorer уже располагает средствами для работы с 
САВ-файлами. Среда разработки Visual J++ версии 1.1 включает в себя прикладные 
программы, предназначенные для создания САВ-файлов. В последующие ее версии 
будет включена утилита CAB Wizard, что сделает создание и распространение 
САВ-файлов еще более легкими. 

    184 Visual J++: основы программирования 

    Исполняемые программы создания САВ-файлов поставляются с Visual J++. Их 
можно установить с инсталляционного компакт-диска Visual J++ с помощью 
программы CABDEVKIT.EXE, расположенной в каталоге CABS&SIGN. Кроме того, при 
инсталляции Visual J++ можно будет воспользоваться соответствующей опцией. 
    Информация о создании и использовании САВ-файлов находится в файле 
OVERVIEW.HTM, расположенном в каталоге CAB&SIGN компакт-диска. В нем 
содержатся инструкции по созданию и использованию САВ-файлов в апплетах, 
создаваемых в Visual J++. Помимо этого там имеется и иного рода информация о 
САВ-файлах, в частности о цифровой подписи (соЗё "signing). 
    Параметры апплета 
    Апплеты по существу представляют собой Java-программы, встроенные в 
WWW-страницы. В апплете присутствуют методы initQ, start(), stopQ и destroyQ, 
аналогичные методу main() Java-программы. Прочие черты аппле-тов также делают 
их похожими на приложение. 
    В приложении параметры командной строки передаются статическому методу 
mainQ в виде массива astrArgsf] типа String. Но у апплета нет командной 
строки, поэтому пользователь ничего не может передать апплету. Апплет связан с 
HTML-страницей. Как передать параметры апплету? 
    В одном из предыдущих примеров в теге <APPLET> содержались теги <PARAM>. 
Именно с помощью тегов <PARAM> можно передать параметры объекту класса Applet, 
встроенному в HTML-страницу. 
    Между тегами <APPLET> и </APPLET> можно разместить теги <PARAM>. Каждый 
такой тег передается апплету. Формат тега <PARAM> следующий: 

    <PARAM NAME="paramname" VALUE="paramvalue"> 

    Тег <PARAM> позволяет указать простую пару ключ/значение, которыми может 
пользоваться апплет. При помощи параметров апплет можно сделать более гибким. 
Например, вместо создания апплета для выполнения конкретной анимационной 
последовательности, можно апплету Animator фирмы Sun передать группу 
параметров, в результате чего его можно применять для выполнения почти любой 
анимационной последовательности. Ниже снова приведены теги <PARAM> HTML-файла 
апплета Animator: 

    <param  name=imagesource  value="images/Beans"> 
    <param  name=batAgroundcolor  value="OxcOcOcO"> 
    <param name=endimage value=10> 
    <param  nan\ef=soundsource  value="audio"> 
    <param name=^eundtrack value=apacemusic.au> 
    <param name=sounds 
    value="l.au|2.au|3.au|4.au|5.au|6.au|7.au|8.au|9.au|0.au"> 
    <param name=pause value=200> 


    Глава 6. Апплеты	185 

    При использовании тегов < РАКАМ > апплет Animator может получить параметр 
с именем "imagesource", значение которого "images/Beans"; параметр с именем 
"backgroundcolor" и значением "ОхсОсОсО"; параметр с именем "endimage" и 
значением "10" и т. д. Всего апплету Animator переданы семь параметров. 
Применяя их значения, апплет может использовать различные последовательности 
изображений, разные цвета фона и звуковые последовательности, может 
увеличивать и уменьшать скорость кадров анимации. Итак, вместо того чтобы 
создавать апплет, который может отображать только заданную последовательность 
изображений в указанном порядке с заданной фоновой музыкой, вы можете получить 
апплет, отображающий почти любую анимационную последовательность, использующий 
любой цвет фона и любую фоновую музыку. Аналогично и другие апплеты можно 
создавать настолько общими, насколько это возможно, передавая им различные 
параметры. Один из методов класса Applet называется getParameterQ. 
Единственным аргументом этого мет ода служит строка, являющаяся именем одного 
из параметров HTML-файла, содержащегося в теге <APPLET>. Этот метод возвращает 
значение указанного параметра. Если параметр не существует, метод возвращает 
значение null. Ниже приведен пример кода, который пытается создать URL на 
основании значения параметра апплета, называющегося "URL".	I 

    class MyApplet extends Applet {	i // Код апплета...                                                 I 
    // Метод для получения URL, указанного параметром "URL" апплета. public 
URL getURLparam(){ URL url; try { 
    url = new URL(getParameter("URL")) ; } catch (MalformedURLException mue) { 
url = null; 
    return url; 
    i 
    //«Оставшийся код апплета. 


    Четыре метопа управления жизненным никлом апплета 

    Апплет исполняется в среде броузера, и его жизненный цикл определяется 
четырьмя методами класса Applet, именно: init(), start(), stop() и destroyQ. В 
большинстве обычных апплетов реализован по крайней мере один из этих 
унаследованных методов. 

    186	Visual J++: основы программирования 

    Метод init() вызывается ВМ броузера, когда апплет впервые загружается, но 
до того, как он отображается. Это наиболее подходящее место для инициализации 
всех объектов апплета, в частности для создания таблиц или загрузки 
изображений, аудиофайлов и данных с удаленного сервера. ВМ броузера может 
вызвать метод initQ только один раз в течение жизненного цикла апплета. 
    Методы startQ и stopQ связаны с отображением апплета на экране. Метод 
startQ вызывается всякий раз, кбгд<Гна экране появляется HTML-страница, 
содержащая апплет. Когда апплет убирают с экрана либо с помощью полосы 
прокрутки, либо при отображении броузеррм другой HTML-страницы, вызывается 
метод stopQ.	~7 
    Методы start() и stopQ предназначены для управления выполнением апплета. 
Например, если у апплета имеется активный фоновый поток, который вычисляет 
движение персонажа в какой-либо видеоигре, то этот поток надо запускать только 
после вызова метода startQ, а остановить его можно после вызова метода stopQ, 
реализованного в апплете. 
    Это только часть того, что делает апплет "благонадежным гражданином" WWW. 
Если вы создали фоновый поток таким образом, что он продолжает свою работу 
даже тогда, когда апплет не отображается на экране, это значит, что апплет 
понапрасну расходует системные ресурсы. Если такое происходит со всеми вашими 
апплетами, то пользователь может решить, что они выполняются медленно и их 
лучше вообще не запускать. 
    Вызовы методов startQ и stop() парные. Каждому вызову метода startQ 
апплета соответствует ровно один вызов метода stopQ. Причем метод startQ 
всегда вызывается после вызова метода initQ и до вызова метода stopQ. 
    Последний метод жизненного цикла апплета — метод destroyQ. Этот метод 
вызывают только один раз, и он предшествует окончательному завершению апплета. 
Вызов этого метода происходит, когда броузер собирается отобразить другую 
HTML-страницу или когда пользователь закрывает броузер. Ресурсы, выделенные 
при вызове метода initQ, должны освобождаться методом destroyQ. Конечно, при 
наличии механизма сбора мусора не обязательно освобождать объекты, поскольку 
сборщик мусора в любом случае удалит их. Необходимо избавляться только от тех 
ресурсов, которые могут задержать уничтожение апплета: фоновых программных 
потоков, которые не удалились методом stopQ (возможно, их перевели в режим 
ожидания последующего вызова метода start()), или сокетов, которые были 
созданы во время выполнения апплета. 
    "    Па рис. б.буДоказана последовательность вызовов методов initQ, 
startQ, stopQ и destroyQ, реализованных в апплете. 
    Далее ^приведен код класса Applet. Этот апплет отображает значение 
таймера. Танкер управляется фоновым потоком, который на одну секунду переходит 
в режим ожидания перед вызовом метода repaint() для перерисовки поверхности 
отображения, связанной с апплетом. Показана обычная методика управления 
жизненным циклом фоновых потоков: переменная m_threadTimer класса Applet 
содержит ссылку на программный поток таймера, 

    Глава 6. Апплеты	187 

который считается запущенным. В каждом цикле программный поток таймера 
проверяет, ссылается ли на него данная переменная. Изменение значения 
переменной становится сигналом для завершения фонового потока. 

    initO { 
    Г start() { 
    Г stop() { 
    Г deestroy() 
    Г 
    N 
    N+l. 
    N+2 

    Рис. 6.6. Последовательность вызова методов init(), startQ, stopO и destroy() 


    import Java.applet.Applet; import java.awt.*; import java.util.*; 
    class TimerApplet extends Applet implements Runnable { // 
Private-переменная включает ссылку на поток // таймера, выполняемый в данный 
момент. private Thread m_threadTimer; 
    // В методе init() создается объект типа Font, 
    // который будет использован в качестве шрифта апплета 
    // для отображения текущего времени. 
    public void init() { 
    Font f = new Font("Helvetica", Font.BOLD I Font.ITALIC, 15). 
    setFont (f); } 
    // В методе start()создается и запускается поток таймера, public void 
start () { 
    * m_threadTimer = new Thread(this); 
    m threadTimer.start(); 
    // В методе stopO поток таймера приостанавливается. Это W/ осуществляется 
путем уничтожения ссылки на поток (m_threadTimer) /Г* В следующей итерации 
цикла таймера в методе run() поток // обнаружит изменение ссылки и немедленно 
завершит метод // run(). public void stop() { 
    m threadTimer = null; 


    188 Mi. Visual J++: основы программирования 


    // В методе destroy() происходит удаление ссылки на поток, // если броузер 
случайно не вызвал метод stop О . public void destroy(} ( m threadTimer = null; 
    // В методе run(), запускаемом любым фоновым потоком // таймера, 
осуществляется цикл таймера. //В каждой итерации вызывается метод repaint(). 
// Когда переменная m_threadTimer перестает ссылаться на // выполняемый в 
данный момент поток, метод завершается, public void run() ( do { 

    repaint(); try { 
    Thread.currentThreadO .sleep(lOOO); }   catch   (InterruptedException  ie) 
   { 
    System.out.println("Timer  thread was  interrupted:"   +  ie) break; } }   
while(m_threadTimer  ==  Thread.currenthread()); 
    } 
    // Отображение текущего времени в методе paint О . public void 
paint(Graphics g) { 
    g.setFont(getFont() ) ; 
    FontMetrics fm = g.getFontMetrics(); 
    Date date = new Date(System.currentTimeMillis()); String strTime = "" + 
date.getHours() + ":" + 
    date.getMinutes() + ":" + date.getSeconds(); 
    int ex = fm.stringWidth(strtime); int cy = size().height/2; 
    g.drawstring(strTime, ex, cy); 


    Использование Applet Wizard 

    Для быстрого создания кода апготетов в Visual J++ включена утилита Applet 
Wizard. Создаваемый ею код представляет собой полный каркас апплета, который 
можно заполнить конкретным содержанием. В Applet Wizard имеются средства для 
указания параметров апплета и создания по умолчани*!о*?5НТМЬ-страницы, 
обеспечивающей визуализацию и загрузку апплета, средства для запуска апплета в 
броузере или в качестве независимого приложения в окне верхнего уровня, а 
также средства поддержки многопотоковой анимации. Applet Wizard экономит 
время, которое в противном случае было бы потрачено на набивание кода каркаса 
апплета и его отладку. Это одна из наиболее замечательных возможностей Visual J++. 

    Глава 6. Апплеты	189 

    Для создания апплета при помощи Applet Wizard нужно сначала открыть новый 
проект в Developer's Studio. Это можно сделать, выбрав команду New... в меню 
File главного меню, а затем строку Project Workspace в диалоговом окне New. 
Ниже показано диалоговое окно New с выделенным пунктом Project Workspace. 
    В диалоговом окне могут появиться не все пункты из числа показанных на 
рисунке. Указанные пункты представляют типы элементов, которые можно создавать 
в среде разработки Visual J++, инсталлированной в данной системе. 
    При нажатии на кнопку ОК появляется диалоговое окно New Project. Для 
создания апплета с помощью Applet Wizard из списка Туре диалогового окна надо 
выбрать элемент Applet Wizard. В поле Name отображается имя проекта, который 
необходимо создать, в поле Location — каталог, в который следует поместить 
новый проект. На следующем рисунке показано, как нужно заполнить поля 
диалогового окна New Project Workspace, чтобы создать апплет 
"AppletWizExample" в каталоге "C:\MSDEV\Projects\AppletWizExample". 


    190 Visual J++: основы программирования 


    Для запуска Applet Wizard следует нажать кнопку Create в диалоговом окне 
New Project Workspace. Далее последует серия из пяти диалоговых окон, в 
которых надо указать характеристики и особенности нового апплета. 
    В первом диалоговом окне, называемом "Java Applet Wizard — Step 1 of 5", 
задаются некоторые общие параметры. Ниже показан внешний вид первого 
диалогового окна Applet Wizard. 
    Вот  что  необходимо  указать  в  этом  диалоговом   окне,   отвечая  на 
поставленные вопросы. 
    •    "How would you like to be able to run your program?" — "Как вы 
собираетесь запускать Вашу программу?" Здесь имеются две опции. Можно создать 
апплет, запускаемый только в броузере, а можно — запускаемый и в броузере, и в 
качестве независимого приложения Java в окне верхнего уровня. При выборе 
второй опции апплет в случае запуска в качестве приложения не будет иметь 
доступа к классу AppletContext (подробно описанному ниже). Без этого класса не 
будут работать некоторые из унаследованных методов, например showStatusQ и 
showDocumentQ. Эти методы предполагают, что апплет запущен в броузере, и при 
запуске в виде независимого приложения приводят к выдаче исключений. 
    •    "What would you like to name your Applet class?" — "Как вы хотите 
назвать свой аптвдет-класс?" По умолчанию в качестве имени апплета 
используется имя проекта, но можно указать и любое другое имя. 
    •    "Would you like to generate source file comments?" — "Хотите ли вы, 
чтобы в исходный файл были вставлены комментарии?" Здесь можно выбрать тип 
комментариев, автоматически добавляемых Applet Wizard к файлам 

    Глава 6. Апппеты 191 

    исходного текста аттлета. При выборе опции Explanatory Comments будут 
добавлены комментарии, объясняющие назначение каждого метода и каждой 
переменной класса. При выборе опции TODO Comments к исходному тексту будут 
присоединены комментарии, указывающие, где следует добавить собственный код к 
классу Applet, созданному Applet Wizard. Например, метод initQ будет снабжен 
следующим комментарием: 

    // TODO: Разместить код дополнительной инициализации. 

    После заполнения всех полей этого диалогового окна следует нажать кнопку 
Next, чтобы перейти к следующему диалоговому окну. Во всех диалоговых окнах 
имеются кнопки Back, предназначенные для изменения выбора опций предыдущих 
диалоговых окон. 
    Во втором диалоговом окне Applet Wizard производится запрос HTML-файла, 
который следует использовать при просмотре и отладке апплета в Visual J++. 
Если в поле "Would you like a sample HTML file?" выбрана опция "Yes, please", 
то будет создан простой HTML-файл, в который будет встроен созданный апплет. 
Имя нового HTML-файла будет состоять из имени проекта и расширения HTML. К 
созданному файлу можно будет в будущем вернуться, с тем чтобы произвольным 
образом модифицировать его. 
    При создании с помощью Applet Wizard нового HTML-файла надо указать 
размеры встроенного в него апплета. По умолчанию его размеры составляют 
320x240 пикселей. Те значения, которые вы введете в это диалоговое окно, будут 
использованы для заполнения полей Width и Height тега <APPLET> в HTML-файле. В 
случае необходимости изменить значения указанных полей HTML-файл можно 
отредактировать. Для перехода к следующему диалоговому окну следует нажать 
кнопку Next. 

    192 Visual J++: основы программирования 

    В третьем диалоговом окне Applet Wizard будут заданы два вопроса: 
    •    Должен ли апплет поддерживать многопотоковую обработку, и если да, то 
надо ли добавить код для отображения анимации? 
    •    Нужен ли обработчик для таких событий,  как перемещение мыши, щелчки 
мышью и прохождение курсора мыши над объектом? 
    Если выбрана поддержка многопотоковой обработки, то Applet Wizard создаст 
класс Applet, j)e^H3yro^HJL jmjjjp^^ 
    который запускает фоновые потоки. Реализация метода run() представляет 
собой просто''цикл repaintO/sleepO," который перерисовывает апплет каждые 50 
мс. Ниже представлен код метода гап(), который создается при выборе опции 
поддержки многопотоковой обработки: 

    public void run() { 
    while (true) 
    { 
    try 
    { 
    repaint(); 
    Thread.sleep(50); ) 
    catch (InterruptedException e) { 
    // Исключение InterruptedException выдается методом 
    // Thread.sleep(). Это означает, что другой поток 
    / / прервал"лЭШШЙ" поток. 
    Stop(); 
    } 

    Если наряду с поддержкой многопотоковой обработки задается и поддержка 
анимации, то Applet Wizard добавляет в метод runQ код для отображения 
анимационной последовательности изображений. Этот код загружает 
последовательность изображений до запуска цикла repaint()/sleep(). Каждая 
итерация цикла repaint()/sleep() отображает в апплете следующее изображение. 
По умолчанию Applet Wizard снабжает создаваемый апплет анимационной 
последовательностью, представляющей собой вращающийся глобус, созданный из, 18 
различных файлов с расширением GIF. Ниже показан апплет, отображающий данную 
анимационную последовательность. Чтобы заменить эту последовательность другой, 
следует изменить количество изображений и имена их файлов. 

    Глава 6. Апплеты	193 

    Чтобы добавить в каркас апплета методы обработки событий, связанных с 
мышью, надо выбрать соответствующие опции в третьем диалоговом окне. В 
результате щелчка на названии опции "mouseUpO, mouseDown()" в апплет 
добавляются пустые методы mouseUpO и mouseDownQ, которые можно заполнить 
кодом, необходимым данному апплету для обработки этих событий. Аналогично, при 
выборе опции mouseMove(), mouseDragQ в апплет будут добавлены обработчики этих 
событий, так что он сможет реагировать на перемещение мыши и на 
"перетаскивание" ею объектов. Обработчики событий mouseEnter() и mouseExitQ 
вызываются, когда курсор входит в ограничивающий прямоугольник апплета в 
броузере или покидает его. Об этих методах и обработчике событий Java 
рассказано в главе 4. 
    В четвертом диалоговом окне Applet Wizard, представленном на следующем 
рисунке, указываются параметры, которые должен распознавать апплет. 

    194 Visual J++: основы программирования 

    В таблицу надо ввести следующую информацию: 

    Попе     Описание 

    Name	Строка с именем параметра, находящаяся в поле Name тега <PARAM> HTML-файла. 
    Member	Имя переменной, которая должна содержать значение параметра. 
Апплеты, созданные Applet Wizard, автоматически считывают соответствующие 
значения всех переменных. 
    Туре	Тип переменной. Это диалоговое окно позволяет указывать только 
переменные встроенных типов: булевого, двойной точности, с плавающей запятой, 
целого, длинного и строки. Значениями булевого типа могут быть true или false. 
Для более сложных типов данных, в частности определяющих цвет или URL, надо 
применить переменную типа String и затем отредактировать код, созданный Applet Wizard." 
    Def-Value	Необязательное поле, указывающее, каким должно быть значение *   
параметра   по   умолчанию,   если   в   HTML-файле   нет   поля <PARAM>. 
    Description        ^-Содержимое этого поля возвращается методом 
getParameterlnfo() (описанным в следующем параграфе), поэтому пользователи 
класса Applet могут узнать, какие параметры апплет поддерживает и каковы их 
значения. Дает краткое описание параметра и определяет допустимый диапазон 
значений (например: "number of prime numbers to generate; 0-10"). 
    Applet Wizard  будет использовать эту таблицу для  создания  кода,  в 
котором осуществляется чтение параметров апплета. Если апплет можно 

    Глава 6. Апплеты 195 

    будет запускать в качестве приложения, то параметры должны быть указаны в 
командной строке^ следующим образом: 

    jview MyAppletClass paraml=vall  param2="va!2  with  spaces" param3=number 
    Пятое диалоговое окно показано ниже: 
    |Name: AppletWizExample 
    _ Лог: Brian Miso 
    (Created with Microsoft Visual J++ Version 1,0| 
    i. fiacK 
    Finish 
    Cancel 

    В этом диалоговом окне надо указать строку, возвращаемую методом 
getAppletlnfoQ апплета. Данный метод, объявленный как public, используется для 
сбора информации об апплете извне, например броузером или другим объектом 
Java. В эту строку следует включить такую информацию, как имя автора, 
сообщение об авторских правах и т.д. По умолчанию Applet Wizard добавляет строку: 

    Created with Microsoft Visual  J++ Version   1.0 

    Если вы не хотите рекламировать фирму Microsoft, указанную строку из 
Appletlnfo можно убрать. 
    Заполнив перечисленные поля, вы должны нажать кнопку Finish. Applet Wizard 
создаст апплет и рабочее пространство для него, которое будет загружён^ в окно 
ClassView. Каркас апплета готов к запуску. При желании его можно 
откомпилировать. Правда, такой апплет не может выполнять что^гщбудь 
существенное. Просмотрев код, созданный Applet Wizard для данного класса, вы 
увидите, что значительная часть работы по созданию нового апплета уже сделана. 
Теперь начинается главное. В апплет необходимо добавить коды инициализации, 
деструктора, обработки событий и фоновых потоков. Желаю удачи! 

    196 Visual J++: основы программирования 


    Класс Applet 

    В этой главе уже говорилось о четырех методах, которые определяют 
жизненный цикл апплета: initQ, startQ, stopQ и destroyQ. В классе Applet 
имеются и некоторые другие методы, предназначенные для облегчения работы с апплетами. 

    Методы предоставления информации об апплете 

    Это два почти стандартных метода, которые позволяют узнать о подробностях 
работы апплета. Каждому, кто создает экземпляр класса Applet, может 
понадобиться информация, например, об авторе апплета или способе связи с ним. 
    Чтобы получить строку, в которой в пригодном для чтения виде содержатся 
сведения о праве собственности и прочих юридических вопросах относительно 
данного апплета, необходимо переопределить метод getApplet-Info() в созданном 
классе типа Applet. В эту строку можно включить имя автора, информацию об 
авторских правах, URL WWW-страницы, на которой пользователь может найти 
необходимую информацию, в частности более подробное описание классов данного 
апплета. Например, Applet Wizard no умолчанию создает метод getAppJetlnfoQ, в 
результате чего возвращается строка приблизительно следующего вида: 

    "Name: <Applet class name>\r\n" + 
    "Author: <your name here>\r\n" + 
    "Created with Microsoft Visual J++ Version 1.0"; 

    Конечно, в эту строку могут быть добавлены информация об авторских правах 
и еще какие-либо дополнительные данные или убрана ссылка на Visual J++. 
    Метод getPararneterlnfoO класса Applet должен возвращать список 
параметров, принимаемых апплетом, а также информацию о типе этих параметров. 
Синтаксис метода getParameterlnfoQ такой: 

    public String[][] getParameterlnfo(); 

    Возвращаемым значением переопределенной реализации этого метода будет 
массив трехстрочных элементов. То есть каждый элемент возвращаемого массива 
.является массивом, состоящим из трех строк. Формат такого массива приведен ниже: 
    {"parате^цг  name",   "parameter  data  type",   "Description"} 
    Строка имени параметра — это та самая строка, которая будет находиться в 
поле Name тега <PARAM> HTML-страницы. Вторая строка описывает формат данных 
этого параметра (например, тип int, long или url). Эти строки могут быть 
произвольного вида. Информация о типах данных должна быть краткой и 
конкретной. В строке описания может находиться все что угодно, 

    Глава 6. Апплеты	197 

хотя наиболее целесообразным будет включить в нее описание используемых 
апплетом параметров и диапазон их допустимых значений. Так, ниже описан 
параметр с именем "numFrames", являющийся целым значением из диапазона от 1 до 100: 

    {"numFrames", "int", "Number of animation frames: 1-100") 
    Реализация метода getParameterlnfoQ выглядит следующим образом: 
    private static String!][] m_aastrParamInfо = ( 
    {"numFrames", "int", "Number of animation frames: 1-100"} {"backcolor", "OxOOrrggbb", 
    "Backgroundcolor to use: in packed red/green/blue 
    hex format"}, 
    {"imageBaseName", "string", '	"Base to use for generating image filenames" + 
    ": <basename>00.gif is used for first image") }; 
    public String[][] getParameterlnfo() { 
    return m_aastrParamInfo; } 


    Методы взаимодействия 5роузепа и апплета 

    Обычно апплет запускается под управлением броузера. Каждому создаваемому в 
броузере объекту класса Applet передается экземпляр класса AppletContext. 
Посредством этого экземпляра апплет может общаться с броузером и в некоторых 
пределах управлять им. Именно с помощью экземпляра класса AppletContext апплет 
может, в частности, загрузить изображения и аудиофайлы из Internet. Апплет 
может заставить броузер отобразить строку в окне состояния, передав ее методу 
showStatusQ класса AppletContext. 

    Загрузка изображений и аудиофайлов 

    В следующем параграфе, где описаны ограничения по работе апплета, 
связанные с безопасностью, сказано, что апплет не может устанавливать 
соединения через Internet с какой-то другой машиной, помимо обслуживающего его 
WWW-сервера. Однако это неверно. Апплет может связаться с любым другим 
сервером Internet с целью загрузки изображений и аудиофайлов. 
    Методу getlmageO класса Applet в качестве входного параметра передается 
URL. Возвращаемым значением является изображение, созданное из загруженного 
файла изображения, на который ссылается URL. (Этот метод фактически является 
оболочкой метода AppletContextgetImage(), поэтому именно объект класса 
AppletContext загружает изображение.) Независимо от того, существуют или нет 
файлы с изображением, этот метод все равно возвращает объект типа Image. Лишь 
при попытке вывести на экран это изображение или еще как-то его использовать 
начинается процесс загрузки. 

    198	Visual J++: основы программирования 

    Если методу getlmageQ передали неверный URL, то последующие операции 
приведут к выдаче исключения. 
    Аналогично методу getlmageQ метод getAudioClipQ используется для загрузки 
аудиофайлов из Internet. (Отметим, что на сегодняшний день создавать 
аудиоклипы можно только из файлов с расширением AU, частотой выборки 8 КГц и 
монозвучанием, в кодировке u-Law. Если вы не знаете, что это значит, или не 
можете создать файл такого типа, следует воспользоваться какой-нибудь свободно 
распространяемой программой преобразования, 1-гапгшмер Gold Wave. Эта и другие 
редактирующие программы могут преобразовывать p6Wsiwft^op_M?r_WAV, 
употребляемый в системе Windows, в формат AU* используемый в Java для 
аудиофайлов. Метод getAudioClipQ класса Applet служит оболочкой метода 
getAudioClip() класса AppletContext. 
    Аудиоклипы в Java представлены объектами типа AudioClip. В интерфейсе 
AudioClip имеется три метода — play(), stop() и 1оор(), которые можно 
использовать для того, чтобы запускать и останавливать проигрывание 
загруженных аудиоклипов. 
    В классе Applet есть несколько удобных методов, предназначенных для работы 
с объектами класса AudioClip. Метод playQ класса Applet принимает URL и 
начинает проигрывать аудиофайл, на который этот URL ссылается. Данный метод 
просто создает объект типа AudioClip из переданного LJRL и вызывает метод playQ. 

    Получение параметров 

    Метод getParameterQ класса Applet используется для получения значений 
параметров от броузера. Это те самые значения параметров, которые хранятся в 
тегах <PARAM> HTML-файла. Метод GetParameterQ принимает имя параметра апплета 
и возвращает строку со значением параметра. Если параметра с таким именем нет, 
метод возвращает null. 

    Метопы getDocumentBaseO n getCodeBaseQ 

    Метод getDocumentBaseQ возвращает URL для HTML-файла, в который встроен 
апплет. Фактически это единственный метод, с помощью которого мсшнр^определить 
имя сервера, с которого загружен апплет. Например, если необходимо загрузить 
изображение с именем "img0001.gif из подкаталога "images" того каталога, в 
котором находился HTML-файл, следует использовать код следующего вида: 
    
    Image img = getIMage(new URL(getDocumentBase(),"images/imgOOOl.gif")); 

    Здесь URL документа принимается в качестве базового адреса для URL 
изображения "images/imgOOO I .gif. 
    Если дЬщуп к классу Applet был осуществлен по URL, получаемому с помощью 
метода getCodeBaseQ, а не из каталога HTML-файла сервера, то для доступа к 
файлам может понадобиться базовый адрес программы, а не документа. В этом 
случае в приведенном выше фрагменте кода, предназначенного для загрузки файлов 
с сервера, вместо метода getDocumentBaseQ следует использовать метод getCodeBaseQ. 

    Глава 6. Апплеты	199 


    Методы класса AppletContext 

    Объект класса AppletContext — это абстракция броузера, в котором запущен 
апплет. Посредством данного объекта апгтлет может получить некоторую 
информацию о броузере и в ограниченных пределах управлять им. 
    Для получения ссылки на объект типа AppletContext следует использовать 
метод getAppletContextQ. Эта ссылка хранится в переменной класса Applet, 
объявленной как private, поэтому для доступа к объекту типа AppletContext 
апплет должен использовать именно этот метод. 
    Метод showStatusQ класса AppletContext помещает переданный ему текст в 
строку состояния броузера. (Если таковой нет, то вызовы этого метода объектом 
AppletContext будут игнорироваться.) Пример размещения текста в строке 
состояния броузера приведен ниже: 

    getAppletContext () .showStatus("Attempting to download files."); 

    Можно дать броузеру указание отобразить новый HTML-файл. Метод 
showDocumentO объекта AppletContext выбирает URL, и броузер немедленно 
начинает загрузку, отображая этот файл в окне апплета. Существует 
перегруженная версия этого метода, которая принимает второй параметр типа 
String,— имя^окна_броузера типа Frame, где надо отобразить новый 
HTML-документ. (Конечно, это можно сделать лишь при условии, что броузер в 
состоянии создать такое окно. В противном случае действие этого метода не 
определено.) Для того чтобы создать ряд окон типа Frame, используется тег 
<FRAMESET>. Впоследствии в одном из таких окон апплет может разместить новый HTML-документ. 

    Взаимодействие с ДРУГИМИ апплетами 

    С помощью объекта типа AppletContext можно получить ссылку на любой другой 
апплет, выполняемый в данный момент. Это позволяет координировать работу 
апплетов, находящихся на одной или разных HTML-страницах. Например, можно 
создать апплет-датчик, который будет отображать ход загрузки другого апплета. 
    Параметром метода getApplet () класса AppletContext является строка, 
хранящая значение поля Name другого апплета (взятое из тега <APPLET> в 
HTML-файле другого апплета). Метод возвращает ссылку на другой апплет, после 
чего можно вызвать методы апплета, ссылка на который представлена, опроошъ его 
классы .с помощьюоператора instanceof или вступить во взаимодействие с ним 
любым другим образом. 
    Некоторые броузеры игнорируют поле Name тега <APPLET>. Это не страшно. В 
классе AppletContext имеется метод, называемый getAppletsQ. Э'щт метод 
возвращает перечень апплетов, дополненный ссылками на все апплёты, запущенные 
в броузере на данный момент. Далее приведен код апплета, который пытается 
найти все другие апгатеты того же класса, выполняемые одновременно с ним, и 
передает им ссылку на себя: 

    import Java.applet.Applet; import java.awt.*; 


    2OO	Visual J++: основы программирования 


    import Java.util.*; 
    class CooperativeApplet extends Applet { 
    // Ссыпки на все другие апплеты класса CooperativeApplet, // выполняемыёПз 
это времяТ" 
    private Vector m_vectOtherApplets = new Vector(); 
    // Методы addRef() и removeRefO используются для передачи // ссылок между 
экземплярами объектов класса // CooperativeApplet. 
    public void addRef(CooperativeApplet a) { if(!m_vectOtherApplets.contains(a)) 
    m__vectOtherApplets . addElement (a) ; } 
    public void removeRef(CooperativeApplet a) ( if(m_vectOtherApplets.contains(a)) 
    m_vectOtherApplets.removeElement(a); 
    // Метод init() ищет все другие апплеты того же типа и // посылает им 
ссылку на свой апплет. public void init() { 
    Enumeration enumApplets = getAppletContext().getApplets() 
while(enumApplets.hasMoreElements()) { try i 
    CooperativeApplet a = 
    (CooperativeApplet)enumApplets.nextElement(); 
    m_vectOtherApplets.addElement(a); a.addRef(this); } catch 
(ClassCastException cce) { 
    // Возникло из-за того, что апплет не порожден // от класса 
CooperativeApplet. Он // игнорируется, и цикл продолжается. 
    // При удалении апплета .другие апплеты класса 
    // CoopetativeApplet должны удалить ссылку на него. 
    public void destroy() { 
    Enumeration enum = m_vectOtherApplets.elements(); 
    while(enum.hasMoreElements()) 
    ((CooperativeApplet)enum.nextElement()).removeRef(this); } } 

    В этот пример не включен код синхронизации фоновых программных потоков. 
Здесь просто демонстрируется "использование метода AppletCon-text.getApplets. 

    Глава 6. Апплеты	2О1 


    Ограничения, налагаемые на апплеты в целях безопасности 

    Апплеты содержат исполняемый код, поэтому при распространении по сети они 
могут легко стать переносчиками вирусов и прочих кодов-злоумышленников, 
присутствие которых в системе нежелательно. Вирус обычно прячется внутри 
исполняемого кода и в "подходящий" момент переходит на другой компьютер. 
Исполняемый код, загруженный с диска или по Internet, с зараженного компьютера 
передается на следующие и заражает их. 
    Несложно представить себе вирусы, использующие апплеты для перемещения от 
компьютера к компьютеру. Ведь исполняемый код передается от WWW-сервера к 
компьютеру, на котором запущен WWW-броузер. Так можно заразить всю WWW! 
    Вот почему предусмотрено множество ограничений, налагаемых на апплеты, 
которые способны инфицировать систему. Если все эти ограничения полностью 
реализовать в ВМ Java, то использовать апплеты для передачи вирусов через WWW 
станет невозможно. К сожалению, побочные запреты затрудняют работу апплетов. В 
этом параграфе рассказано о налагаемых на апплеты ограничениях и о том, как 
можно безопасно компенсировать связанные с этим потери производительности. 

    Что не разрешено 

    Возможно, самое важное из налагаемых на апплеты ограничений — это 
запрещение апплетам записывать какие-либо данные в какую бы то ни было 
локальную систему файлов на машине броузера. Код апплета на машине клиента не 
может открывать и создавать файлы, а также работать с каталогами. 
    Попытка открыть объекты типа File, FilelnputStream или FileOutputStream на 
машине клиента (машине, на которой выполняется апплет) приведет к выдаче 
исключения, обусловленного нарушением безопасности. Так, приведенный далее код 
вызывает выдачу такого исключения, если апплет встроен в HTML-страницу: 

    import Java.applet.Applet; import java.awt.*; 
    public'class BadApplet extends Applet { 
    puby.c void init() { try { 
    FilelnputStream in = new FilelnputStream( ***	getParameter ( "f ileToOpen" 
) ) ; ^ } catch (Security-Exception) { 
    System.out.println("SECURITY PROBLEM TRYING TO OPEN 
    FILE!"); ) } ) 


    2O2	Visual J++-. основы программирования 


    Менеджер Безопасности п апплеты 

    Объектом, ответственным за прекращение потенциально опасных действий 
апплета, является SecurityManager. Подробно об объекте SecurityManager 
рассказано в главе 11. 
    Вот краткое описание того, как действует объект SecurityManager. У каждого 
объекта типа System имеется один объект SecurityManager. При попытке выполнить 
потенциально опасные операции у менеджера безопасности запрашивают, все ли в 
порядке. Если менеджер безопасности выдает разрешение на операцию, выполнение 
программы продолжается. —————— 
    Если же операция не разрешена, выдается исключение, обусловленное 
нарушением безопасности. Как было показано в предыдущем примере, выдача такого 
исключения осуществляется при попытке открыть файл из апплета. 
    Прл_шдолнении приложений Java менеджер безопасности не запуска-: _ется. 
Разрешается осуществлять любые операции, в том числе и те, выполнение которых 
может привести к нарушению безопасности. Ведь любое запускаемое приложение 
"проверено" пользователем, поэтому ему разрешено выполнять практически все. 
    Однако апплеты не загружаются и не запускаются пользователем, поэтому их 
"благонадежность" не подтверждена. ВМ броузера автоматически запускается с 
менеджером безопасности, который не позволяет выполнять "подозрительные" 
операции. Далее рассказано, как в Java обеспечивается безопасность апплетов. 

    Выполнение операции с Файлами 

    Итак, в Java нельзя выполнять операции, связанные с записью в файлы. Это 
может стать проблемой, особенно если апплету приходится хранить информацию в 
промежутках времени между сеансами. Предположим, вы создаете апплет, который 
играет с пользователем в шахматы. Большинству шахматных программ для выбора 
следующего хода требуются минуты или даже часы. Если пользователь не хочет 
ждать столько времени, ему будет удобнее сохранить игру, с тем чтобы 
продолжить ее позднее. Но, не сохранив информацию о шахматной позиции на 
машине клиента, как можно отложить игру? 
    Если нельзя записать информацию об игре на машине клиента, то где же 
еще^ее можно хранить? На машине сервера! Пользователю известна машина, 
представившая апплет, поскольку ее IP-адрес либо имя являются частью URL, 
значение* которого возвращается методом getDocumentBase(). Через Internet 
информацию можно послать на сервер и сохранить неоконченную игру на мИЙщне 
сервера. Когда пользователь опять вернется на страницу с апплетом, 
предназначенным для игры в шахматы, он может указать некоторый 
идентификационный номер сохраненной игры. Сервер передаст информацию апплету, 
и игру можно будет продолжить. На рис. 6.7 показано, как можно передавать 
данные от апплета к серверу и, спустя некоторое время, от сервера к апплету. 

    Глава 6. Апплеты	2ОЗ 


    (а) <маркер отправления данных>, "bob," <байты данных ...> (6) <маркер 
получения данных>, "bob," <байты данных ...> 
    pTOK-fleifOH, выполняемый на сервере 
    Машина www.server.com 
    Хранение данных 
    в файле с именем "bob" 
    'Чтение данных из файла с именем "bob" 
    Апплет в броузере 
    ЙЁЙРга8е 
    Файловая система сервера 

    Рис. 6.7. Запись данных для длительного хранения на сервере и их 
последующее считывание 

    Как видно из рис. 6.7, для осуществления подобных операций на машине 
сервера должна быть запущена программа, готовая принимать данные от апплета. 
Используемый WWW-сервером протокол HTTP не предоставляет механизма хранения 
данных на сервере. Поэтому надо создать некоторую исполняемую программу, 
которая запускается на сервере специально для хранения и пересылки данных 
назад пользователю. 
    Есть несколько способов пересылки данных от апплета к удаленному 
пользователю Internet. Однако, как будет показано в следующем параграфе, 
апплету „разрешено устанавливать сетевое соединение только с машиной сервера. 
При попытке послать данные от апплета к удаленной системе непосредственно 
„связаться можно только с WWW-сервером, с которого пришел апплет. 
    Ниже представлен простой вариант реализации механизма сохранения 
файла^йредназначенного для работы в Internet. Апплет_осуществляет_соеди-нение 
с сервером через сокет и посылает команду ^сохранить файл". Этим он указывает 
приложению сервера, что пересылаются данные, которые необходимо сохранить. 
Затем апплет пересылает идентификатор файла, в котором надо сохранить данные, 
потом — сами данные. 

    2О4 Visual J++: основы программирования 

    Для того чтобы через некоторое время получить данные, апплет снова 
соединяется с приложением сервера. Сначала он пересылает через сокет символ 
"послать данные", за которым следует идентификатор файла, хранящего эти 
данные. Затем программа, располагающаяся на сервере, отсылает указанные данные 
апплету. Ниже представлен код, который необходимо добавить к коду класса 
апплета, чтобы разрешить ему сохранить данные на сервере этого типа: 

    import Java.applet.Applet; import java.awt.*; import java.net.*; import java.io.*; 
    public class MyStoringApplet extends Applet { 
    public static final int m_nStorageServerPort = 1001; 
    // Код апплета... 
    // Метод saveRemoteData() принимает массив байтов, который 
    // следует сохранить на удаленной машине сервера, 
    // и имя файла, предназначенного для хранения данных. 
    public void saveRemoteData(String strFilename,byte[] abData, int offset, 
    int length) { 
    // Сеанс связи через сокет с сервером, обслуживающим // апплет. Переменная 
m_nStorageServerPort используется // в качестве номер_а порта, с которым . 
осуществляется // соединение.         "" 
    DataOutputStream out = null; try { " """ """"  '"' 
    InetAddress addr = InetAddress.getByName( getDocumentBase().getHost()); 
    Socket sock = new Socket(addr, m_nStorageServerPort); 
    out = new DataOutputStream(sock.getOutputStream()); } catch 
(UnknownHostException uhe) { 
    System.out.println("Could not lookup host."); 
    return; } catch (lOException ioe) ( 
    System.out.println("Could not connect back to host."); 
    return; } try { 
    //Пересылка команды "сохранить данные". 
    out.writelnt(0); 
    Ч»ч 
    ^i/ Пересылка имени файла, предназначенного для 
    // хранения данных. 
    out.writeUTF(strFilename); 
    // Запись данных. 
    out.write(abData, offset, length); 


    Глава 6. Апплеты	2О5 


    // Закрытие потока данных, проходящего через сокет. 
    out.close (); } catch (lOException ioe) { 
    System.out.println("Problem writing data to server:" + ioe), 
    return; } 
    // Метод getRemoteData() принимает имя сохраненного файла 
    // данных. Возвращает массив байтов. 
    public byte[] getRemoteData(String strFilename) { 
    // Соединение через сокет с сервером, обслуживающим 
    // апплет. Переменная m_nStorageServerPort используется 
    // в качестве номера порта соединения. 
    DataOutputStream out = null; 
    InputStream in = null; 
    try { 
    InetAddress addr = InetAddress.getByName( getDocumentBase () .getHost ()) ; 
    Socket sock = new Socket(addr, m_nStorageServerPort); 
    out = new DataOutputStream(sock.getOutputStream ()); 
    in = sock.getlnputStream(); } catch (UnknownHostException uhe) { 
    System.out.println("Could not lookup host."); 
    return null; } catch (lOException ioe) { 
    System.out.println("Could not connect back to host."), 
    return null; 
    // Пересылка команды "послать данные" и имени файла. // Регистрация всей 
отосланной информации. ByteArrayOutputStream bytes = new 
ByteArrayOutputStream() try { 
    out.writelnt(l); 
    out.writeUTF(strFilename); 
    int n; 
    while |_true) { 
    n = in . read () ; bytes.write(n); } 
    . } catch (EOFException eof) { } catch (lOException ioe) { 
    System.out.println("Problem reading data from remote server. ••••4   
finally   { try   { 
    in.close () ; ^4^     } catch (lOException ioe) ( 


    2O6	Visual J++: основы программирования 


    //Возвращение массива байтов, return bytes.toByteArray(); 

    Приложение сервера, которое надо явно запустить на машине WWW-сервера, 
ожидает поступления запроса на соединение. Когда осуществляется новый сеанс 
связи, программа сервера ждет команды "сохранить данные" или "послать данные" 
и строку идентификатора файла. Если строка получена, сервер пересылает данные 
в указанный файл или создает такой файл и сохраняет всю информацию, 
пост^1швш);ю_че?ез сокет. Далее приведена реализация программы-сервера: 

    import java.net.*; import java.io.*; 
    // Каждый экземпляр объекта SimpleFileServer создан для 
    // обработки одного сеанса связи с сервером. Статические методы 
    // и переменные управляют программой сервера и объектом ServerSocket и 
    // создают отдельные экземпляры объекта SimpleFileServer 
    // для обработки каждого сеанса связи. 
    public class SimpleFileServer { 
    // Порт связи. 
    private static final int m_nServerPort = 1001; 
    //Статические переменные класса содержат 
    // _оДъект ServerSogket. 
    private static ServerSocket m_socketServer; 
    // Метод main() создает сокет сервера, ожидает сеанса // связи и создает 
экземпляр SimpleFileServer для каждого сеанса. public static void main(String 
[] astrArgs) ( // Создание сокета сервера, try { 
    m_socketServer = new ServerSocket(m_nServerPort); } catch (lOException 
ioe) { 
    System.err.println("Problem creating server socket:" + ioe); 
System.exit(-1); } 
    // Создание обработчика объекта для каждого сеанса 
    // связи с сокетом сервера. 
    try*{ 
    Socket s; 
    while (null != (s = m_sqcketServer.accept()}) { new SimpleFileServer(s); s.close(}; 
    m_socketServer.close() ; } catch (lOException ioe) ( 
    System.err.println("Server socket problem: " + ioe); } 


    Глава б. Апплеты	2О7 


    //  Конструктор отдельного объекта SimpleFileServer. 
    //  В  его  коде  завершается  пересылка  всего файла. 
    private  SimpleFileServer(Socket  socket)   throws   lOException   { 
    //   Получение  потоков   ввода-вывода   сокета. 
    DatalnputStream in = new DatalnputStream(socket.getlnputStreamf)) ; 
    DataOutputStream out = new DataOutputStream(socket.getOutputStream()) ; 
    // Определение типа запроса и имени файла. 
    int  nType  =  in.readlnt(); 
    String strFilename =  in.readUTF(); 
    // Если предложено записать д(|)айл,  создается объект FileOutputStream // 
'и~пройсходит" запись данных.	.     .  .          .•.          .          .   
,  , if (_Q  =_=  nType)    { try   { 
    FileOutputStream  file__out   = 
    new FileOutputStream(strFilename); 
    for(int  n   ;   -1    !=   (n  =   in.readO)    ;   ) 
    file_out.write(n); }   catch   (EOFException  eof)    { } 
    //  Если нужно прочитать  файл,   создается объект  FilelnputStream. }   
else  if   (1  == jiType)    { try   {    * 
    FilelnputStream file_in =new FilelnputStream(strFilename); 
    for(int   n   ;   -1   !=   (n  =   file_in.read())    ;    ) 
    out.write(n); 
    } catch (EOFException eof) { } } 
    // Завершение сеанса связи, in.close (); out.close(); } } 

    У этой программы-сервера имеются некоторые недостатки. В частности, она не 
гарантирует безопасность данных. Любой пользователь может связаться с сервером 
и загрузить или выгрузить из него любые файлы, переписать файл, который кто-то 
загрузил ранее. Но, вероятно, самая существенная проблема заключается в том, 
что вы не можете корректно завершить работу программы-сервера! Если отвлечься 
от этих недостатков, на примере этого сервера показано, как осуществляется 
запись данных из апплета. Такие серцеры необтд^мы^для^д^тельното хранения 
данных, полученных от апплет	~~"~"" ""—— 

    2О8	Visual J++: основы программирования 


    Ограничения, налагаемые на сеанс связи, ПРОВОДИМЫЙ через сокет 

    Таким же нежелательным, как доступ загруженного исполняемого кода к 
локальным файлам системы, является осуществление им же связи через сеть. 
    Представьте следующую ситуацию. Вы загрузили на свой компьютер апплет и 
просматриваете его. Вдруг вы замечаете, что кто-то получил доступ к 
финансовому серверу вашей фирмы, который находится в этой же комнате. Вы 
подходите к серверу, чтобы узнать, кто с ним связался, и обнаруживаете, что 
это ваш собственный компьютер. И хотя вы думаете, что "всего лишь" 
просматриваете WWW-страницу, данные вашей компании в это время могут быть 
вытащены и отправлены неизвестно куда. 
    Это пример того, что может случиться, если у апплетов^нет ограничений на 
установление произвольных соединений через сокет. Существует несколько хорошо 
известных способов (и, возможно, еще большее количество малоизвестных) 
проведения атак на компьютеры через Internet. В предыдущем примере показано, 
что случается, когда апплет, загруженный как часть WWW-страницы, атакует 
другие машины офиса из броузера, в котором запущен. К счастью, налагаемые Java 
ограничения, обеспечивающие безопасность, блокируют такие атаки апплетов. 
    Единственная машина, с которой апплет может связаться,— это WWW-сервер, с 
которого он загружен. Такое ограничение предотвращает описанный выше вид 
нападения, но позволяет апплету получать и отправлять необходимые данные через Internet. 
    На примере класса MyStoringApplet показано, как связать апплет.с его 
WWW-сервером. Строка необходимого для этого кода, выглядит следующим образом: 

    Socket s = new Socket(InetAddress.getByName(getDocumentBase()),nPort); 

    В то же время апплет, находящийся на машине клиента, не может создать 
серверный сокет. Разрешено устанавливать только простые сетевые соединения" 
между машиной клиента и WWW-сервером, обслуживающим апплет. 

    Ограничения по СОМ-объектам 

    Создание экземпляров СОМ-объектов и даже связь с ними в Internet Explorer 
запрещены. Это сделано из соображений безоТТасности, поскольку сами 
СОМ-объекты не защищены и могут выполнять в системе пользователя какие угодно 4$ункции. 
    В частности, апплет мог бы создать объект приложения MS Word, являющийся 
легальным СОМ-объектом. Используя функции Word Basic, апплет мог%1 приказать 
приложению Word найти финансовые документы на локальной машине, скопировать их 
и послать по электронной почте неизвестному адресату. Для обеспечения 
безопасности Java-апплета вообще должны быть запрещены связи с СОМ-объектами. 

    Глава 6. Апплеты	2О9 

    Решение этой проблемы состоит в использовании так называемой цифровой 
подписи (digital signature). Применяя технику шифрования открытым ключом, 
можно "пометить" файл с расширением CAB собственной цифровой подписью. Если 
пользователь признает цифровую подпись автора апплета достоверной, то апплет 
сможет обойти все ограничения, обеспечивающие безопасность. Он сможет создать 
экземпляр СОМ-объекта, получить доступ к локальным файлам и связаться по 
Internet с другими компьютерами, а не только с тем WWW-сервером, с которого 
сам получен. 
    В электронной документации по Visual J++ в разделе "Java and COM/Using 
Java with COM/Using Java and COM Together/Creating a Signed CAB file" подробно 
описано, как можно создать и ввести цифровую подпись в САВ-файл. Но прежде 
необходимо инсталлировать инструментарий CAB&SIGN. 
