воскресенье, 15 ноября 2009 г.

Лекция д.т.н, проф. С.Д. Ваулина "Методика написания диссертации"


Я сам аспирант и знаю, что некоторые мои читатели тоже занимаются наукой. Поэтому выкладываю свой конспект специальной лекции, прочитанной проректором по научной работе ЮУрГУ, профессором, доктором технических наук Ваулиным Сергеем Дмитриевичем "Методика написания диссертации".

Учеба в аспирантуре имеет две основные стороны: во-первых, это - непосредственно обучение: прохождение спецкурсов, педагогическая деятельность, кандидатский минимум по философии, английскому языку и специальности; во-вторых - подготовка выпускной квалификационной работы - кандидатской диссертации. Результатом обучения в аспирантуре как раз и является защита этой работы. В идеале, защита должна состояться ровно через 3 года после поступления (для меня - сентябрь 2012-го), чуть хуже - втечение года после окончания обучения в аспирантуре, совсем плохо - позже или никогда.

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

Прямо сейчас, не откладывая, стоит сделать следующее:

1. Четко сформулировать тему диссертации. Естественно, что формулировка в процессе работы может меняться, но сама тема (т.е. конкретное содержание работы) - нет. Очень плохо, если она изменится.

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

3. Написать оглавление диссертации. Практически все диссертации содержат: введение, теоретическую, экспериментальную и методическую (практическу, внедренческую) части.

Теперь рассмотрим основные моменты работы подробнее.

В диссертациях по техническим наукам выделяют 4 главы:

1. Обзорно-аналитическая.

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

В начале работы над диссератцией делается первое приблежение такого обзора. Естественно, что за 3 года ситуация в отрасли может сильно измениться, поэтому данную главу нужно постоянно корректировать. Необходимо всегда быть в курсе последних событий, касающихся своей темы.

Следует не бояться идти своим путем. Для кандидатской диссертации вполне допускается новизна только в ньюансах.

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

2. Опытно-экспериментальная.

Обычно в научно-исследовательской работе выделяют два пути: первый - в зависимости от экспериментальных данных формулируем теорию, второй - имея априорные знания формулируем теорию, а затем доказываем ее экспериментально. В зависимости от того, какой путь выбран данная глава является второй или третьей (а, соответственно, Теоретическая - третьей или второй).

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

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

Заканчивается глава выводами. Вообще, любая глава, кроме первой заканчивается выводами. Их должно быть не более 8-ми, мельчить здесь не стоит.

3. Теоретическая.

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

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

Заканчивается глава выводами.

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

4. Практическая (методическая, внедренческая).

Здесь на основе полученных в предыдущих главах результатов предлагают методику применения этих результатов для получения практической выгоды. Основное, что нужно сделать в этой главе - объяснить, а зачем вообще была проведена работа, какой от нее прок. Если данной главы нет (она - опциональная), то следует ожидать соответствующего вопроса на защите.

Заканчивается глава выводами.

5. Заключение.

В заключении должно быть 8-10 пунктов. Необходимо укрупненно выбрать главное, что нужно знать читателю работы.

6. Список используемых источников (не литературы!).

Помимо литературы здесь могут быть диссертации, отчеты, доклады, статьи и т.д. Должно быть не менее 100 источников.

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

7. Приложение.

Все, что следует показать, но занимает много места следует включать в приложение. Объем диссертации ограничен 150-ю страницами (больше - признак нескромности и может повлиять отрицательно), объем приложения - не ограничен.

8. Введение - самая важная часть диссертации.

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

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

Введение переписывается несколько раз, его объем - не более 6-8 страниц.

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

Объем автореферата - 1 печатный лист, т.е. 16 страниц текста с полуторным интервалом. Содержит обычно введение, самое основное по главам работы, заключение (включается полностью, 1 в 1 с заключением из диссертации), список своих публикаций в рекомендованных ВАК источниках.

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

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

Необходимо иметь 5-8 публикаций по теме работы, причем одна из них должна быть сделана в ВАКовском рецензируемом издании. Для защиты диссертации по техническим наукам очень хорошо иметь патент (в ЮУрГУ развита система патентоведения и есть соответствующие курсы для аспирантов). Так же существует государственный реестр алгоритмов и программ, регистрация своей работы в котором очень приветствуется.

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

Апробация подразумевает выступление на 4-х - 6-ти конференциях и семинарах. Конференции могут быть местные (ЮУрГУ), региональные (Челяб. область), всероссийские и международные. Очень плохо, если все выступления только на местных или региональных конференциях. Необходимо принять участие хотя бы в одной всероссийской и/или международной.

Конференции - хорошее место для того, чтобы о тебе узнали. В дальнейшем это может помочь в получении гранта и поиске оппонентов. Так же обычно издаются сборники трудов конференции, что подразумевает публикацию доклада или тезисов. Информацию о конференциях можно узнавать на сайте ЮУрГУ (здесь и здесь), в других источниках, а также получать от научного руководителя.

Официальные оппоненты - один доктор наук и один кандидат, которые должны ознакомиться с диссертацией и написать отзыв. Важно на третьем году обучения знать, кто будет оппонентами и познакомиться с ними. Если они из другого ВУЗа, то нужно в нем как-то засветиться(например, выступить там на конференции).

Ведущая организация - организация, авторитетная в области по которой осуществляется защита. Ее представители пишут отзыв на диссертацию. Сейчас составляются новые списки ведущих организаций.

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

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

суббота, 14 ноября 2009 г.

ECF: Распределенная обработка событий


Продолжаем разговор. Нанешняя заметка будет посвящена проблеме организации распределенной обработки событий с помощью механизмов, которые предоставляет Eclipse Communication Framework.

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

