Показаны сообщения с ярлыком клиент. Показать все сообщения
Показаны сообщения с ярлыком клиент. Показать все сообщения

16 октября 2009 г.

Клиент для Google Wave в виде Java Web Application


Автор: zokotuhaFly (aka shaman.sir)
Источник: http://habrahabr.ru/blogs/google/72235/ (перепечатывается здесь с любезного разрешения автора)

Клиент для Google Wave в виде Java Web Application + небольшая история

Здравствуйте.

[Здесь должен быть абзац с небольшой историей, который я поместил в постскриптум, дабы не создавать ненужных ощущений до прочтения]

sametimed

Итак, я написал небольшой проект на Java, который представляет собой клиент к Google Wave, который, в свою очередь, вы можете расширить необходимыми вам возможностями. Визуально выглядит он не очень презентабельно (впрочем, а-ля Windows 3.11, почему бы и нет), поскольку веб-дизайнер из меня не очень хороший, однако абсолютно за весь исходный код я отвечаю головой :). Так, например, требующиеся для отображения CSS-стили при этом выглядят максимально понятно, а XHTML-структура максимально упрощена (не в ущерб стандартам) для того, чтобы настоящий дизайнер мог развернуться и сделать действительно красиво :).

sametimed в работе

Сайт, исходные коды, и war-пакет с текущим состоянием проекта находятся по адресу http://code.google.com/p/sametimed

Сейчас у меня нет под рукой общедоступного хостинга, чтобы я мог прямо вот так запустить проект и дать поиграться с ним не отходя от хабра, однако, я могу предложить вам видео (на vimeo), демонстирующее его основные возможности, что, думаю, тоже вполне ничего, а запустить проект вы сможете сами, благо подробные инструкции есть на сайте проекта, а развёрнутые пояснения и подробное описание работы (и руководство к расширению возможностей) — внутри…



Установка


Для того, чтобы запустить этот клиент, нужно, по идее, четыре вещи:

Чуть более подробные инструкции как его запустить сам по себе или в виде рабочего проекта для Eclipse представлены на странице проекта. Здесь же я рассмотрю структуру кода и опишу как он действует (пока без что UML-диаграмм, извините, по мере возможности я их добавлю).

Общая концепция


Здесь под сочетанием «серверная сторона/часть» подразумевается не сервер wave-protocol, а серверная строна веб-приложения.

Поскольку в wave-protocol сейчас существует только консольный клиент, релизованный в виде jar-приложения, основной вопрос состоит в своевременной передаче информации с клиента на серверную часть и наоборот.

Посему стало необходимо ввести два понятия:
  • Команда: отправляется с клиента на сервер, требует «открыть волну», «добавить участника», «отменить действие», «сказать привет» и т.п., отправляется по действию пользователя.
  • Сообщение: отправляется с сервера на клиент, сообщает о том, что «кто-то пригласил в волну», «sonya ответила привет», «добавился участник», «произошла ошибка», отправляется при любом обновлении.

При этом, команду с клиента можно отправить сразу, сильно не задумываясь о нагрузке на сервер, а вот сообщения об обновлениях требуется отсылать только по факту происшествия. Однако, на клиенте находится javascript, который без сложных приспоcоблений не может осуществлять такие вещи. Здесь потребовался Reverse Ajax, вернее его реализация для Java — библиотека DWR (Direct Web Remoting), она позволяет вызывать функцию на клинете с сервера в тот момент, когда решит сервер, а не клиент. Их сайт рассказывает о всех возможностях, которые вовсе не ограничены данным применением, но для проекта, как я уже сказал, из всех возможностей я использовал только эту.

Команды и сообщения передаются в виде XML, а содержимое сообщений об обновлениях в виде JSON, засчёт чего в построении интерфейса участвует только Javascript (я использовал JQuery), а серверная сторона даже не задумывается о существовании UI.

(для серверной стороны в проекте есть интерфейс Renderer, реализация которого вызывается при необходимых обновлениях, но он предназначен как раз для случаев вроде консольного клиента)

Описание процесса, происходящего в текущей версии


