Чтобы добавлять сообщения и комментарии, .

@zEvg:
zEvg

Кто хорошо разбирается в дебрях Flex RPC. Можно ли вызвать какой-то хук после того как обеъект дисериализровался на клиенте, что-типа постконстракта?

@yzh44yzh:
yzh44yzh

Сделал себе интересную подлянку с registerClassAlias. Допустим, у нас есть типизированный объект Message, зарегистрированный этой функцией, так что сервер может отправлять объекты такого типа клиентам. Коннектятся два клиента. Один отправляет Message, сервер бродкастит его на всех, кто законнекчен. И один из клиентов (который отправлял) получает типизированный Message , а второй (который не отправлял) — нетипизированный Object, но с такими же полями.

То бишь, у второго клиента registerClassAlias не сработал. Если второй клиент теперь отправит сообщение, то дальше оба будут получать нормальный Message.

А все потому, что я сделал так:
public class Message
{
registerClassAlias("com.flashdevs.textChatLib.Message", Message);
...

А нужно было так:
registerClassAlias("com.flashdevs.textChatLib.Message", Message);
public class Message
{
...

и registerClassAlias у меня вызывался только при инстанциировании Message, а не раньше. (Причем вызывался многократно при каждом инстанциировании :)

@zEvg:
zEvg

Как делаете вы? Непосредственно мапите бизнес(доменнные) объекты на стороне Java к объектам во Flex ? То есть граф объектов Flex повторяет граф объектов Java. Или создаете специальные классы(посредники, DTO if you will), некое представление бизнес объектов для Flex, при этом граф этих объектов может не повторять граф бизнес объектов. Ваш вариант ?

@zEvg:
zEvg

Провел небольшой бенчмарк AMFPHP vs ZendAMF на нашей системе. Не стал писать никаких синтетических тестов а просто замел на нашем бекенде AMFPHP, который мы уже используем 2 года, на ZendAMF. Скажу сразу что заменить удалось за пол часа, что является очень приятным :). Запустил приложение и замерил время вызова определенного метода, который грузит записи с бекнэда в приложение.
Результаты замера на 100 записях:
// zendamf 100 items
#getBodyItems invocation time: 2.163 secs
#getBodyItems invocation time: 2.23 secs
#getBodyItems invocation time: 2.164 secs
~2.2 s

// amfphp 100 items
#getBodyItems invocation time: 2.061 secs
#getBodyItems invocation time: 2.138 secs
#getBodyItems invocation time: 2.067 secs
~2.07 s

Как видно из замеров время запроса на небольши количествах записей практически не отличается, хотя все-таки видено что amfphp на десятки милисекунд быстрее. Вот эта разница дает значительный разрыв на большом количестве записей.
Результат замера на 2000 записях:
// zendamf 2000 items
#getBodyItems invocation time: 29.729 secs
#getBodyItems invocation time: 28.048 secs
#getBodyItems invocation time: 26.87 secs
#getBodyItems invocation time: 27.195 secs
~28.0 s

// amf 2000 items
#getBodyItems invocation time: 20.419 secs
#getBodyItems invocation time: 19.463 secs
#getBodyItems invocation time: 19.612 secs
#getBodyItems invocation time: 19.513 secs
~20 s

На 2х тыс. разнbца во времени достаточно большая ~8 секунд.
Вывод: остаемся пока на AMFPHP.

ЗЫ. Кстати, проект AMFPHP написан на 4 PHP, уже 3 года как мертв — не поддерживается и не развивается =\

@yzh44yzh:
yzh44yzh

недосериализация amf на wowza причиняет дополнительные неудобства. Red5 даст вам LinkedHashMap<String, Object> для Object и ArrayList<Object> для Array. А Wowza даст AMFDataObj и AMFDataMixedArray соответственно. И их еще нужно будет преобразовывать во что-то юзабельное, чтобы передать в общую для Red5/Wowza бизнес логику.

Но это ерунда в сравнении с тем, что Wowza не поддерживает передачу типизированных объектов, из-за чего и приходится передавать везде Object или Array :)

@yzh44yzh:
yzh44yzh

Будучи надежно защищен юнит-тестами, принялся кромсать amf сериализацию из red5, выбрасывая всякое не нужное :)

Изначально там было 6500 строк кода. Теперь осталось 3600. Это еще не предел, буду кромсать дальше. Зато добавил 800 строк тестов :)

@yzh44yzh:
yzh44yzh

Юнит-тесты выявили баг в работе с reference tables для строк. У себя пофиксил. Послать им баг-репорт, что ли? С патчем.

Однако уже 2 часа ночи. Пора спать. Завтра пошлю )

@yzh44yzh:
yzh44yzh

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