Вся обработка событий в OSGi базируется на интерфейсе EventAdmin. ECF предлагает свою реализацию этого интерфейса - класс DistributedEventAdmin, который расположен в бандле org.eclipse.ecf.remoteservice.eventadmin. К сожалению, данный бандл не входит в поставку ECF 3.0/3.1, поэтому его придется взять на CVS-сервере: pserver://dev.eclipse.org//cvsroot/rt в каталоге org.eclipse.ecf/server-side/bundles/org.eclipse.ecf.remoteservice.eventadmin.

Работает этот механизм так: DistributedEventAdmin представляет собой SharedObject. Как мы уже говорили, такие объекты копируются между ECF-контейнерами и могут обмениваться сообщениями. Соответственно, все вызовы sendEvent и postEvent - это отправка сообщений между репликами объекта DistributedEventAdmin (синхронная и асинхронная, соответственно).

ECF обладает интересной особенностью: если мы зарегистрируем в разных контейнерах SharedObject'ы под одним и тем же ID, то они будут считаться репликами и между ними будет возможен обмен сообщениями. Это важно, потому что DistributedEventAdmin не умеет автоматически создавать свои реплики (не переопределяет метод initialize() из класса BaseSharedObject), поэтому требуется его регистрировать и в контейнере-источнике событий и в контейнере-приемнике, причем под одним и тем же именем.

Итак, в бандле-источнике событий нужно сделать следующие:

1. Создать серверный контейнер или создать клиентский и подключиться к используемому серверу:

public static final String GENERIC_SERVER_ID = "ecftcp://localhost:4280/mygroup";



public static final String GENERIC_SERVER_CONTAINER = "ecf.generic.server";



// ...

container = getContainerManager().getContainerFactory().createContainer(GENERIC_SERVER_CONTAINER,

        getID(GENERIC_SERVER_ID));



// ...

public static ID getID(String id)

{

    return IDFactory.getDefault().createStringID(id);

}


2. Создать экземляр класса DistributedEventAdmin, расшарить его и стартовать:

DistributedEventAdmin eventAdmin = new DistributedEventAdmin(context);

ISharedObjectContainer so = (ISharedObjectContainer) container.getAdapter(ISharedObjectContainer.class);

so.getSharedObjectManager().addSharedObject(getID(name), eventAdmin, null);



eventAdmin.start();

 


3. Зарегистрировать eventAdmin как сервис. Если мы хотим, чтобы он мог отправлять события с любыми топиками, то так:

context.registerService(EventAdmin.class.getName(), eventAdmin, null);


Если же мы хотим ограничить топики событий, то так:

Properties props = new Properties();

props.put(EventConstants.EVENT_TOPIC, topics);

context.registerService(EventAdmin.class.getName(), eventAdmin, props);

 


Все, после этого можно генерировать события. Они будут отправляться на все реплики DistributedEventAdmin.

Теперь разберемся с обработкой событий. Здесь все несколько проще. Нам нужно:

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

2. Создать экземляр класса DistributedEventAdmin, расшарить его и стартовать.

3. Зарегистрировать обработчик событий:

protected Dictionary<String, Object> getHandlerServiceProperties(String... topics)

{

    Dictionary<String, Object> result = new Hashtable<String, Object>();

    result.put(EventConstants.EVENT_TOPIC, topics);

    return result;

}



// ...

eventHandlerRegistration = bundleContext.registerService(EventHandler.class.getName(),

        new DemoEventHandler(), getHandlerServiceProperties("name/samolisov/ecf/*"));

 


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

Естественно, что контейнеры не обязательно должны быть типа ecf.generic.client и ecf.generic.server. Это могут быть любые контейнеры, реализующие SharedObject API, например XMPP, ActiveMQ, Weblogic и т.д.

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

Скачать примеры к статье (исходники и конфигурации запуска. Rar, 27 Кб)

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

среда, 11 ноября 2009 г.

ECF: Средства для взаимодействия команды разработчиков с помощью Eclipse


Мы уже успели поговорить и о том, что такое Eclipse Communication Framework, и о том, как программировать с его помощью. Сегодня я бы хотел рассказать о тех графических средствах, которые ECF предоставляет для Eclipse (и, соответственно, - ваших RCP-приложений).

Под графическими средствами подразумевается перспектива Communications и виды, которые представлены в группах Communications и File Transfer. Прежде всего эти компоненты GUI обеспечивают возможность взаимодействия разработчиков, использующих Eclipse между собой в команде. Однако, обо всем по порядку.

Давайте перейдем в перспективу Communications. По-умолчанию, внизу, под редактором кода, появятся два вида: Collaboration - обеспечивает общение между участниками проектной команды с использованием специально разработанного ECF-протокола: ecf.generic. Вот так он выглядит, пока соединение с сервером не установлено:



Рядом будет открыт вид Messages - окно сообщений. Здесь мы можем писать сообщения другим пользователям и, соответственно, читать поступившие от них. Пустой вид Messages представлен на рисунке:



На панели быстрого запуска мы можем увидеть кнопку Connect Workspace to Collaboration Group - позволяет соединиться с сервером и начать общение с проектной командой.



Кнопка Connect to Provider в свою очередь обеспечивает подключение с помощью выбранного ECF-провайдера. Например, можно подключиться к IRC, Jabber или MSN серверу.



Если нажать на кнопку Connect Workspace to Collaboration Group, то появится окошко выбора сервера, к которому мы хотим подключиться. Я предварительно запустил сервер на 4280-м порту и именно с ним осуществляю соединение:



Подключение выполнено, правда я сейчас - единственный подключенный к серверу.



Можно запустить еще один экземпляр Eclipse или, если есть возможность, воспользоваться другой машиной. Если писать сообщение в поле ввода, которое расположено внизу под окошком диалога, - оно будет отправлено всем участником команды:



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