При запуске клиента, вы видите страницу с полем для ввода юзернейма и единственной кнопкой. Wave-protocol сервер при этом должен быть запущен. При нажатии на кнопку введённый вами юзернейм передаётся в сервлет (GetClientViewServlet), который подключает вас «к волне», и возвращает всю структуру (модель) клиента в виде JSON. JQuery на клиентской стороне строит весь интерфейс вейвлета. Если вы нажмёте кнопку ещё раз, вы запросите ещё один вейвлет, при этом между собой они будут различаться по внутреннему ID (сгенерированному автоматически), указанному на синей полоске в скобках. По этому ID и сервер и клиент определяют, к какому клиенту относится команда и/или сообщение.

Одновременно с этим DWR начинает ожидать вестей об обновлениях, а вы в это время можете ввести любую из доступных команд в поле консоли и нажать «send». Например, создать волну командой «\new», открыть её командой «\open » и что-нибудь сказать, набрав просто текст (как в скайпе). При нажатии на кнопку «send» отправляется POST-запрос к другому сервлету (CommandsReceiverServlet), который получает сгенерированную в виде XML команду и тут же её выполняет, передавая данные серверу wave-protocol.

Обновления в данный момент приходят с серверной стороны (и от сервера wave-protocol) в виде XML-сообщения (на клиентской стороне есть обработчик, который вызывается при получении новых сообщений), в котором содержится алиас изменившейся модели (например, «chat», «inbox», «userslist», «errors» или «editor») и её содержимое в виде JSON, которое тут же обрабатывается и обновляет соответствующую часть вейвлета.

Как видите, всё до невозможности просто.

Структура проекта и возможности для расширения



Java:


  • name.shamansir.sametimed.wave Все классы, лежащие «снаружи» и относящиеся к клиенту непосредственно; здесь находятся абстрактные классы AUpdatingWavelet и ADocumentsWavelet — определяющие структуру соответствующего типа вейвлетов (обновляющийся вейвлет и его расширение, вейвлет содержащий документы). Класс SimpleWavelet служит примером такой реализации. Класс WavesClient занимается обработкой команд и именно он возвращает модель содержащегося в нём вейвлета вGetClientViewServlet.
  • name.shamansir.sametimed.wave.messaging Все, что относится к командам и сообщениям. Идентификаторы команд/сообщений вCommandID/MessageTypeID, сами классы Command и UpdateMessage, сервлет-обработчик CommandsReceiverServlet.
  • name.shamansir.sametimed.wave.model Классы, описывающие модель вейвлета. Они содержат описание каждой суб-модели, вроде списка участников, чата или текстового документа. И ModelID, описывающий возможные модели с абстрактным классом AModel, конечно. Плюс,ModelFactory, фабрика моделей.
  • name.shamansir.sametimed.wave.model.base Значения моделей, вроде «набора строк чата», «набора блоков текста в документе», «списка доступных волн» и т.д. Именно в этих классах описывается преобразование в JSON.
  • name.shamansir.sametimed.wave.model.base.atom То, из чего состоят значения, если это необходимо для их структуры — «строка чата», «блок текста», «идентификатор волны»
  • name.shamansir.sametimed.wave.model.base.chat Вейвлет с функцией чата и клиент для него
  • name.shamansir.sametimed.wave.model.base.editor Вейвлет с поддержкой редактируемого документа, на данный момент не имплементирован, поэтому отключен
  • name.shamansir.sametimed.wave.render Классы, относящиеся к рендерингу Здесь находится тот самый класс JSUpdatesListener, который средствами DWR вызывает функцию обновления на клиенте.

Самый логичный способ расширения — имплементация класса ADocumentsWavelet или расширение класса WaveletWithChat. Поскольку, в наиболее вероятном случае, вы будете оперировать понятием «документ» (А чат или что-либо другое, как раз являются документами), то именно такой способ должен подойти вам лучше всего. Также вам понадобится задуматься о модели данного документа (создав соответсвующий тип, реализующий AModel, добавить id модели в тип ModelID и генерацию модели в ModelFactory).

Если документ не будет обрабатывать какие-либо новые команды, то этого достаточно — можно заменить в GetClientViewServlet возвращаемый вейвлет на ваш собственный и вуаля!.. Ах, да, ещё генерация UI на клиенте, но об этом ниже.

