Сериализация RPC-GWT

Мне интересно, есть ли способ получить сериализованную строку из «таблицы строк».

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

7 | 0 | 48 |https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/|03152A2DEBABDCE5D33BF4C88511DD1E|net.customware.gwt.dispatch.client.standard.StandardDispatchService|execute|net.customware.gwt.dispatch.shared.Action | gov.senasa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction / 2514804035 | gov.senasa.embalajemadera.shared.domain.DeclaracionJurada / 1628723960 | java.util.ArrayList / 4159755760 | gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad /4152068152|java.lang.Integer/3438268394|Pallet|gov.senasa.embalajemadera.shared.domain.TipoEmbalaje/309031988|java.util.HashSet/3273092938|gov.senasa.embalajemadera.shared.domain.Contenedor/1178264080|nro contensor 1 | java.lang.Boolean / 476441737 | gov.senasa.embalajemadera.shared.domain.Despachante / 3149599025 | DESP || java.lang.Long / 4227064769 | Treyes 8978 — КАПИТАЛЬНЫЙ ФЕДЕРАЛЬ | [email protected] Спина Гонсало | 45510141 | direccion destino | direccion exportador|java.util.Date/3385151746|chasis/|gov.senasa.embalajemadera.shared.domain.ImportadorExportador/918958990|[email protected]|46sen.rara.jpg shared.domain.DatoAduana / 2671264783 | NRODESPACHO | IC01 | gov.senasa.embalajemadera.shared.domain.LugarDeArribo / 3008903128 | NROMANIIMPO | gov.senasa.embalajemadera.shared.domain.soon350 | merca | gov.senasa.embalajemadera.shared.domain.Pais / 3238585366 | АВСТРАЛИЯ | АФГАНИСТАН | номер экспорта | gov.senasa.embalajemadera.shared.domain.TransportePatente / 1923027028 | 1 | 1 | 1 | 1 | | 5 | 6 | 7 | 0 | 0 | 8 | 1 | 9 | 0 | 10 | 1 | 10 | 0 | 11 | 0 | 12 | 0 | 0 | 11 | 10 | 126951 | 0 | 0 | 0 | 0 | 0 | 13 | 1 | 14 | 0 | 0 | 15 | 16 | 1 | 17 | 0 | 18 | 0 | 19 | 20 | ZRCrAA | 21 | 22 | 19 | 0 | 0 | 23 | 24 | 0 | 0 | 0 | 0 | 0 | 25 | 26 | 0 | 0 | 27 | VnTkM $ A | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 28 | -11 | 29 | 0 | 0 | 19 | -13 | 21 | 30 | 0 | 0 | 0 | 23 | 31 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 1 | 32 | 0 | 16 | 0 | -18 | 0 | 0 | -2 | 0 | 0 | 33 | 0 | 0 | 0 | 27 | VnTkM $ A | 0 | 34 | 35 | 0 | 0 | 0 | 10 | 24754701 | 0 | -18 | 36 | 19 | 37 | 0 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -20 | 39 | 40 | 41 | 0 | 0 | 0 | 42 | 0 | 43 | 10 | 10 | 42 | 0 | 44 | 10 | 1 | 0 | -22 | -5 | 45 | 0 | 0 | 0 | -18 | 8 | 1 | 46 | 0 | 0 | 0 | 19 | 47 | 48 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |

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

0

Решение

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


Запрос десериализации

Это может быть больше, чем пример в документе, но применяются те же правила. Для запрос как этот, мы разбиваем его следующим образом, разбивая на | персонаж.

7

Версия 7

0

Флаги не установлены.

48

Следующие 48 токенов — это таблица строк, из которых строится массив. Эти строки будут представлять типы передаваемых данных, а также фактические строки Java.

Таким образом, мы читаем строки в строку [48], а затем все оставшиеся числа являются либо ссылками, либо примитивами. Данные как ZRCrAA а также VnTkM$A вероятно, base64 кодируется долго. Считайте это списком строк, и мы будем использовать его, когда начнем запрашивать объект из данных.