Первый пункт: Send Screen Capture to ... позволяет отправить другому разработчику снимок выбранной области экрана. Например, как выглядит участок кода, текста в консоли или еще что-нибудь. Если выбрать данный пункт, то появится окошко-предупреждение, объясняющее, что сейчас надо делать.



Подождем 5 секунд и выделим участок кода. Нам зададут вопрос, а действительно мы хотим этот участок отправить или может быть мы шутим?



К сожалению для ECF, мы не шутим, и ему приходится отправлять выделенный участок кода:



Второй пункт: Co-Browse Web with .... Здесь все просто: мы вводим урл сайта и он открывается во встроенном браузере Eclipse. Причем, открывается как у нас, так и у пользователя, с которым мы ведем взаимодействие.

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



Пишем текст сообщения, нажимаем ОК - сообщение всплывает перед глазами коллеги:



Четвертый пункт: Send Private Message to .... Понятно, что всплывающее сообщение сразу же отвлекает от работы, поэтому пользоваться данной опцией нужно аккуратно. К счастью, существует данный пункт меню. Если его выбрать - на вкладке Messages появится страничка с диалогом. Здесь можно отправлять и получать приватные сообщения от/к пользователя/ю.



Пятый пункт: Send Show View Request to .... Наиболее понравившаяся мне опция, с ее помощью мы отправляем коллеге запрос на открытие у него в Eclipse интересующего нас вида. Т.е., например мы хотим, чтобы он открыл вид Ant. Выбираем данный пункт меню - всплывает окно выбора вида:



Выбираем пункт Ant и он открывается в Eclipse у коллеги:



В общем-то это все, что хотелось сказать о пункте Collaboration. Теперь рассмотрим общение с помощью провайдеров, например подключимся к Jabber, используя свой аккаунт: выберем пункт XMPP, после чего заполним форму.



К сожалению, разработчики не предусмотрели автоматическое определение Jabber-сервера, поэтому его придется вводить руками. Нам вежливо напомнят, что аккаунту на Gmail соответствует сервер talk.google.com.

После подключения к серверу появится вкладка Contacts, на которой, соответственно, будут отображаться все наши контакты:



После того, как мы выберем конкретный контакт, на вкладке Messages откроется страница диалога:



Если контакт подключен к серверу так же с помощью ECF, то в его контекстном меню будут доступны опции Send Show View Request и Send Screen Capture, аналогичные описанным выше. Send File тоже работает.



Вид File Transfer/File Transfer представляет собой download-клиент. После нажатия на иконку Start new File Download откроется диалоговое окно выбора источника файла:



При нажатии на Download файл будет закачан.



Существует так же вид Connections/Chats, который представляет собой список, например, IRC-чатов. Если подключиться к IRC-серверу с помощью соответствующего провайдера, то данный вид откроется и будет активным:



И, наконец, вид Connections/Service Discovery предоставляет нам дерево запущенных удаленных OSGi-сервисов. Показываются все обнаруженные сервисы, включая и запущенные на локальной машине. В случае одного запущенного R-OSGi-сервиса это окно выглядит так:



Понятно, что данное представление существенно помогает при отладке взаимодействия в распределенной OSGi-среде.

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

А вы собираетесь использовать средства, предоставляемые ECF в своей практике? Я имею ввиду как программные абстракции, так и Collaboration/Service Discovery/Providers.

З.Ы. Недавно я начал контрибьютить в ECF - разрабатываю провайдер для взаимодействия по протоколу OSCAR/ICQ с использованием caffeineim. Если вы используете данную библиотеку - хорошая новость для вас: я продолжу ее поддерживать.

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

суббота, 7 ноября 2009 г.

ECF: Распределяем объекты между OSGi-фреймворками


Сегодня мы рассмотрим еще одну замечательную возможность, которую предоставляет нам Eclipse Communication Framework - обмен копиями объектов между бандлами, запущенными на разных экземплярах OSGi-фреймворка (т.е. на разных JVM), реализованную в виде SharedObject API. Данный механизм основан на понятии "репликация", суть которого применительно к ECF следующая: в контейнер добавляется экземпляр класса, реализующего интерфейс ISharedObject, который становится доступен всем другим контейнерам, подключенным к тому же серверу, что и донор. Фактически, в каждом клиентском контейнере создаются копии исходного объекта, которые называются репликами.

Прежде всего разберемся непосредственно с распределяемым объектом. В ECF реализован базовый класс BaseSharedObject, который содержит реализацию многих полезных возможностей, таких как идентификация, управление конфигурацией, логгирование, отправка и прием сообщений. Рекомендуется все свои распределяемые объекты наследовать от данного класса.

Наиболее интересными для нас методами являются BaseSharedObject#initialize(), описывающий стратегию репликации главного объекта (т.е. того объекта, который собственно копируется, для него метод BaseSharedObject#isPrimary() вернет true) и стратегию десериализации реплик из словаря свойств, и BaseSharedObject#getReplicaDescription(ID receiver), задающий порядок сериализации реплицируемого объекта в словарь свойств. Словарь свойств - это объект класса, реализующего Map<String, Serializable>. ECF умеет передавать объекты между контейнерами только в таком виде. Чем-то данный механизм напоминает словарь свойств события, которым оперирует EventAdmin. Данный механизм предоставляет программисту возможность наиболее гибко управлять процессом репликации своего объекта (например, определять какие поля не нужно копировать вовсе).

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