@yzh44yzh:
yzh44yzh

Red5 умеет сериализовать только java bean классы :(
BeanMap beanMap = new BeanMap(object);

Мысль о том, что amf сериализация будет привязана к java bean меня удручает. Нафига лишние зависимости?
К тому же у меня нифига не получается, вылезает java.lang.NoClassDefFoundError :)

Что ж, придется копаться в spicefactory, там это дело как-то похитрее сделано.

@yzh44yzh:
yzh44yzh

Кто чем занят этим чудным вечером (ночью?). Кто-то выкачивает весь интернет для обновления гита, кто-то терзает фотошоп.

А я терзаю сериализацию-десериализацию AMF, выдранную из Red5. Уже покрыл юнит-тестами null, boolean, int, Number и String. Осталось покрыть Date, Array, Object и типизированный класс.

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

@develar:
develar

Касательно вопроса о "времени жизни и доступности" таблиц строк/объектов/traits. juick.com juick.com Будет очень хорошо, если вы ткнете в некий источник, где об этом бы говорилось, но пока что не нашел (ну а в "спеке" от Adobe, этого, конечно, нет).

Ранее всегда думал, что аналог reset stream для Java во флеше есть readObject. То есть каждый readObject якобы работает с чистыми ref tables. И если вы реализуете Externalizable, то предполагаете, что вы теряете все прелести этих таблиц. Но на самом деле оно так только в том случае, если вы прочли из input порцию данных в ByteArray и этот byteArray передали в readExternal. А вот если вы никак не подменяете оригинальный input, то он именно что несет с собой контекст — это естественно и логично — ведь если на стороне сервера данные кодируются одним stream (одним экземпляром), то на стороне флеша вы должны быть в состоянии декодировать в Externalizable классе вектор без возни на стороне сервера с разными ref tables. А вот если на стороне сервера вы кодируете данные разными экземплярами — это ваши проблемы и флеш тут не причем.

@develar:
develar

Кто-либо сталкивался с тем, что "приблуда именующая себя flash player" при декодировании вектора внутри IExternalizable класса неправильно определяет класс элемента? То есть вектор инициализируется правильном типом, а вот для элемента (предположим, длина вектора 1, его тип и тип его контента одинаков — один класс) оно использует текущий класс (свойство которого и должно получать данные readExternal) (все классы при этом 7, то есть fixed и externalizable)? При этом вектор кодируется правильно — если прочесть его отдельно, то все корректно, 10030103640A0700<object data> читается верно.

Нормальной спеки по AMF нет, но в правильности кодирования вектора уверен. Нет в спеке и пояснений о политике reset/clear, но ведь оно же при readObject все traits/string table обнуляет — то есть счетчики в ноль точно, тем более, что ref от length отличается явно и контекстнонезависим.

@yzh44yzh:
yzh44yzh

Ну вот мой доклад yzh44yzh.com

Я планировал больше картинок, но сейчас как-то лень их делать. Может, позже добавлю.

@yzh44yzh:
yzh44yzh

Итак, друзья мои, если в registerClassAlias вы юзаете длинные имена классов, типа "com.mycompany.project.module.vo.SomeVO", то вот это длинное имя класса передается строкой в КАЖДОМ запросе.

Если вы морально готовы пойти на уступки стилю кодирования, и можете отказаться от "com.mycompany.project", то лучше это сделать. Чем короче имя класса, тем больше траффика вы съэкономите.

Хотя если важно уменьшить траффик, то IExternalizebale к вашим услугам.

( Разумеется, при все том AMF существенно компактнее, чем JSON, у уж тем более, чем XML. Очевидная вещь, но на всякий случай уточняю :)

@yzh44yzh:
yzh44yzh

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

Ясное дело, в рамках спецификации AMF такое указать невозможно, ибо никаких сессий там нет. А в рамках RTMP было бы вполне возможно.

Надо точно выяснить этот момент.

@yzh44yzh:
yzh44yzh

Забавно, 55 кодируется одним байтом, а -55 четырьмя байтами.

@develar:
develar

Если кто еще такой тормоз как я — бага bugs.adobe.com закрыта, то есть Dictionary реализован. Вектора, по понятным причинам (type erasure, а это проблемы Java) нет. Но все же нормальная спека должна быть опубликована.

@develar:
develar

@yzh44yzh , рассказывая на bufpug о AMF, потыкай трупик Adobe bugs.adobe.com :)

@yzh44yzh:
yzh44yzh

Я посмотрел реализацию AMF (сериализация и десериализация) в Red5, BlazeDS и Spicefactory. Нигде этот функционал не реализован как отдельный модуль. Во всех трех серверах он размазан по многим классам и нескольким пакетам.

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