Как говорится в документе, мы теперь читаем из второго списка, ссылки (начинается с 1|2|3|4|1|5|6|7|0|0|8...). Поскольку мы продолжаем десериализацию, нам нужны еще четыре вещи: URL-адрес приложения, строгое имя политики, класс сервиса, который мы собираемся вызвать, и имя метода в этом классе сервиса.

Поскольку это все строковые объекты, мы будем читать строки. В com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader, фактический класс Java, который будет читать это, мы видим, что readString () выглядит так:

@Override
public String readString() throws SerializationException {
return getString(readInt());
}

Итак, сначала мы читаем int, а затем используем его, чтобы найти строку. Вот getString:

@Override
protected String getString(int index) {
if (index == 0) {
return null;
}
// index is 1-based
assert (index > 0);
assert (index <= stringTable.length);
return stringTable[index - 1];
}

Теперь нашим первым фрагментом данных был базовый URL, читаемый как строка. Мы видим 1 когда мы читаем int, мы получаем (1 — 1)-ю строку в строке [48], которую мы создали выше:

https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/

Строгое название политики следующее, 2, который мы читаем как

03152A2DEBABDCE5D33BF4C88511DD1E

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

Далее мы ищем класс обслуживания, 3:

net.customware.gwt.dispatch.client.standard.StandardDispatchService

И, наконец, метод для вызова, 4:

выполнять

Отсюда вам нужно знать, что StandardDispatchService.execute — мы знаем, что требуется только один аргумент, вероятно, Action экземпляр, так как мы видим 1 указав один аргумент, то 5, который, если он декодирован как Object, означает, что мы читаем 5-ю строку (посмотрите, как работает readObject, чтобы понять почему). Не зная, какие поля Action или другие классы (перечислены ниже), мы не можем догадаться, что будет дальше с какой-либо уверенностью:

  • net.customware.gwt.dispatch.shared.Action
  • gov.senasa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction
  • gov.senasa.embalajemadera.shared.domain.DeclaracionJurada
  • gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad
  • gov.senasa.embalajemadera.shared.domain.TipoEmbalaje
  • gov.senasa.embalajemadera.shared.domain.Contenedor
  • gov.senasa.embalajemadera.shared.domain.Despachante

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


Запрос Сериализации

Мы уже рассмотрели многие основы, пытаясь разобрать это сообщение, поэтому давайте попробуем собрать его воедино. Класс в клиентском коде GWT, который управляет этим, com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter, Методы prepareToWrite() а также toString() помогите немного подготовить почву, показывая самые первые и последние вещи, которые мы будем делать в нашей работе:

/**
* Call this method before attempting to append any tokens. This method
* implementation <b>must</b> be called by any overridden version.
*/
@Override
public void prepareToWrite() {
super.prepareToWrite();
encodeBuffer = new StringBuilder();

// Write serialization policy info
writeString(moduleBaseURL);
writeString(serializationPolicyStrongName);
}

@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
writeHeader(buffer);
writeStringTable(buffer);
writePayload(buffer);
return buffer.toString();
}

prepareToWrite() Метод запускается из потока с добавлением двух строк — базового URL модуля и строгого имени политики — строк, которые вы узнаете в процессе десериализации. toString() Метод показывает три этапа, которые мы выпишем: заголовок, таблица строк и «полезная нагрузка», или ссылки на объекты и значения примитивов.

Почему строки отслеживаются не так, как другие листовые значения? Таким образом, у нас есть все строки в одном месте, так что мы можем ссылаться на них более одного раза и отправлять их каждый раз. Сравните это с XML или JSON, где каждый раз, когда вы хотите использовать значение, вы должны снова записать значение, даже если оно точно такое же.

Заголовок состоит из версии (последняя — 7) и устанавливаемых флагов (в вашем примере подойдет только 0).

В суперклассе AbstractSerializationStreamWriterЕсть четыре поля:

private int objectCount;
private Map<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
private Map<String, Integer> stringMap = new HashMap<String, Integer>();
private List<String> stringTable = new ArrayList<String>();