Давайте рассмотрим пример метода initialize():

    /* (non-Javadoc)

     * @see org.eclipse.ecf.core.sharedobject.BaseSharedObject#initialize()

     */


    protected void initialize() throws SharedObjectInitException

    {

        super.initialize();



        if (isPrimary())

        {

            // If primary, then add an event processor that handles activated

            // event by replicating to all current remote containers

            addEventProcessor(new IEventProcessor()

                {

                    public boolean processEvent(Event event)

                    {

                        if (event instanceof ISharedObjectActivatedEvent)

                        {

                            ISharedObjectActivatedEvent activated = (ISharedObjectActivatedEvent) event;

                            if (activated.getActivatedID().equals(getID()) && isConnected())

                            {

                                MySharedObject.this.replicateToRemoteContainers(null);

                            }

                        }

                        return false;

                    }

                });



            System.out.println("Primary(" + getContext().getLocalContainerID().getName() + ")");

        }

        else

        {

            // This is a replica, so deserialize properties from HashMap to Object

            _name = (String) getConfig().getProperties().get(NAME_PROPERTY);

            _property = (DemoProperty) getConfig().getProperties().get(PROPERTY_PROPERTY);

            System.out.println("Replica(" + getContext().getLocalContainerID().getName() + ")");

        }

    }


Чтобы реплицировать главный экземпляр SharedObject'а, мы регистрируем свой обработчик события ISharedObjectActivatedEvent. Обработчик достаточно прост: мы убеждаемся, что действие происходит именно с нами и вызываем метод replicateToRemoteContainers. Вообще, данный метод принимает массив удаленных контейнеров, которым мы хотим отправить наш объект. В случае, если передается null, - объект отправляется всем контейнерам, с которыми можно установить соединение.

Если же перед нами не главный объект, а его реплика, то мы извлекаем из конфигурации нужные поля и строим из них копию объекта (т.е. производим десериализацию).

Пример метода getReplicaDescription:

    /* (non-Javadoc)

     * @see org.eclipse.ecf.core.sharedobject.BaseSharedObject#getReplicaDescription(org.eclipse.ecf.core.identity.ID)

     */


    protected ReplicaSharedObjectDescription getReplicaDescription(ID receiver)

    {

        System.out.println("getReplicaDescription(receiver = " + receiver + ")");



        // Put primary state into properties and include in replica description

        // This is serialization SharedObject into HashMap

        Map<String, Serializable> properties = new HashMap<String, Serializable>();

        properties.put(NAME_PROPERTY, _name);

        properties.put(PROPERTY_PROPERTY, _property);

        return new ReplicaSharedObjectDescription(this.getClass(), getConfig().getSharedObjectID(),

                getConfig().getHomeContainerID(), properties);

    }

 


В качестве параметра в метод передается ID контейнера-получателя. Таким образом обеспечивается возможность строить сериализованное представление объекта в зависимости от ID получателя. Впрочем, в данном примере мы эту возможность не используем. В целом код метода тривиален: строим словарь параметров, по которому создаем ReplicaSharedObjectDescription.

Теперь научимся всем этим пользоваться. Прежде всего - рассмотрим процедуру добавления в контейнер распределяемого объекта. Общий алгоритм такой:

1. Создаем контейнер, тип которого реализует SharedObject API:

public static final String GENERIC_CLIENT_CONTAINER = "ecf.generic.client";

// ...

_client = ContainerFactory.getDefault().createContainer(GENERIC_CLIENT_CONTAINER);


2. Подключаемся к серверу:

private void connectClient() throws Exception

{

    connectClient(_client, createServerID(), null);

}



private ID createServerID() throws Exception

{

    return createNewID(GENERIC_SERVER_ID);

}



private void connectClient(IContainer containerToConnect, ID connectID, IConnectContext context)

        throws ContainerConnectException

{

    containerToConnect.connect(connectID, context);

}



private ID createNewID(String id)

{

    return IDFactory.getDefault().createStringID(id);

}


3. Добавляем распределяемый объект в контейнер:

private ISharedObjectManager getClientSOManager()

{

    return ((ISharedObjectContainer) _client).getSharedObjectManager();

}



// ...



ISharedObjectManager manager = getClientSOManager();

ID id = manager.addSharedObject(createNewID(OBJECT_ID),

    new MySharedObject(OBJECT_NAME, OBJECT_PROPERTY_ID, OBJECT_PROPERTY_A, OBJECT_PROPERTY_B), null);

System.out.println("Added new SharedObject with ID = " + id.getName());


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

Теперь рассмотрим процедуру получения реплики объекта и работы с нею. Вообще, стоит отметить, что SharedObject API подразумевает асинхронное взаимодействие. Т.е. в контейнере, который будет работать с репликами объектов, регистрируется обработчик события ISharedObjectActivatedEvent. Именно в этом обработчике и должен находиться код, принимающий копию распределяемого объекта. Например, такой:

if (event instanceof ISharedObjectActivatedEvent)

{

    try

    {

        ISharedObjectActivatedEvent ae = (ISharedObjectActivatedEvent) event;

        System.out.println("Took shared object with id = " + ae.getActivatedID().getName()

                + " to " + ae.getLocalContainerID().getName());

        ISharedObjectManager manager = getClientSOManager(index);

        ISharedObject sharedObject = manager.getSharedObject(ae.getActivatedID());

        System.out.println("Shared Object class = " + sharedObject.getClass().getName());



        if (sharedObject instanceof MySharedObject)

        {

            MySharedObject my = (MySharedObject) sharedObject;

            System.out.println("Name = " + my.getName());

            System.out.println("Demo = " + my.getProperty());

        }

    }

    catch (Exception e)

    {

        e.printStackTrace();

    }

}


Здесь мы получаем из контейнера экземпляр распределяемого объекта и выводим в OSGi-консоль значения его свойств.