Если же необходимы свои собственные, предназначенные сугубо для него команды, то необходимо добавить соответствующие команды в тип CommandID. После этого нужно расширить класс WavesClient для поддержки вашего вейвлета и для того, чтобы он обрабатывал и передавал новые команды вейвлету независимо от предка. И, в этом случае, заменить в GetClientViewServlet реализацию WavesClient на вашу собственную. И снова вуаля! (снова, не учитывая UI)

Вам понадобится, конечно же, учесть пару тонкостей при написании обработки команд, но в общих чертах это весь необходимый процесс для расширения функциональности клиента.

Javascript


  • ui.js занимается генерацией интерфейса, для каждого блока модели есть собственный метод
  • command.js отправляет и генерирует команды, принимает сообщения об обновлениях, а также содержит обработчики для кнопок
  • ajax.js скрипт, который давно пора заменить на соответствующий метод jquery, но никак не доходят руки :). используется в command.js

Для того, чтобы добавить генерацию вашей модели, достаточно добавить строку, вызывающую ваш метод обработки в методы CreateClient и renderUpdate объектаClientRenderer в ui.js и собственно написать этот метод. Всё остальное будет (должно) работать самостоятельно.

CSS


  • sametimed-plain.css ещё чуть более ужасный интерфейс чем цветной :)
  • sametimed-colored.css цветной, Windows 3.11-подобный интерфейс :).

Пока что стили, определяющие позиционирование и внешний вид (цветность) не разделены, хотя они разделяются комментариями в самих файлах. Думаю, я не буду этого менять.

Эпилог


Вышло много текста про один небольшой аппликейшн, но зато пост описывает все его возможности. Надеюсь найдётся не один человек, которому эта разработка будет интересна и если найдётся, планирую усовершенствовать его дальше. Пока это небольшая поделка «из интереса», но немного стараний — и её можно превратить в полноценный проект.

Тех, кто это будет тестировать, прошу постить баги в соответствующее место, в разумных пределах и не относящиеся к дизайну :).

Участие в разработке приветствуется но поощрить нечем, только лицензией :).

Важное пояснение


Если вы будете проверять приложение в работе одновременно с консольным клиентом wave-protocol, сообщения отосланные из консольного клиента будут приходить на одно позже. Это не баг и не фича, это способ генерации «документа» чата. В консольном случае Google, по крайней мере как посчитал я, изменили порядок записи элементов документа так, чтобы их было удобнее отображать в консоли (начало элемента, конец элемента, затем его содержимое). В моём случае элемент строится в привычном порядке (начало, содержимое, конец), этим и обусловлено это несоответствие. Если исправить порядок генерации в моём проекте на тот же, что и в консоли, они будут будут полностью друг другу соответствовать.

И да, специальной проверки/валидации введённых на клиенте данных пока не производится.

P.S. Собственно, абзац: Где-то с месяц назад в поисках работы я получил тестовое задание от некоторой фирмы X, аж на три недели. При том, что большую часть задания я выполнил и показал текущий на то время вариант в работе и прошёл 80% тестов на собеседовании, меня не приняли. Посему я считаю что имею право поделиться и исходным кодом и руководством к использованию с хабраобществом. Код я развил немного в другом направлении, но тем не менее он может оказаться полезным, даже если у гугла неожиданно окажется есть всё необходимое и в рабочем (доступном разработчикам) варианте.

да-да, слово editor используется часто применительно к проекту, потому что изначально он планировался (и планируется) как одновременный редактор

11 августа 2009 г.

Меняем меню: расширения волнового клиента


Как известно, в Google Wave можно использовать два типа расширений (extensions) - гаджеты и роботы. Напомню:

Гаджет - небольшая программка, которая выполняется на стороне клиента, принадлежит определенной волне и позволяет ее участникам взаимодействовать между собой, поскольку они все могут менять статус гаджета и одновременно видят эти изменения. Наглядный пример - гаджет голосования. Вы проголосовали "за" или "против", гаджет учел ваш голос и изменил таблицу результатов. Все участники волны, в которую внедрен данный гаджет, увидели это изменение.
Робот - исполняется на стороне сервера. Можно даже сказать "на стороне серверов", поскольку сейчас роботы размещаются "в облаках", на серверах Google AppEngine. Робот - равноправный участник волны. Так же, как люди, он имеет доступ к содержанию волны, может его изменять, добавлять или удалять участников данной волны, а также создавать новые "блипы" (реплики в обсуждении) и новые волны.