В итоге оказалось, что проще всего достать AMF из кода Red5 :)

@yzh44yzh:
yzh44yzh

Реализация в BlazeDS по занудности побивает реализацию в Red5. Ежели интересно вникнуть, то смотреть нужно spicefactory.

А если поставить вопрос иначе? Откуда выдрать сериализацию amf для своего сервера? Думаю, что оттуда, где проще написано. Из spicefactory :)

Проще написано — проще выдрать :)

@yzh44yzh:
yzh44yzh

Вот тут spicefactory.org amf сериализация/десериализация наглядно сделана. Код читаешь, и все понятно. Не то, что в Red5.

@yzh44yzh:
yzh44yzh

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

@yzh44yzh:
yzh44yzh

Ладно, хватит на сегодня. С типизированым объектом вообще все понятно:
var user : User = new User;
user.id = 123;
user.name = "Bob";
var so4 : SharedObject = SharedObject.getLocal("so4");
so4.data.val = user;
/*
76 61 6C val
0A object marker
23 0010|0|01|1 has 2 fields|not-dynamic|traits|value
0F 00001111 (7 << 1) | 1 class name length
76 6F 2E 55 73 65 72 vo.User
05 string header
69 64 id
09 string header
6E 61 6D 65 name
04 integer marker
7B 123
06 string marker
07 string header
42 6F 62 Bob
*/
c нетипизированым объектом непонятно, откуда берутся два null marker, один вначале списка name-value, другой в конце списка
var so3 : SharedObject = SharedObject.getLocal("so3");
so3.data.val = {id:123, name:"David"};
/*
76 61 6C val
0A object marker
0B 0000|1|01|1 dynamic|not traits-ref, not traits-ext but traits|not ref but value
01 null marker ?
09 string header 00001001 (4 << 1) | 1
6E 61 6D 65 name
06 string marker
0B string header 00001011 (5 << 1) | 1
44 61 76 69 64 David
05 string header 00000101 (2 << 1) | 1
69 64 id
04 integer marker
7B 123
01 null marker ?
*/
В исходниках Red5 никакие null marker при сериализации объектов не вставляются.

@yzh44yzh:
yzh44yzh

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

С сериализацией объектов нифига не разобрался. По исходникам то же вроде бы не Rocket Science, но hex-редактор показывает кое-где какие-то непонятные байты, которые я не могу интерпретировать, ни по спеке, ни по исходникам.

К тому же типизированый объект почему-то записался как name1:name2:value1:value2, а не типизированый записался нормально name1:value1:name2:value2

Поищу еще какую-нибудь открытую реализацию. BlazeDS, хотя бы. Может там понятнее будет :)

@yzh44yzh:
yzh44yzh

Увы, моему ограниченому уму не все понятно :(
Сохраняю всякие данные в SharedObject и гляжу hex-редактором, чего там получилось.

Начинаю с простого:
var so1 : SharedObject = SharedObject.getLocal("so1");
so1.data.val = 123;
интерпретируется легко:
76 61 6C | val
04 | integer marker
7B | 123 как число

Чуть более сложное:
var so2 : SharedObject = SharedObject.getLocal("so2");
so2.data.val = "123";
и уже затык
76 61 6C | val
06 | string marker
07 | string header
31 32 33 | 123 как строка
не могу понять string header. По идее первый бит показывает, что это value, а не reference, остальные долны обозначать длинну строки. Но длинна строки — 3 символа (по идее 3 байта в UTF-8), а хедер говорит, что 7.

Дальше еще хуже:
var so3 : SharedObject = SharedObject.getLocal("so3");
so3.data.val = {id:123, name:"Bob"};
76 61 6C val
0A 0B 01 09 ?
6E 61 6D 65 name
06 string marker
07 string header
42 6F 62 Bob
05 ?
69 64 id
04 integer marker
7B 123
01 ?

registerClassAlias("vo.User", vo.User);
var user : User = new User;
user.id = 123;
user.name = "Bob";
var so4 : SharedObject = SharedObject.getLocal("so4");
so4.data.val = user;

76 61 6C val
0A 23 0F ?
76 6F 2E 55 73 65 72 vo.User
05
69 64 id
09
6E 61 6D 65 name
04 integer marker
7B 123
06 string marker
07 string header
42 6F 62 Bob

ладно, пора спать. Рано или поздно разберусь :)

@develar:
develar