Еще одной интересной возможностью является обмен сообщениями между объектом и его репликами. Такой обмен можно использовать, например, для синхронизации состояния объектов. В примерах к статье приведен код класса MyMessagingSharedObject, демонстрирующего использование данного механизма. Стоит отметить, что отправка сообщения осуществляется посредством вызова метода BaseSharedObject#sendSharedObjectMsgTo, который принимает в качестве параметров ID контейнера-назначения (если равен null - то сообщение будет отправлено всем контейнерам), и сообщение - объект класса SharedObjectMsg, который можно построить с помощью целого ряда методов SharedObjectMsg#createMsg.

Код отправки сообщения может быть таким:

public void sendMessage(ID targetId, Object message) throws IOException

{

    sendSharedObjectMsgTo(null, SharedObjectMsg.createMsg(this.getClass().getName(),

        "handleMessage", new Object[] {getLocalContainerID(), message}));

}


"handleMessage" - имя метода класса распределяемого объекта, который будет вызван при получении сообщения. Обмен сообщениями, как и обмен объектами, - асинхронный.

ECF позволяет управлять механизмом сериализации/десериализации посылаемых/принимаемых сообщений. Для регистрации своего сериализатора/десериализатора необходимо вызвать метод ISharedObjectContainer#setSharedObjectMessageSerializer. Сделать это можно, например, при создании контейнера:

private void createClient() throws Exception

{

    _client = ContainerFactory.getDefault().createContainer(GENERIC_CLIENT_CONTAINER);

    ((ISharedObjectContainer) _client).setSharedObjectMessageSerializer(new XStreamSOMessageSerializer());

}


В примерах приведен код класса XStreamSOMessageSerializer, который использует библиотеку XStream для отправки сообщений в XML-формате.

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

Статья получилась объемная и несколько сумбурная. Если у вас возникли какие-то вопросы - вы всегда можете задать их в комментариях. В следующий раз я постараюсь написать о тех графических средствах, которые предоставляет ECF.

Оставайтесь на связи.

Скачать примеры к статье (исходники и конфигурации запуска. Rar, 375 Кб)

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

понедельник, 26 октября 2009 г.

ECF: Обмен данными между бандлами с помощью DataShare API


Проголосуйте за ролик первой Российской команды, принявшей участие в конкурсе Imagine Cup Digital Media.

Суровый челябинский программист снова с вами и сегодня мы поговорим об ином аспекте взаимодействия бандлов нежели вызов сервисов - об обмене сообщениями. Под сообщением в данном случае подразумевается произвольный поток байт. Для обеспечения такого взаимодействия в состав ECF входит DataShare API.

Суть использования данного API заключается в том, что каждый контейнер может создавать сколь угодно много каналов. Каналы с одинаковыми ID являются связанными и по ним осуществляется асинхронный обмен сообщениями. Асинхронность в данном случае обозначает, что контейнер при создании канала регистрирует для этого канала листенер. При получении сообщений от других контейнеров данный листенер срабатывает. Все свободное от обработки сообщений время контейнер может заниматься своими делами.

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

В качестве примера рассмотрим использование DataShare API с помощью контейнеров семейства ecf.generic и контейнера ecf.xmpp.smack, реализующего соединение по протоколу XMPP (напомню, что данный протокол используется в Jabber).

Оба этих типа контейнеров (ecf.generic.client и ecf.xmpp.smack) не могут взаимодействовать напрямую, а только посредством сервера. В случае ecf.xmpp.smak сервером является любой XMPP-сервер, например gmail.com, ya.ru и т.д. В случае ecf.generic.client сервером является контейнер типа ecf.generic.server, который надо предварительно создать:

public static final String GENERIC_SERVER_ID = "ecftcp://localhost:4280/mygroup";



public static final String GENERIC_SERVER_CONTAINER = "ecf.generic.server";





private IContainer createServer() throws Exception

{

    return ContainerFactory.getDefault().createContainer(GENERIC_SERVER_CONTAINER, getNewID(GENERIC_SERVER_ID));

}



public void start(BundleContext context) throws Exception

{

    _container = createServer();

    // ...

}


Метод gentNewID(String id) будет использоваться во всех примерах, код его тривиален:

private ID getNewID(String id) throws IDCreateException

{

    return IDFactory.getDefault().createStringID(id);

}


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

Общая последовательность работы с DataShare API такова:

1. Создаем контейнеры (например, ecf.generic.client

public static final String GENERIC_CLIENT_CONTAINER = "ecf.generic.client";



//...



containers = new IContainer[CLIENTS_CNT];



for (int i = 0; i < containers.length; i++)

    containers[i] = ContainerFactory.getDefault().createContainer(GENERIC_CLIENT_CONTAINER);

 


2. Создаем для контейнеров каналы, если каналы будут иметь одинаковый ID, то они будут связанными, т.е. по ним можно будет рассылать данные.

public static final String CHANNEL_NAME = "channel";



for (int i = 0; i < CLIENTS_CNT; i++)

{

    IChannelContainerAdapter channelContainer = (IChannelContainerAdapter) containers[i]

                    .getAdapter(IChannelContainerAdapter.class);

    channelContainer.createChannel(getNewID(CHANNEL_NAME), getChannelListener(containers[i].getID()), null);

}


Один контейнер может иметь только один канал с заданным ID. Т.е. нельзя регистрировать два канала с одним и тем же ID для одного и того же контейнера.

Для создания канала используется IChannelContainerAdapter. Чтобы при создании данного адаптера не было NPE, необходимо присутствие бандла org.eclipse.ecf.provider.datashare в конфигурации запуска.

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

private Hashtable<ID, IChannelEvent> messageEvents = new Hashtable<ID, IChannelEvent>();



private IChannelListener getChannelListener(final ID id) throws Exception

{

    return new IChannelListener()

    {

        public void handleChannelEvent(IChannelEvent event)

        {

            if (event instanceof IChannelMessageEvent)

            {

                messageEvents.put(id, event);

            }

        }

    };

}


3. Подключаемся к серверу.

Подключение к серверу осуществляется вызовом метода connect контейнера клиента. Данный метод получает два параметра: ID сервера и так называемый контекст соединения. Контекст соединения может служить, например, для авторизации. В нашем случае авторизация не нужна, поэтому можно передать null.

public static final String GENERIC_SERVER_ID = "ecftcp://localhost:4280/mygroup";



private void connectClients() throws Exception

{

    for (int i = 0; i < containers.length; i++)

        connectClient(containers[i], IDFactory.getDefault().createStringID(GENERIC_SERVER_ID), getConnectContext(i));

}



private IConnectContext getConnectContext(int index)

{

    return null;

}



private void connectClient(IContainer containerToConnect, ID connectID, IConnectContext context)

        throws ContainerConnectException

{

    containerToConnect.connect(connectID, context);

}

 


4. Отправляем сообщение.

Сообщение отправляется с помощью метода IChannel#sendMessage, который может быть вызван с одним обязательным параметром: массивом байт, который будет передан по каналу. В таком случае этот массив байт получат все контейнеры, которые создали канал с соответствующим ID. Другим вариантом может быть вызов метода IChannel#sendMessage с переданным в качестве первого параметра ID контейнера-получателя. В таком случае, сообщение будет передано только этому контейнеру.

private void sendMessages() throws IDCreateException, Exception

{

    IChannelContainerAdapter senderContainer = getChannelContainer(0);



    IChannel sender = senderContainer.getChannel(getNewID(CHANNEL_NAME));

    sender.sendMessage("Hello from ECF!".getBytes());



    sleep(3000);



    for (int i = 1; i < CLIENTS_CNT; i++)

        handleEvent(containers[i].getID());

}



private void handleEvent(ID id)

{

    IChannelEvent event = messageEvents.get(id);

    if (event instanceof IChannelMessageEvent)

    {

        System.out.println("chanelID = " + event.getChannelID().getName());

        System.out.println("data = " + new String(((IChannelMessageEvent) event).getData()));

        System.out.println();

    }

}


Теперь вернемся к серверу. Во-первых, сервер имеет возможность обрабатывать момент соединения с ним клиента. Для этого нужно добавить к контейнеру сервера обработчик события IContainerConnectedEvent. Для добавления обработчиков событий у IContainer есть метод addListener:

// ...

_container.addListener(createConnectedContainerListener());

// ...



private IContainerListener createConnectedContainerListener()

{

    return new IContainerListener()

    {

        public void handleEvent(IContainerEvent event)

        {

            if (event instanceof IContainerConnectedEvent)

            {

                try

                {

                    ID containerId = ((IContainerConnectedEvent) event).getTargetID);

                    System.out.println("connected from " + containerId.getName());

                }

                catch (Exception e)

                {

                    e.printStackTrace();

                }

            }

        }

    };

}


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

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

Интересным примером может быть организация обмена данными с помощью XMPP-протокола. Такой вариант имеет свои особенности.

1. ID сервера задается в пространстве имен ecf.xmpp и представляет собой корректный Jabber Account ID, который будет использоваться.

public static final String XMPP_NAMESPACE = "ecf.xmpp";



// ...

return IDFactory.getDefault().createID(IDFactory.getDefault().getNamespaceByName(XMPP_NAMESPACE),

    "samolisov@gmail.com");


2. При соединении обязательно нужно передать контекст соединения, т.е. пользовательскую пару логин/пароль. Создается контекст соединения следующим образом:

ConnectContextFactory.createUsernamePasswordConnectContext(username, password);


3. ID канала задается в пространстве имен по-умолчанию и может называться как угодно, например, просто channel.

4. При отправке сообщения необходимо обязательно указывать ID получателя, заданное относительно пространства имен ecf.xmpp. Чтобы не было проблем, нужно указывать полный уникальный ID ресурса (т.е. что-то вроде "samolisov@gmail.com/Gajim12345"). Я сделал следующую ошибку: не указал ID ресурса, указал только samolisov@gmail.com, но у меня было запущенно два Джаббер-клиента, в итоге сервер (gmail.com) не смог выбрать куда ему отправить сообщение.

5. Вполне допускается и корректно работает отправка данных между разными Jabber-серверами. Однако, gmail.com, например, не позволяет отправлять сообщение на JID, которого нет в ростере. Поэтому перед тестированием/использованием добавьте JID получателя в ростер отправителя.

6. Существует замечательный инструмент отладки. Если при запуске вашего приложения указать Java-машине параметр -Dsmack.debugEnabled=true, то будет показано окно, отображающее все XMPP-пакеты, проходящие через ваш аккаунт.

Тема взаимодействия бандлов очень интересна. ECF позволяет не только обмениваться потоками байт, но и произвольными Java-объектами, однако, об этом мы поговорим в другой раз.

Оставайтесь на связи.

Скачать примеры к статье (исходники и конфигурации запуска. Rar, 16 Кб)

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

суббота, 24 октября 2009 г.

Два года блогу Сурового челябинского программиста!




Здравствуй, мой дневничок. Поздравляю тебя со знаменательной датой - твоим вторым днем рождения. Ровно два года назад я, работая еще в Capital-IT, завел тебя, чтобы поиграться с платформой blogspot, но кто же знал, что все зайдет так далеко.

А ведь действительно, далеко все зашло. Нет, я еще пока не тысячник, но сегодня на счетчике виднеется число 634, которое меня очень радует. Что особо интересно, начиная с мая, рост числа подписчиков ускоряется (после какого-то порогового значения этот маховик начинает раскручивать сам себя). Для примера: первую сотню я набирал год, вторую - 7 месяцев, третью - 4 месяца, а шестую - 3 недели.

