|
|
|||||||||||||||||||||||||||||
|
Построение "толстого" Java -клиента, который полностью интегрирован с Google TalkИсточник: oracle Лукас Джеллема
Как стало совершенно ясно на выставке Oracle OpenWorld 2007, концепции и тенденции интеграции Web 2.0 с приложениями уровня предприятия являются сегодня главным направлением в разработке приложений. Приложения Oracle Fusion Applications предлагают хороший пример того, как объединение очень привлекательных, визуально богатых пользовательских интерфейсов с популярными возможностями Интернета, например, социальные закладки ( social bookmarking ) и мгновенный обмен сообщениями ( IM - instant messaging), приводит к эффективным и продуктивным приложениям. Объединенное в пользовательском интерфейсе приложения взаимодействие является важным фактором при совместной работе, которую продвигает этот новый стиль приложений. Электронная почта ( e - mail ), голосовая связь по IP ( VoIP ) и служба коротких сообщений ( SMS ) - все это входит в число потенциально рассматриваемых каналов коммуникации, но IM (чат) вполне может быть самым мощным среди них. Рассмотриваемая в статье обучающая программа предначена для демонстрации разработки полнофункционального Web -приложения, которое полностью интегрировано с Google Talk (http://www.google.com/talk/) , одним из главных сервисов IM . Проект с открытым исходным текстом Smack предлагает библиотеку API Java , чтобы подключиться к протоколу Extensible Messaging and Presence Protocol ( XMPP ) - коммуникационным протоколам IM . Используя Smack , можно очень быстро создать Java -приложение, которое разговаривает с Google Talk (и слушает его), или с любым другим сервисом IM , который соответствует техническим требованиям XMPP . Затем можно использовать Oracle ADF Faces Rich Client (который на момент написания этой статьи являлся частью Oracle JDeveloper 11 g Technical Preview ), чтобы создать пользовательский интерфейс и, в частности, Active Data Service (Обслуживание активных данных), который делает возможным мгновенные обновления Web -клиента ("проталкивание" с сервера на браузер) при получении сообщения. И, наконец, возможность перетаскивания "толстого" клиента Oracle ADF Faces используется для быстрого сбрасывания записей данных в сообщения чата. Полностью файлы проекта и программы-примеры для каждой части обучающей программы доступны по адресу http://www.oracle.com/technology/pub/files/jellema-googtalk-project.zip . Подготовка
Часть 1: Создание Java -приложения, которое разговаривает с Google Talk Настройка Smack и подключение к Google Talk для программируемых чатов (чтение и отправление сообщений) Запустите Oracle JDeveloper 11 g . Создайте новое приложение - и назовите его, например, GoogleTalk . Также создайте новый проект; например, GoogleTalkJavaClient . Выберите из меню Application Template No Template [ All Technologies ] . Из меню, вызываемого нажатием правой кнопки мыши, или из меню Tools откройте окно диалога Project Properties для узла Project . Перейдите к узлу Libraries and Classpath . Добавьте к проекту архивные файлы smack . jar и smackx . jar : кликните по кнопке Add JAR / Directory . Просмотрите библиотеки и файлы Smack , выберите оба файла (и smack . jar , и smackx . jar ) и кликните по кнопке Select . Файл JAR будет добавлен к проекту. Кликните по кнопке OK . Теперь мы создадим класс, который будет в состоянии через Google Talk посылать сообщения IM . Создайте в пакете otn . adf . googletalk класс MessageSender . Дайте Oracle JDeveloper указание добавить основной ( main ) метод. package otn.adf.googletalk; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; public class MessageSender { private static String username = "YOUR_GOOGLE_TALK_USERNAME"; private static String password = "YOUR_GOOGLE_TALK_PASSWORD"; ConnectionConfiguration connConfig; XMPPConnection connection;
public MessageSender() throws XMPPException { connConfig = new ConnectionConfiguration("talk.google.com", 5222, "gmail.com"); connection = new XMPPConnection(connConfig); connection.connect(); connection.login(username, password); }
public void sendMessage(String to, String message ) { Message msg = new Message(to, Message.Type.chat); msg.setBody(message); connection.sendPacket(msg); }
public void disconnect() { connection.disconnect(); }
public static void main(String[] args) throws XMPPException { MessageSender messageSender = new MessageSender(); messageSender.sendMessage("youraccount@gmail.com", "Hello You. This is my first message sent programmatically using Smack API and Google Talk."); messageSender.disconnect(); } } Удостоверьтесь, что в строках 10 и 11 вы вставили имя и пароль для своего собственного аккаунта Google Talk . Также вставьте свой аккаунт (или аккаунт вашего друга) в строку 36, как пункт назначения (адресат) этого сообщения: " Hello , world of instant messaging ". Конструктор класса MessageSender создает подключение к серверу Google Talk и регистрируется на нем (строки 17-21). После создания экземпляра MessageSender вызывается метод sendMessage с адресатом (кому посылается сообщение) и с самим текстом сообщения. И, наконец, подключение закрывается с помощью метода разъединения. Когда вы выполните приложение, то быстро получите свое первое сообщение Google Talk , посланное вашим собственным приложением через сервер Google Talk : Присутствие Одной из хороших возможностей мгновенного обмена сообщениями является присутствие ( presence ), то есть индикация того, кто из друзей и контактов доступны в данный момент. И Google , и API Smack поддерживают присутствие. Сначала мы можем сказать всему миру о нашем собственном присутствии, выполняя приведенные ниже строки программы: // сказать миру (или хотя бы нашим друзьям), что мы доступны Presence presence = new Presence(Presence.Type.available); presence.setMode(Presence.Mode.chat); connection.sendPacket(presence); Примечание : мы можем выбирать из нескольких режимов присутствия: available (доступен), chat (в чате), away (отошел), xa ( extended away - расширенное отсутствие) и dnd ( do not disturb - просьба не беспокоить). Еще более интересен для нас тот факт, что наша программа может узнать о присутствии всех наших контактов - в терминах IM , поименный список ( roster ) - или о присутствии определенного контакта. Вот так определяется список всех наших контактов: Roster roster = connection.getRoster(); Collection iter = roster.getEntries(); for (RosterEntry entry : iter) { System.out.println(entry.getName() + " (" + entry.getUser() + ")"); } Получение сообщений Если бы мы могли бы только посылать сообщения, то наш разговор (то есть, сеанс связи) был бы весьма унылым. Следующий шаг, который мы собираемся предпринять, добавляет нашему "приложению" возможности прослушивания. Мы создадим класс MessageListener , который будет выглядеть в высшей степени похожим на класс MessageSender. Фактически, для его создания достаточно сначала сохранить файл MessageSender . Java как MessageListener . Java , а затем использовать поиск и замену, чтобы заменить все вхождения слова MessageSender на MessageListener . Затем сделаем так, чтобы класс реализовывал интерфейс PacketListener , добавляя к спецификации класса implements PacketListener . Единственное требование, предъявляемое нам этим интерфейсом, это реализация метода processPacket , как показано в этом примере: public void processPacket(Packet packet) { Message message = (Message)packet; System.out.println("Message (from: "+message.getFrom()+"): "+message.getBody()); } После того, как мы зарегистрировали наш класс как прослушиватель для сообщений с подключением, этот метод будут вызывать всякий раз, когда сервер Google Talk посылает сообщение нашему подключению. Регистрация нашего класса как "прослушивателя" для сообщений из чата на наш акаунт делается путем добавления этих строк программы в конце конструктора для MessageListener (): // прослушивать поступающие сообщения для этого подключения PacketFilter filter = new MessageTypeFilter(Message.Type.chat); connection.addPacketListener((PacketListener)this, filter); Примечание: Мы должны добавить к нашему классу следующие операторы импорта; однако, если мы добавим код, описанный выше, то Oracle JDeveloper , вообще говоря, сделает это за нас. import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.packet.Packet; Теперь мы готовы получать сообщения. Если мы изменим основной метод нашего класса MessageReceiver следующим образом, то получим наше собственное (посланное нами же) сообщение: public static void main(String[] args) throws XMPPException, InterruptedException { MessageListener messageListener = new MessageListener(); messageListener.sendMessage("youraccount@gmail.com", "Hello You. This is my second message sent programmatically using Smack API and Google Talk."); // listen for 3 seconds Thread.sleep(3000); messageListener.disconnect(); } Конечно, вместо того, чтобы говорить с самим собой, было бы намного лучше слушать сообщения из чата, посланные нашими друзьями. Если ни один из них в настоящее время не доступен, Google Talk предлагает вам ряд "друзей", с которыми можно разговаривать. Эти так называемые боты являются программируемыми аккаунтами Google Talk , которые предлагают перевод с одного языка на другой. Боты имеют аккаунты типа en2zh@bot.talk.google.com и nl2en@bot.talk.google.com, составленные из двухбуквенных сокращений языка-источника и выходного языка. (Уведомление о ботах перевода Google Talk можно найти, например, на http://googletalk.blogspot.com/2007/12/merry-christmas-god-jul-and.html ) Мы можем просто послать сообщение из чата боту Google , и он пошлет нам возвращаемое сообщение, что попросту говоря, просто великолепно для быстрого нахождения перевода простых слов или фраз. Мы можем изменить основной метод еще раз и сделать так, чтобы он считал на французском языке от 1 до 10 (хотя, конечно, для того чтобы сделать это, вы не нуждаетесь в боте перевода): MessageListener messageListener = new MessageListener(); String[] englishCounting = new String [] {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}; for (int i=0;i< messageListener.disconnect(); } Thread.sleep(500); order right the in replies receiving of chances increase to pause slight add englishCounting[i]); messageListener.sendMessage(?en2fr@bot.talk.google.com?, { i++)> Результат выполнения приложения на этот раз - счет на французском языке: Примечание: на своем сервере боты Google Talk выполняются многопоточно (то есть, несколькими потоками). Это означает, что если задержка между посланными боту сообщениями достаточно мала, ответы могут быть возвращены в другом порядке, поскольку, например, поток, обрабатывающий слово "пять", может фактически ответить быстрее, чем поток, имеющий дело со словом "два". Если же перед посылкой следующего сообщения мы будем ожидать 500 мс (или около того), то у нас будут неплохие шансы получить все ответы в надлежащем порядке. Часть 2: построение Oracle ADF Faces Rich Client Теперь, когда у нас имеется наше базовое приложение Java , которое может и посылать сообщения в чат, и принимать их, пришло время узнавать, как можно создать Web -приложение, которое сможет делать те же самые вещи. Мы будем использовать Oracle ADF Faces Rich для создания пользовательского интерфейса поверх уровня связывания данных Oracle ADF , чтобы подключить Web -уровень к сервисам данных, которые обеспечивают доступ к контенту и операциям. Мы добавим к MessageSender небольшую дополнительную функциональную возможность: свойство под названием roster (именной список) - список наших "приятелей по чату" - друзей, которых мы добавили в Google Talk . Сначала создадим новый класс по имени TalkMate , bean -компонент с двумя свойствами: toName и displayName . package otn.adf.googletalk; public class TalkMate { String toName; String displayName; public TalkMate() { } public void setToName(String toName) { this.toName = toName; } public String getToName() { return toName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getDisplayName() { return displayName; } } Добавим к классу MessageSender следующее приватное свойство: private Collection<TalkMate> roster = new ArrayList(); и создадиме для этого свойства методы средств доступа (получатель ( getter ) и посылающий ( setter )). Затем добавим к методу конструктора MessageSender () следующие строки программы: Roster friendsRoster = connection.getRoster(); Collection rosterIter = friendsRoster.getEntries(); for (RosterEntry entry : rosterIter) { TalkMate friend = new TalkMate(); friend.setDisplayName(entry.getName()!=null?entry.getName():entry.getUser()); friend.setToName( entry.getUser()); this.roster.add(friend); } rosterIter // // также, в проверочных целях, добавьте к чату с самим собой // ваш собственный аккаунт TalkMate veryBestFriend = new TalkMate(); veryBestFriend.setDisplayName("Your Own Display Name"); veryBestFriend.setToName( "YOUR_OWN_GOOGLE_TALK_ACCOUNTNAME"); this.roster.add(veryBestFriend);
Теперь мы создадим bean -компонент MessageReceiver , который показывает возможности прослушивания чата способом, дружественным к потенциальным потребителям. Сначала создадим следующий bean -компонент: package otn.adf.googletalk; import Java.util.Date; public class ChatMessage { private Date timestamp; private String from; private String body;
public ChatMessage() { } public ChatMessage(String from, String body) { this.timestamp = new Date(); this.from = from; this.body = body; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public Date getTimestamp() { return timestamp; } public void setFrom(String from) { this.from = from; } public String getFrom() { return from; } public void setBody(String body) { this.body = body; } public String getBody() { return body; } } Затем - класс MessageReceiver, что-то вроде этого : package otn.adf.googletalk; import Java.util.ArrayList; import Java.util.List; public class MessageReceiver { private List messages = new ArrayList(); public MessageReceiver() { try { MessageListener messageListener = new MessageListener(); messageListener.setReceiver(this); } catch (Exception e) { } } public void processMessage(String from, String body) { ChatMessage message = new ChatMessage(from, body); messages.add(0, message); for (ChatMessageListener listener:messageListeners) { listener.processChatMessage(message); } } public void setMessages(List messages) { this.messages = messages; } public List getMessages() { return messages; } } Чтобы заставить все это работать вместе, мы должны внести в класс MessageListener небольшое изменение. Добавим получателя приватного ( private ) свойства к классу MessageListener , и создадим для него получателя и посылающего: MessageReceiver receiver; Затем добавьте в метод processPacket() следующую строку: if (this.receiver != null) { receiver.processMessage(message.getFrom(), message.getBody()); } Это заставит MessageListener вызывать processMessage из MessageReceiver, что , в свою очередь , добавит новый ChatMessage к коллекции сообщений в bean- компоненте MessageReceiver. Чтобы показать MessageSender и bean- компоненты MessageReceiver в палитре управляющих элементов данных Oracle ADF (Oracle ADF Data Control Palette), мы должны опубликовать оба bean- компонента как элементы управления данными . Давайте сначала сосредоточимся на MessageSender : Создайте и опубликуем управление данными из меню, вызываемого щелчком по правой кнопке мыши, из узла bean -компонента: После того как Oracle JDeveloper закончит создание элемента управления данными, мы найдем MessageSender в палитре элементов управления данными, доступной для использования любым клиентом Oracle ADF: Также опубликуйте элемент управления данными для MessageReceiver тем же способом, что и MessageSender - из меню, вызываемого правой кнопкой мыши. Теперь в приложении Google Talk создайте новый проект; назовите его GoogleTalkWebClient . Этот проект будет содержать наше Web -приложение, страницу JavaServer Faces ( JSF ), и связанные с ними конфигурационные файлы. После создания Web-проекта выберите в навигаторе узел GoogleTalkWebClient , перейдите к New Gallery и выберите создание страницы JSF . Назовите страницу, например, ChatClient . jspx : Кликните по OK , и страница будет создана. На этой странице мы хотели бы иметь два раздела: один для того, чтобы писать и посылать сообщения в чат, и второй, в котором перечисляются поступающие сообщения. В этой статье мы не будем уделять слишком много внимания проведению сеансов связи (разговоров) в чате с многочисленными реципиентами (получателями) - мы оставим это, как упражнение для читателя (Вас, скорее всего, просто бесит, когда автор пытается выйти сухим из воды, отделываясь выражениями типа этого). С созданием двух разделов на странице очень удачно справляется компонент Oracle ADF 11 g Faces Rich Panel Splitter . Перетащите этот компонент из палитры компонентов и опустите его на страницу. Это даст нам панель с двумя разделами для посылки и получения сообщений. Установите ширину разделителя панелей на 100%. Перетащите sendMessage () в первый фасет. Опустите его как ADF Parameter Form . Задайте в следующем диалоговом окне осмысленные подсказки: Отображается следующее диалоговое окно и позволяет нам установить значения по умолчанию входных параметров операции sendMessage (). Кликните по OK , чтобы принять параметры настройки. Oracle JDeveloper создаст на странице два элемента inputText и кнопку управления, а также файл PageDefinition с итератором variables , содержащим две переменные, два элемента attributeValues и элемент operationBinding для операции sendMessage (). Измените размеры элемента messageBody - установите rows = 15 и columns = 60. Удалите message_to элемента inputText. Мы заменим его списком всех наших контактов, из которого может быть выбран требующийся контакт. Перетащите атрибут displayName из коллекции списка в MessageSender Data Control на нашу страницу и опустите его непосредственно перед Message inputText , как ADF Select One Choice . Появится диалоговое окно Edit List Binding . Как источник базовых данных (Base Data Source) выберите variables iterator . Как источник списка данных (List Data Source) выберите MessageSender.root.roster . Отобразите Data Value sendMessage_to на List Attribute toName и выберите displayName как Display Attribute ( атрибут элемента изображения ). Затем кликните по OK . Когда вы теперь будете выполнять страницу ChatClient , можно послать сообщения в чат вашим друзьям по Google Talk . Наш следующий шаг должен расширить ChatClient , чтобы также и получать посланные нам из чата сообщения. Перетащите коллекцию Messages на элемент управления данными MessageReceiver Data Control второго фасета Panel Splitter на странице ChatClient . Опустите эту коллекцию как ADF Read - Only Table . В появляющемся окне диалога Edit Table Columns сохраните только столбцы from и body . Также перетащите на страницу операцию Execute в Messages collection и опустите ее как командную кнопку под таблицей Messages , которую мы создали выше. Измените текст на кнопке с Execute на Refresh . Когда теперь страница будет выполняться, вы сможете использовать левую сторону страницы для посылки сообщений в чат, в то время как в правой стороне страницы можно получать ответы от всех контактов по IM , кликая по кнопке Refresh . Архитектура этого небольшого приложения может быть изображена так, как на этом рисунке: Бизнес-сервис в нашем приложении составлен из классов MessageSender и MessageReceiver , которые - благодаря помощи библиотеки Smack - взаимодействуют с сервером Google Talk . Оба этих класса были опубликованы, как ADF Data Controls . Страница JSF связала эти элементы управления данными: связывание операций для операции SendMessage в MessageSender Data Control и связывание таблицы для коллекции Message элемента MessageReceiver Data Control . У ADF Collections имеется множество связанных с ними действий, одно из которых - это действие Execute , которое обновит коллекцию; оно привязано к кнопке Refresh . Отметьте, что можно заставить страницу работать немного более гладко, если установим partialSubmit на истину ( TRUE ) для обеих кнопок и добавим значение ID кнопки Refresh атрибуту partialTriggers таблицы Messages : <af:commandButton actionListener="#{bindings.Execute.execute}" text="Refresh" disabled="#{!bindings.Execute.enabled}" partialSubmit="true" id="refreshReceived" /> <af:table value="#{bindings.messages.collectionModel}" var="row" rows="#{bindings.messages.rangeSize}" emptyText="#{bindings.messages.viewable ? 'No rows yet.' : 'Access Denied.'}" fetchSize="#{bindings.messages.rangeSize}" partialTriggers="refreshReceived" > Я послал одно сообщение самому себе - и получил некоторое число сообщений от моего старшего сына, пытающегося привлечь мое внимание: Не стесняйтесь и далее украшать эту страницу, она уже сейчас справляется с порученной ей работой, но все здесь можно немножко "причесать". Например, установите на ложь ( false ) атрибут noWrap для столбца Body , который отображает тело сообщения чата; при этом будет показано все тело сообщения (в нескольких строках, если это необходимо). Использование контейнеров PanelBox - это простой путь к добавлению некоторого контекстного фрейма и заголовка к различным компонентам нашей страницы. Часть 3: использование функциональных возможностей Oracle ADF Active Data Service для "активизации" Web -клиента Хорошо, теперь наш Web -клиент "разговаривает" с Google Talk . Отправка сообщений, получение сообщений …, о чем еще, казалось бы, можно мечтать? Но есть одна вещь, которая далека от идеала - мы должны кликнуть по кнопке Refresh , чтобы увидеть недавно полученные сообщения. Мы хотели бы, чтобы наш клиент чата автоматически сообщал о получаемых им сообщениях. И это именно та функциональная возможность, которую мы собираемся добавить к Web -приложению, используя Oracle ADF Active Data Service ( ADS ). В стек технологий Oracle Fusion включен ADS , который позволяет связывать некоторые компоненты Oracle ADF Faces с активными источниками данных, используя уровень моделирования Oracle ADF или управляемые bean -компоненты. Компоненты, включая таблицу, дерево и все типы графов и диаграмм, могут поддерживать ADS - что означает, что они обновляются почти в реальном времени, в то время как входят в силу изменения в основном источнике данных. Чтобы использовать ADS , нужно иметь склад данных, который сообщает о событиях изменения данных, и вы должны создать бизнес-сервисы, которые реагируют на эти события, а также связанные элементы управления данными для представления этих сервисов. Альтернативным способом использования функциональных возможностей ADS наших компонентов является использование управляемого bean -компонента для атрибута числовой величины, и наличие bean -компонента реализует интерфейс ActiveDataModel . Именно это мы затем сделаем: мы базируем таблицу Messages на новом управляемом bean -компоненте, который реализует ActiveDataModel и регистрирует себя с нашим классом MessageReceiver для новых событий ChatMessage . Сначала внесем некоторые изменения в наш бизнес-сервис. Создадим интерфейс ChatMessageListener: package otn.adf.googletalk; public interface ChatMessageListener { public void processChatMessage(ChatMessage message); } Затем применим к классу MessageReceiver некоторые изменения. Вставим строки, которые добавляют приватный список ChatMessageListeners и метод, позволяющий внешним партнерам регистрировать программы прослушивания: private List messageListeners = new ArrayList(); public void registerChatMessageListener(ChatMessageListener listener) { messageListeners.add(listener); } Добавим эти строки к методу processMessage: for (ChatMessageListener listener:messageListeners) { listener.processChatMessage(message); } Они позаботятся о вызове каждого зарегистрированного слушателя всякий раз, когда будет получено новое сообщение ChatMessage . Теперь мы создадим в проекте GoogleTalkWebClient новый класс: GoogleTalkActiveListener . Этот класс реализует интерфейс ChatMessageListener и регистрируется с MessageReceiver . Он также расширяет CollectionModel ADF , чтобы заставить его действовать как источник данных для компонента таблицы. Это необходимо для реализации таких методов как getRowData (), getRowCount (), getRowIndex (), getRowKey () и setRowKey (). Однако реальный интерес в этом классе представляет та его часть, которая реализует интерфейс ActiveDataModel . Он указывает на компонент таблицы, который может регистрировать с этим классом как ActiveDataListener для ActiveDataUpdateEvents . Всякий раз, когда наш класс GoogleTalkActiveListener получает новое сообщение, он посылает новое ActiveDataUpdateEvent всем программам прослушивания; в нашем случае только таблице. Таблица использует проталкивание с сервера на клиент для обновления списка сообщений, отображенных в браузере. Мы должны пройти следующие шаги: 1. Создание класса GoogleTalkActiveListener . Некоторые из ключевых разделов этого класса упомянуты ниже; полный листинг содержится в каталоге /Part3/ в project files download . В конструкторе мы имеем экземпляр MessageReceiver , связанный с текущей страницей. Затем мы регистрируем новый экземпляр GoogleTalkActiveListener с MessageReceiver как ChatMessageListener . public GoogleTalkActiveListener() { DCBindingContainer bc = (DCBindingContainer)FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet (FacesContext.getCurrentInstance(), "#{bindings}", Object.class); MessageReceiver messageReceiver = (MessageReceiver)bc.findDataControl("MessageReceiver").getDataProvider(); messageReceiver.registerChatMessageListener(this); … processChatMessage () вызывается MessageReceiver всякий раз, когда бывает получено GoogleTalkChatMessage. В свою очередь он создает ActiveDataUpdateEvent с единственным ActiveDateEntry для нового сообщения. Это событие посылают всем зарегистрированным ActiveDataListeners: public synchronized void processChatMessage(ChatMessage message) { Map values = new HashMap(); values.put("body", message.getBody()); values.put("from", message.getFrom()); ActiveDataEntry newEntry = new ChatActiveInsertDataEntry(values, "" + ++rowCount); List updateList = new ArrayList(); updateList.add(newEntry); ActiveDataUpdateEvent event = new ChatActiveDataUpdateEvent(this, rowCount, updateList); for (ActiveDataListener listener : adsListeners) { try { listener.dataChanged(event); } catch (Throwable e) { } } //конец цикла for } 2. Конфигурирование управляемого bean- компонента googleTalkActiveListener. googleTalkActiveListener googletalkwebclient.GoogleTalkActiveListener session 3. Изменение атрибута величины в компоненте af:table для того, чтобы он ссылался на управляемый bean -компонент вместо связывания таблицы. <af:table value="#{googleTalkActiveListener}" var="message"> 4. Удаление кнопки Refresh со страницы ChatClient . Дело не только в том, что ее обновление - это неправильная вещь (связывание таблицы, которую таблица больше не использует); мы также не испытываем необходимости в опции обновления ( refresh ), благодаря инфраструктуре ADS . Здесь мы видим ADS в действии: я кликнул по сообщению в чат в левой стороне страницы. Сообщение, которое я послал, меньше чем через секунду было получено и моим клиентом Google Talk , и таблицей сообщений с поддержкой ADS в правой стороне экрана. Благодаря проталкиванию с сервера на клиент, без какого бы то ни было взаимодействия пользователя с системой сообщение из чата появляется в браузере. Часть 4: метод буксировки для перетаскивания записей данных в чат В этой последней части мы рассмотрим, как функциональные возможности чата, созданные нами к настоящему времени, могут быть объединены в реальную информационно-ориентированную страницу нашего приложения. Мы увидим, как можно использовать функциональные возможности метода буксировки ("перетащил и оставил"), доступные в Oracle ADF 11 g Faces Rich , чтобы позволить пользователям перетаскивать записи, отображенные на странице, и опускать их в компоненте чата, чтобы добавить суть записи в сообщение чата, которое можно послать коллеге или другу. Шаги для достижения этих очень привлекательных расширенных функциональных возможностей являются на удивление простыми и требуют всего лишь небольшого кодирования на третьем шаге: • Предложите данные для отображения.
Предложите данные для отображения Давайте сначала обеспечим некоторые данные для этого приложения. Чтобы избавить вас от проблем, связанных с необходимостью получения доступа к базе данных, для подачи некоторых данных мы используем несколько bean -компонентов. Источники этих классов вы найдете в числе ресурсов для этой обучающей программы. Класс LibraryManager расширяет ArrayList . Он создает множество экземпляров Book Bean и делает их доступными потенциальным потребителям. Класс LibraryManager сконфигурирован как управляемый bean- компонент : libraryManager otn.adf.googletalk.model.LibraryManager session Сделайте макет страницы, включая таблицу данных Страница ChatClient . jspx готова к некоторому "косметическому ремонту". Ниже приводятся требующиеся для этого шаги:
Затем установите Inline Style для компонента Thumbnail af:image на width:60px. Окно структуры для страницы должно теперь выглядеть как-то вроде этого: В этот момент можно выполнить страницу, и вы увидите таблицу с некоторыми деталями книги справа от интегрированного средства чата в левой стороне страницы. Однако, интеграция перетаскивания все еще не доступна. Добавьте функциональные возможности перетаскивания Чтобы указать инфраструктуре Oracle ADF 11 g Faces Rich , что записи в таблице являются перетаскиваемыми (источники перетаскивания), мы должны добавить в таблицу элемент Collection Drag Source : Определите в этом элементе атрибут modelName : <af:collectionDragSource actions="COPY" modelName="libraryModel"/> Аналогично мы должны определить, что должно быть целевым пунктом перемещения для этой операции перетаскивания. Нашим целевым пунктом перемещения является элемент inputText записи Message , который мы используем для печати сообщения из чата. Мы должны добавить к этому inputText элемент DropTarget : Мы специфицируем af:dataFlavor, как принимающий экземпляры класса org.apache.myfaces.trinidad.model.RowKeySet, которые являются тем же, что и полезная нагрузка для операций перетаскивания в записях таблицы; также установите дискриминантный (отличительный) атрибут на libraryModel . Примечание: чтобы сделать элемент inputText регенерируемым через частичную визуализацию страницы ( partial page rendering - PPR), мы должны назначить ему ID - подойдет любое уникальное значение ID . Поскольку мы должны проделать некоторые манипуляции с текстом сообщения, мы будем использовать свойство управляемого bean-компонента, чтобы связать значение inputText с переменной из pageDefinition; установите атрибут величины на #{googleTalkActiveListener.messageBody}. Наконец, мы должны определить, как будет обработано событие опускания. Для этого установите атрибут dropListener элемента dropTarget на #{ googleTalkActiveListener . handleDrop }. <af:inputText id="msgbody" value="#{googleTalkActiveListener.messageBody}" label="Message" columns="60" rows="10"> <f:validator binding="#{bindings.message.validator}"/> <af:dropTarget actions="COPY" dropListener="#{googleTalkActiveListener.handleDrop}"> <af:dataFlavor flavorClass="org.apache.myfaces.trinidad.model.RowKeySet" discriminant="libraryModel"/> </af:dropTarget> </af:inputText> Теперь мы добавили две ссылки на элементы в bean -компоненте googleTalkActiveListener , которого пока еще не существует. Таким образом, заключительный шаг для того, чтобы заставить "перетаскивание" работать, будет сделан в классе GoogleTalkActiveListener . Сначала добавим свойства messageBody и messageAddition (оба типа String ) и handlingDrop (типа Boolean ). Сгенерируем аксессоры ( accessors ) для свойства messageBody . Метод handleDrop вызывается всякий раз, когда элемент msgBody обнаруживает событие опускания ( drop event ). Он получает экземпляр класса DropEvent и возвращает экземпляр DnDAction . В нашем случае обработка события опускания означает выяснение, какая книга была опущена в области сообщения чата, и добавление описания этой книги к текущему сообщению чата. public DnDAction handleDrop(DropEvent dropEvent) { Transferable dropTransferable = dropEvent.getTransferable(); DataFlavor rowKeySetFlavor = DataFlavor.getDataFlavor(RowKeySet.class); RowKeySet tableDrop = dropTransferable.getData(rowKeySetFlavor); if (tableDrop != null) { // получить данные для опущенных строк CollectionModel dragModel = dropTransferable.getData(CollectionModel.class); StringBuilder rowOutput = new StringBuilder(); if (dragModel != null) { for (Object currKey : tableDrop) { dragModel.setRowKey(currKey); Object rowValue = dragModel.getRowData(); rowOutput.append(rowValue); } } messageAddition= rowOutput.toString(); handlingDrop=true; return DnDAction.COPY; } else { return DnDAction.NONE; } } Из события опускания мы отыскиваем объект Transferable. Мы создаем экземпляр DataFlavor для класса, про который мы знаем, что именно он ожидается в этом конкретном событии опускания. Из объекта Transferable мы отыскиваем RowKeySet с ключами строки для записей Book в коллекции таблицы, которая была опущена в этом событии. Затем в цикле for мы извлекаем каждый из RowValues - экземпляров Book - и добавляем их (точнее, результат применения к ним метода toString ())в конец StringBuilder . В этом случае мы реализуем метод toString () для класса Book следующим образом: @Override public String toString() { return this.getTitle()+" by "+this.getAuthor()+", published in "+this.getYear()+" by "+this.getPublisher(); } Итог всех опущенных записей затем сохраняется в свойстве messageAddition , а свойство handlingDrop устанавливается на истину ( TRUE ). В методе setMessageBody (), который вызывается в течение фазы applyRequestValues , после того как выполняется метод handleDrop , мы объединяем текущее значение тела сообщения, посланного от клиента, с добавлением, извлеченным из опущенных записей: public void setMessageBody(String messageBody) { if (handlingDrop) { handlingDrop = false;> this.messageBody= messageBody +" "+ messageAddition; } else { this.messageBody = messageBody; } } Когда вы теперь выполняете страницу ChatClient , вы перетаскиваете любые записи Book и опускаете их на область Message (и никуда больше). Когда вы опускаете запись, детали книги добавляются к сообщению, как будто вы ввели их сами: Конечно, после удаления одной или большего количества книг, вы можете послать сообщение в чат; и снова, это будет точно так же, как если бы вы ввели детали книги сами.
Примите мои поздравления, вы только что построили "активное" приложение с толстым клиентом! Ссылки по теме
|
|