Спеки по AMF 3 нету (2007 год устарел, нет вектора и dictionary), Adobe как всегда в своем духе (хотя тут еще плюсуется невозможность использования вектора неявно, так как в java получить тип элемента коллекции (не массива, а именно коллекции) невозможно из-за type erasure — то есть использование в flex messaging ограниченно), но RI ;) В одном моем проекте мне нужно было паковать объекты — juick.com , теперь вот надо примитивы :) Если на стороне Java можно изобретать что-то свое, то на стороне флеша лучше возложить процесс десериализации на сам плеер. В случае коллекции строк она не рассматривается как примитив (только int, uint, Number) и мы имеем overhead 1 байт на элемент — но все штука в том, что строка кодируется не как short+utf8, а как uint29 с переменной длинной + utf8. То бишь это означает, что если строка менее 65536, то этот overhead убирается — ведь 65536 кодируется не в два, а в один байт.

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

@nirthfurzahad:
nirthfurzahad

Привет Жуйк. Есть вопрос на смекалку:
Есть сервер, скажем WebOrb. Там есть метод, принимающий экземпляр Person класса, как параметр.
Задача в том:
Нужно послать экземпляр класса, не разу не объявляя его в коде, и не используя [RemoteClass].
P.S. На вопрос "зачем надо" отвечу потом ^_^.

@neFormal:
neFormal

блядство какое-то.. есть pyamf и наколенная(не моя) реализация AMF на сервере.. точнее это толи AMF0, толи AMF3.. не мышонок, не лягушка.. вот под эту реализацию был подогнан клиент, а я сейчас пытаюсь сделать бота-тестера на питоне, чтобы нагрузить систему и проверить пару потенциальных косяков.. pyamf(с amf3) бодренько декодирует данные, приходящие с сервера, а вот в обратную сторону ЧЛЕН!!1 бля, вот как?. пишет "Invalid data type".. и ведь как то же этот сервер работает с флэшками..

@ilja-panin:
ilja-panin

вопрос к мастерам извращений, как пропихнуть инстанс flash.geom.Rectangle во флеш из джавы? тип Rectangle убирать нельзя, сам класс модифицировать можно (как и сервенную часть)

во флеше имеем
public class Node
{

private var _bounds:Rectangle;
public function set bounds(value:Rectangle):void
{
_bounds = value
}

public function get bounds():Rectangle
{
return _bounds;
}
........

@deep:
deep

amfexplorer.riaforge.org — плагин для ФФ встраиваемый прямо в фаербаг и умеющий смотреть чтоже там реально передается в амф.

@develar:
develar

Чувствуете ли вы себя ущербным и неполноценным программистами из-за того, что пишите на AS для Flash Player? Ах да, 3D-движки, ах да, ведь есть куча задач и не инструмент виноват. Но полноценный нормальный язык типа Java доставляет, как сеанс отдыха ;)

jpauclair из-за того что "Since Flash 10, there is one big flaw with AMF: it doesn’t support Vector." и "not only it’s not implemented, but it’s not even documented!" покопался и jpauclair.net Но у меня другая задача — мне вот надо write, поэтому — pastebin.org (но только Object Vector, int/uint и number не реализовано (как вы можете прочесть в вышеприведенной статье в силу только единственной dense array impl в векторе он имеет более компактную запись (ага, основная проблема as в том, что он как бы сделан в духе js, хотя 99% разработчикам даром не нужно все это — кто-то страдает выбором hash map vs dense array?))).

Код реализован не как хак. В настройку контекста сериализации добавлено два новых свойства — collectionAsVector (сериализовать Java Collection как Flash Vector, false по умолчанию) и fixedVector (по умолчанию true).

Но реверс-инжинирнг jpauclair не полон. Непонятно, а что же с флагом fixed? А он идет после маркера типа и длины — 0x0 not fixed и 0x1 fixed. К тому же в отношении сериализации object vector он ошибается — там нет никакой специальной оптимизации для упаковки, оптимизация общая — если object traits уже есть — пишем только ссылку на. После бита fixed указывается не utf8 encoded имя конкретного класса объекта элемента, а именно имя того класа, что является общим для всех элементов. Вектор может быть объявлен с абстрактным (это в патче не тестировалось ;)), а не конкретным классом, поэтому никакой спецоптимизации типа — запишем 1 раз object traits в начале и все, быть не может. Более того, даже если у вас externalizable это не значит, что после заголовка вектора у вас будет идти сплошной ваш байтовый поток — оно будет разделено маркером типа, в данном случае 10 (kObjectType) — а вот это уже можно было бы оптимизировать, потому что сложно представить зачем может понадобиться вектор у которого маркер типа элемента пляшет (типа kDateType|kByteArrayType|kAvmPlusXmlType) — но в теории Vector.<Object> допустим и поэтому если вам важен размер message — собственный бинарный формат.

Спасибо, Adobe, за документацию.

@kutu:
kutu

есть ли способ получить bytesLoaded или bytesTotal при использовании AMF через NetConnection?