Радует не только рост числа на счетчике. Благодаря блогу я познакомился с очень интересными людьми. Так же смог систематизировать имеющиеся у меня знания, научиться писать и выступать. Работа над блогом позволила мне изучить довольно интересные технологии, с которыми иначе я бы не познакомился. Ну и из приятных бонусов - футболка от Eclipse Foundation и, в конце концов, - наберите в гугле "Суровый челябинский программист" :)

Здесь стоит отметить и поблагодарить ресурсы и их авторов/владельцев, которые абсолютно бесплатно приводят мне посетителей:

- Google - основной трафик идет по поисковым запросам. Самый "брендовый" запрос: "Суровый челябинский программист". Самыми курьезными запросами были: "ошибка t1 на ризографе", "скачать naumen dms", "почему надо переходить на линукс", "блог конкурс приз", "суровый шрифт", "мой блог".



- ХабраХабр - основной источник непоискового трафика. Когда я разместил на нем ссылку Что нового в Eclipse Galileo, то за день пришло 3000 уникальных посетителей. Стоит заметить, что на Хабре я чуть больше года и уже пригласил туда 8 человек.

- Блог о разработке Enterprise-приложений на Java - относительно недавно там был размещен интересный обзор русскоязычныx Java-блогов, откуда и идет большая часть переходов. Рекомендую подписаться на этот блог, его автор хорошо разбирается в том, что называется J2EE и сопутствующих технологиях.

- Progg - преимущественно русскоязычная социальная сеть, напоминающая Digg для программистов. Можно запостить туда ссылку на свою заметку и, если она понравится - за нее будут голосовать.

- Eclipse - да, на официальном сайте Eclipse тоже есть ссылка на мой блог.

- Как можно больше об Eclipse по-русски - блог об Eclipse и всем, что с ним связано. В частности, там присутствует замечательное описание Eclipse Modeling Framework.

- Записки дебианщика - блог о GNU/Linux (естественно, как и отражено в названии - в основном о Debian GNU/Linux). Там можно найти сведения о настройке тех или иных программ и сервисов, а также о верстве диплома в LaTeX.

Интересно, но из социальной сети "ВКонтакте" переходов очень мало, так же как и из Twitter.

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

На последок поделюсь планами на будущий год. В следующем году постараюсь полнее освещать свою научную работу в сфере моделирования/исполнения бизнес-процессов и распределенных систем. Продолжу писать про OSGi, ECF и Eclipse Platform. Начну серию статей о BPEL. Так же, вероятно (пока не уверен), напишу что-нибудь про доверенный документооборот.

В общем, думаю, будет интересно. Оставайтесь на связи :)

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...

пятница, 23 октября 2009 г.

ECF: Используем Remote Services API


При знакомстве с Eclipse Communication Framework'ом мы отметили, что некоторые его контейнеры поддерживают несколько разнородных API. В частности, R-OSGi и Generiс контейнеры, а так же появившийся в недавно вышедшем ECF 3.1 REST контейнер, поддерживают API вызова удаленных сервисов, т.н. Remote Services API.

Давайте поговорим о том, что можно делать с помощью данного API, а затем о том, как его использовать.

ECF Remote Services API это еще один (наряду с R-OSGi) способ обеспечения работы бандлов в распределенной среде. Точно так же одни бандлы могут выставлять сервисы, доступные другим бандлам на другой JVM (и, естественно, даже на другой машине). Отличие от R-OSGi в том, что данная система, во-первых, не стандартизирована (т.е. это не OSGi подсистема, а часть непосредственно ECF), а, во-вторых, - более гибка, т.к. можно явно указывать, где находятся бандлы, выставляющие сервисы, (т.е. отпадает необходимость в процедуре поиска сервисов) и использовать для обеспечения взаимодействия различные протоколы и реализации (ECF Generic, Active MQ, Weblogic, JavaGroups и даже XMPP).

Основной механизм, через который осуществляется использование данного API (как, впрочем, и других API фреймворка) - механизм адаптеров. После создания экземпляра любого контейнера, поддерживающего Remote Services API, необходимо привести его к классу IRemoteServiceContainerAdapter с помощью вызова метода getAdapter(IRemoteServiceContainerAdapter.class).

Небольшое отступление. Использование такого механизма обеспечивает очень высокую гибкость. Основное свойство ECF заключается в том, что каждый контейнер может реализовывать несколько API, а каждый API быть реализовано несколькими контейнерами. Причем можно расширять как набор API, так и набор контейнеров. Поэтому контейнер реализует интерфейс IContainer и знать не знает об каких-то там API. Эта расширяемость как раз и обеспечивается за счет применения механизма адаптеров и их независимой регистрации.

После этого начинаются различия между хостовым бандлом и клиентским. Хостовый бандл регистрирует сервис с помощью вызова метода IRemoteServiceContainerAdapter#registerRemoteService, который получает на вход массив имен сервисов; экземпляр класса, реализующего сервис, и словарь свойств сервиса:

_context = context;



// Create R-OSGi Container

IContainerManager containerManager = getContainerManagerService();



_container = containerManager.getContainerFactory().createContainer("ecf.r_osgi.peer");



// Get remote service container adapter

IRemoteServiceContainerAdapter containerAdapter = (IRemoteServiceContainerAdapter) _container

    .getAdapter(IRemoteServiceContainerAdapter.class);



// Register remote service

_serviceRegistration = containerAdapter.registerRemoteService(new String[] {IHello.class.getName()},

        new SimpleHello(), null);



System.out.println("IHello RemoteService registered");


Клиент же должен найти ссылки на заданные сервисы и обеспечить получение нужного.

public static final String ROSGI_SERVICE_HOST = "r-osgi://localhost:9278";



//...



_context = context;