Возникает вопрос, а как добавлять роботов или гаджеты в волну? Поскольку роботы - это полноценные участники волны, то их можно добавлять точно так же, как и людей. Заносите адрес робота в свою адресную книгу, а потом оттуда по мере необходимости подключаете робота к нужной волне. Гаджет вставляется с помощью специального пункта в меню "Debug" (мы сейчас говорим о волновом клиенте, который используется в "песочнице" Google Wave Sandbox - http://wavesandbox.com/).

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

Задача 1. Вы написали (или откопали где-то код) удобный гаджет. Часто и с удовольствием вставляете его в волну. Но вам не хочется постоянно лезть в меню "Debug" и добавлять строку с адресом гаджета. Хорошо бы иметь просто кнопку, которая будет вставлять нужный гаджет по мановению вашего пальца.

Создаете "манифест", т.е. XML-файл с таким примерно текстом:
<extension location="Toolbar">
<info text="Are You In?"
description="Click to insert the Are You In? gadget."
imageUrl="http://wave-api.appspot.com/public/gadgets/areyouin/icon.png"/>
<insertGadget url="http://wave-api.appspot.com/public/gadgets/areyouin/gadget.xml"/>
</extension>

Затем создаете новую волну, идете в меню "Debug >Add Extension Installer" и там указываете адрес, где лежит ваш "манифест".

Сохраняете волну (кнопка "Done"). В ней появляется изображение элемента из "пазлов" с названием вашего гаджета, надписью "Автор неизвестен" и кнопкой "Install". Ее и нажимаем.


Все, теперь в вашем меню редактирования "блипа" появляется новая кнопка, которая вставляет ваш гаджет куда пожелаете. (Гусары, молчать! :) На моем скриншоте вы видите добавленную кнопку с сине-белым логотипом facebook'а.


Примечание: Структура "манифеста", как видим, очень проста. Location указывает место, куда будет добавлена кнопка (сейчас допустимо два варианта - Toolbar и NewWaveMenu, о котором чуть ниже). Info text - пояснение. Description - более подробное описание гаджета. ImageUrl - расположение картинки для кнопки. InsertGadget url - соответственно, местоположение xml-файла вашего гаджета. Можете попробовать с демо-гаджетом, который лежит здесь - http://wave-api-dmo.appspot.com/public/simplemap/whereareyou.xml


Задача 2. Вы продались роботам, частенько выкрикиваете во сне "Слава роботам! Смерть человекам!" и в любую волну добавляете с десяток любимых ботов. Правильно, Твитти нужна? Нужна, кто ж сообщит последние сплетни из твиттера. Рози нужна? Нужна, кто ж будет переводить их с разных языков. "Иветта, Мюзетта, Жанетта..."

Хочется иметь возможность одним нажатием добавлять нужный гарем роботов (ну, и людей заодно, конечно) к волне.


Создаем другой манифест:
<extension location="NewWaveMenu">
<info text="New Twave"
description="Creates a new Twitter wave."/>
<createNewWave>
<participant id="tweety-wave@appspot.com"></participant>
</createNewWave>
</extension>

Точно так же открываем новую волну, затем идем в меню "Debug >Add Extension Installer" и там указываем адрес, где в сети лежит этот "манифест".
Оп! И в панели рядом с кнопкой "New Wave" появляется кнопка со стрелкой вниз, которая при нажатии на нее показывает выпадающее меню с добавленными вами пунктами.

Примечание: Как видим, тут изменилась Location, а также появилась возможность создавать волну (createNewWave) и добавлять в нее участников (participant).

Пока, насколько мне известно, еще нет способа таким же образом добавлять участников в существующую волну или удалять их оттуда. Но API будет со временем расширяться и улучшаться. И если эти ожидания сбудутся, то у нас с вами появится возможность подстраивать свою рабочую среду в волнах сообразно нашим персональным вкусам и предпочтениям.
Или создавать "клоны" волнового клиента, заточенные под определенный круг задач (IM, совместное редактирование текстов, дискуссионное или игровое сообщество и т.п.).
Или под умения и привычки пользователей (хм, "1С-волна" для бухгалтерии, это наверное было бы круто, да! :).