Первый — это текущий индекс каждого объекта — мы будем использовать его для отслеживания объектов, которые мы видели раньше. следующий objectMap, чтобы мы могли проверить каждый объект и проверить, видели ли мы его раньше, и если да, то где, чтобы мы могли написать ссылку на эту позицию. stringMap Поле делает то же самое, но для строк — JS обрабатывает строковые ключи специально. Наконец, stringTable Сам список всех строк, которые мы видели, каждая добавлена ​​только один раз.

Если вы разберете Java скомпилированного приложения для метода сервиса, такого как List<String> filterStrings(List<String> strings, String startsWith)вы увидите что-то вроде этого:

ClientSerializationStreamWriter streamWriter = ...;//create with serializer
streamWriter.prepareToWrite();
streamWriter.writeString("com.acme.project.shared.MyService");//service interface
streamWriter.writeString("filterStrings");//method name
streamWriter.writeInt(2);//number of arguments to be found in the stream
streamWriter.writeObject(strings);
streamWriter.writeString(startsWith);

Точное знание того, что будет написано для каждого метода, зависит от знания сигнатуры метода в Java — с только что скомпилированным GWT JS и образцом полезной нагрузки отобрать обратный инжиниринг довольно сложно. Но давайте продолжим и посмотрим, что будет дальше.

Реализация writeObject берет объект и сначала отмечает его тип. Если объект нулевой, то мы просто пишем пустую строку (a.k.a. 0) и готово. В противном случае мы проверяем, уже написали ли мы этот объект ранее (и, таким образом, пишем отрицательное число, чтобы увидеть, куда идти в полезной нагрузке), или нам нужно посмотреть, как написать остальную часть этого объекта, и сериализовать каждое поле.

Каждый объект, который можно сериализовать, должен иметь FieldSerializer, который описывает, как кодировать и декодировать этот объект. В GWT есть много CustomFieldSerializer, пользовательских реализаций для определенной цели, которые говорят RPC не генерировать сериализатор автоматически. Одним из примеров может быть ArrayList, если мы передали это в — ArrayList_CustomFieldSerializer делегаты Collection_CustomFieldSerializerBase, который делает это:

public static void serialize(SerializationStreamWriter streamWriter,
Collection instance) throws SerializationException {
int size = instance.size();
streamWriter.writeInt(size);
for (Object obj : instance) {
streamWriter.writeObject(obj);
}
}

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

Итак, у нас есть эти данные в нашей таблице String:

  • BaseUrl
  • название строки политики
  • «Com.acme.project.shared.MyService»
  • «filterStrings»
  • «Java.util.ArrayList»
  • «Java.lang.String»
  • каждая строка в аргументе stringsи строка startsWithХотя мы не знаем, есть ли дубликаты, мы не можем знать, будет ли количество строк одинаковым.

В нашей полезной нагрузке, скажем, мы позвонили filterStrings(["a", "ab", "abc", "a"], "ab"), у нас будут ссылки 1 (индекс строки baseurl), 2 (индекс строки строгого имени политики), 3 (индекс строки имени службы), 4 (индекс строки имени метода), 2 (int для числа ожидаемых полей), 5 (имя класса strings аргумент списка), 4 (количество элементов в списке), 6 (тип первого элемента в списке, строка), 7 (содержимое «a»), 6 (тип строки), 8 (содержимое «ab») ), 6 (строковый тип), 9 (содержимое «abc»), 6 (строковый тип), 7 (содержимое «a»), 6 (строковый тип второго аргумента) и, наконец, 8 (содержимое «ab») снова).

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


Формат сериализации RPC не сложен — я настоятельно рекомендую читать код в различных com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader подклассы, чтобы понять, что он делает. Оттуда вы сможете анализировать эти два списка значений (строки и ссылки / примитивы) в реальные объекты, а также структуру всех классов, которые могут быть отправлены по проводам, и переопределять их на любом языке или в любой среде. ,

5

Другие решения

Если вы не можете изменить уровень обслуживания для экспорта более обычной конечной точки REST, я, вероятно, использовал бы мост Java, используя что-то вроде GWT-syncproxy, и экспорт интерфейса, легко используемого в php.

0

По вопросам рекламы [email protected]