// Create R-OSGi Container

IContainerManager containerManager = getContainerManagerService();

_container = containerManager.getContainerFactory().createContainer("ecf.r_osgi.peer");



// Get remote service container adapter

IRemoteServiceContainerAdapter containerAdapter = (IRemoteServiceContainerAdapter) _container

       .getAdapter(IRemoteServiceContainerAdapter.class);



// Lookup IRemoteServiceReference

IRemoteServiceReference[] helloReferences = containerAdapter.getRemoteServiceReferences(IDFactory.getDefault().createID(_container.getConnectNamespace(), ROSGI_SERVICE_HOST), IHello.class.getName(), null);



// Get remote service for reference

IRemoteService remoteService = containerAdapter.getRemoteService(helloReferences[0]);

 


Видно, что осуществляется поиск сервисов в конкретном удаленном контейнере, имеющем конкретный адрес.

После получения сервиса его можно вызвать. Remote Service API позволяет вызывать сервис по-разному:

1. С помощью прокси

// Get the proxy

IHello proxy = (IHello) remoteService.getProxy();



// Call the proxy

proxy.hello("RemoteService Client via Proxy");

System.out.println((new Date()) + " RemoteService Called via Proxy");

 


2. Через синхронный вызов. "Вызов" в данном и нижеследующих случаях обозначает экземпляр класса, реализующего интерфейс IRemoteCall.

public void start(BundleContext context) throws Exception

{

    // Call Sync

    remoteService.callSync(createRemoteCall("RemoteService Client Sync"));

    System.out.println((new Date()) + " RemoteService Called Sync");

}



// ...



private IRemoteCall createRemoteCall(final String message)

{

    return new IRemoteCall()

    {

        @Override

        public String getMethod()

        {

            return "hello";

        }



        @Override

        public Object[] getParameters()

        {

            return new Object[] {message};

        }



        @Override

        public long getTimeout()

        {

            return 0;

        }

    };

}


3. Через асинхронный вызов. Напомню, что асинхронный вызов представляет собой команду, посылаемую сервису и обработчик, срабатывающий при возвращении сервисом значения. После отправки команды клиент может продолжать выполнять какой-то код.

Обработчик представляет собой объект класса, реализующего интерфейс IRemoteCallListener

public void start(BundleContext context) throws Exception

{

    // ...

    // Call Async

    remoteService.callAsync(createRemoteCall("RemoteService Client Async"), createRemoteCallListener());

    System.out.println((new Date()) + " RemoteService Called Async");

}



private IRemoteCallListener createRemoteCallListener()

{

    return new IRemoteCallListener()

    {

        @Override

        public void handleEvent(IRemoteCallEvent event)

        {

            if (event instanceof IRemoteCallCompleteEvent)

            {

                IRemoteCallCompleteEvent cce = (IRemoteCallCompleteEvent) event;

                if (!cce.hadException())

                    System.out.println("Remote call completed successfully!");

                else

                    System.out.println("Remote call completed with exception: " + cce.getException());

            }

        }

    };

}


4. Через IFuture. Данный механизм так же асинхронный, но отличается от предыдущего тем, что результат от сервиса мы получаем тогда, когда хотим. Если к тому времени результат еще не вычислен - наш поток блокируется.

// Call Async via IFuture

IFuture future = remoteService.callAsync(createRemoteCall("RemoteService Client Async via Future"));

System.out.println((new Date()) + " Create IFuture");

// ...

future.get();

System.out.println((new Date()) + " RemoteService Called via IFuture");


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

name.samolisov.ecf.hello
name.samolisov.ecf.remoteservices.rosgi.host (для клиента - .client Auto-Start: true)
org.eclipse.ecf
org.eclipse.ecf.sharedobject
org.eclipse.ecf.remoteservice
org.eclipse.ecf.identity
org.eclipse.ecf.discovery
org.eclipse.ecf.provider
org.eclipse.ecf.provider.jmdns
org.eclipse.ecf.provider.r_osgi
org.eclipse.ecf.ssl
ch.ethz.iks.r_osgi.remote
org.eclipse.core.jobs
org.eclipse.core.runtime.compatibility.registry
org.eclipse.equinox.common
org.eclipse.equinox.concurrent
org.eclipse.equinox.registry
org.eclipse.osgi
org.eclipse.osgi.services
org.objectweb.asm


Пара слов об использовании протокола, специально разработанного для ECF. Этот протокол представлен двумя видами контейнеров: ecf.generic.server и ecf.generic.client. Сервер, соответственно, используется в хостовом бандле, а клиент - в клиентском. При создании сервера можно задать его порт и т.н. группу (часть имени после последнего слэша):

public static final String GENERIC_SERVICE_HOST = "ecftcp://localhost:4280/mygroup";



// ...



// Create R-OSGi Container

ID serverId = IDFactory.getDefault().createStringID(GENERIC_SERVICE_HOST);

IContainerManager containerManager = getContainerManagerService();

_container = containerManager.getContainerFactory().createContainer("ecf.generic.server", serverId);


Чтобы при использовании generic в момент получения адаптера не возникло NPE, нужно немного изменить конфигурацию запуска - добавить туда бандл org.eclipse.ecf.provider.remoteservice. Относящиеся к R-OSGi бандлы (ch.ethz.iks.r_osgi.remote, org.objectweb.asm, org.eclipse.ecf.provider.jmdns, org.eclipse.ecf.provider.r_osgi) можно убрать.

Возможно, данная статья поможет вам разобраться с понятием remouting и заставит обратить внимание на соответствующие механизмы платформы Eclipse. В любом случае буду рад ответить на ваши вопросы.

Скачать примеры к статье (исходники, class-файлы и конфигурации запуска. Rar, 39 Кб)

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter


А дальше...