Me pregunto si hay un método para obtener una cadena serializada de una "tabla de cadenas".

Lo que necesito es PUBLICAR algunos datos en un formulario de página web. Esto es lo que envía cuando lo hago manualmente:

7 | 0 | 48 | https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/|03152A2DEBABDCE5D33BF4C88511DD1E|net.customwarech.cltient.cltient .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. Palasalet | shared.domain.TipoEmbalaje / 309031988 | java.util.HashSet / 3273092938 | gov.senasa.embalajemadera.shared.domain.Contenedor / 1178264080 | nro contenedor 1 | java.lang.Boolean / 476441737 | gov.senasa. embalajemadera.shared.domain.Despachante / 3149599025 | DESP || java.lang.Long / 4227064769 | Treyes 8978 - CAPITAL FEDERAL|gonzalo@rsystem.com.ar|Spina Gonzalo | 45510141 | direccion destino | direccion exportador | java.util. Fecha/3385151746|chasis/|gov.senasa.embalajemadera.shared.domain.ImportadorExportador/918958990|gonzalo@gmail.com|46326066|gov.senasa.embalajemadera.shared.domain.DatoAduana/2671264783|NRODESPACHO|IC01|gov.NRODESPACHO|IC01|gov. .embalajemadera.shared.domain.LugarDeArribo / 3008903128 | NROMANIIMPO | gov.senasa.embalajemadera.shared.domain.PuntoIngreso / 1183502123 | 717.3 | aduana origen | Terrestre c amion | merca | gov.senasa.embalajemadera.shared.domain.Pais / 3238585366 | AUSTRALIA | AFGHANISTAN | nombre exportador | gov.senasa.embalajemadera.shared.domain.TransportePatente / 1923027028 | acoplado | chasis | 1 | 2 | 3 | 4 | 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 |Can't load full resultsTry againRetrying...Retrying...

Ya leí esto pero, como ve, es un poco más complejo que el ejemplo de la documentación. No puedo construir la carga útil. Necesito un método compatible con VB6 o PHP, o simplemente una buena explicación para poder hacer mi propia rutina. Gracias

2
Gonzalo 27 ene. 2016 a las 23:11

2 respuestas

La mejor respuesta

Editar: respondí deserialización (que es posible que aún necesite si va a hacer algo con los resultados de el servidor), pero solicitó la serialización. La primera parte sigue siendo la deserialización de una solicitud, pero debajo de la pausa, mostraré cómo serializar una solicitud, nuevamente con la información tan limitada que tenemos en esta pregunta.


Solicitar deserialización

Puede ser más grande que el ejemplo del documento, pero se aplican las mismas reglas. Para una solicitud como esta, la desglosamos de la siguiente manera, dividiéndola en el carácter |.

7

Versión 7.

0

No se establecen banderas.

48

Los siguientes 48 tokens son la tabla de cadenas, construya una matriz con ellos. Esas cadenas representarán los tipos de datos que se pasan, así como las cadenas Java reales.

Entonces leemos las cadenas en una Cadena [48], y luego todos los números restantes son referencias o primitivas. Los datos como ZRCrAA y VnTkM$A probablemente estén codificados en base64. Trate esto también como una lista de cadenas, y lo usaremos cuando comencemos a solicitar el objeto de los datos.

Como dice el documento, ahora leemos de la segunda lista, las referencias (comienza con 1|2|3|4|1|5|6|7|0|0|8...). A medida que continuamos deserializando, necesitamos cuatro cosas más: la URL de la aplicación, el nombre seguro de la política, la clase de servicio que estamos a punto de invocar y el nombre del método en esa clase de servicio.

Dado que todos estos son objetos de cadena, leeremos cadenas. En com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader, la clase Java real que leería esto, vemos que readString () se ve así:

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

Entonces, primero leemos un int, luego lo usamos para encontrar una cadena. Aquí está 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];
}

Ahora, nuestro primer dato fue la URL base, leída como una cadena. Vemos 1 como el int que leemos, luego obtenemos la (1 - 1) ésima cadena en la Cadena [48] que creamos arriba:

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

El nombre de la política sólida es el siguiente, 2, que leemos como

03152A2DEBABDCE5D33BF4C88511DD1E

El servidor usará esto y encontrará un archivo llamado 03152A2DEBABDCE5D33BF4C88511DD1E.gwt.rpc, que describe una política de seguridad de lo que se puede crear en el servidor (para prohibir a un pirata informático crear cualquier tipo de objeto en su servidor).

A continuación, buscamos la clase de servicio, 3:

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

Y finalmente el método a invocar, 4:

ejecutar

A partir de aquí, necesita saber qué StandardDispatchService.execute: sabemos que solo se necesita un argumento, probablemente una instancia de Action, ya que vemos 1 indicando un argumento, luego 5 , que si se decodifica como un objeto significa que leemos la quinta cadena (observe cómo funciona readObject para ver por qué). Sin saber qué campos tienen Action o las otras clases (que se enumeran a continuación), realmente no podemos adivinar qué sucede a continuación con certeza:

  • 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

(tenga en cuenta que solo supongo que estas son clases serializables por su paquete y nombre; ¡podrían ser solo cadenas simples que alguien decidió enviar por cable como parte de su solicitud!)


Solicitar serialización

Ya cubrimos muchos de los conceptos básicos para tratar de desarmar este mensaje, así que intentemos armarlo. La clase en el código de cliente de GWT que gestiona esto es com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter. Los métodos prepareToWrite() y toString() ayudan a preparar un poco el escenario, mostrando las primeras y últimas cosas que haremos en nuestro trabajo:

/**
 * 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();
}

El método prepareToWrite() comienza la secuencia agregando dos cadenas: la URL base del módulo y el nombre seguro de la política, cadenas que reconocerá del proceso de deserialización. El método toString() muestra las tres etapas que escribiremos: el encabezado, la tabla de cadenas y la "carga útil", o referencias de objetos y valores primitivos.

¿Por qué tenemos Strings rastreados de manera diferente a otros valores de hoja? De esta manera tenemos todas las cadenas en un solo lugar, de modo que podemos hacer referencia a ellas más de una vez y solo enviarlas cada una una sola vez. Compare esto con XML o JSON, donde cada vez que desee usar un valor, debe escribir el valor nuevamente, incluso si es exactamente el mismo.

El encabezado consta de la versión (la última es 7) y las banderas que se deben establecer (en su muestra, solo 0 servirá).

En la superclase AbstractSerializationStreamWriter, hay cuatro campos:

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>();

El primero es el índice actual de cada objeto; lo usaremos para realizar un seguimiento de los objetos que hemos visto antes. Siguiente objectMap, para que podamos examinar cada objeto y comprobar si lo hemos visto antes, y si es así, dónde, para que podamos escribir una referencia a esa posición. El campo stringMap hace lo mismo pero para cadenas: JS trata las claves de cadena de manera especial. Finalmente, el stringTable en sí, la lista de todas las cadenas que hemos visto, cada una agregada solo una vez.

Si desarma el Java generado por una aplicación compilada para un método de servicio como List<String> filterStrings(List<String> strings, String startsWith), verá algo como esto:

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);

Saber exactamente qué se escribirá para cada método depende de saber cuál es la firma del método en Java; con solo GWT JS compilado y una muestra de carga útil, es algo difícil realizar ingeniería inversa. Pero sigamos y veamos qué pasa después.

La implementación de writeObject toma el objeto y primero anota su tipo. Si el objeto es nulo, simplemente escribimos una cadena nula (también conocida como 0) y listo. De lo contrario, verificamos si ya hemos escrito este objeto antes (y, por lo tanto, escribimos un número negativo para ver dónde ir en la carga útil), o debemos buscar cómo escribir el resto de ese objeto y serializar cada campo.

Cada objeto que se puede serializar debe tener un FieldSerializer, que describe cómo codificar y decodificar ese objeto. Hay muchos CustomFieldSerializers en GWT, implementaciones personalizadas para un propósito específico, que le dicen a RPC que no genere automáticamente un serializador. Un ejemplo podría ser para ArrayList, si hubiéramos pasado eso en - ArrayList_CustomFieldSerializer delegados a Collection_CustomFieldSerializerBase, que hace esto:

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

Primero escribimos el tamaño de la lista, para que el deserializador sepa cuántos elementos leer, y luego escribimos cada elemento de la lista. En nuestro caso, los escribiremos todos como cadenas. Luego, escribiremos una más cadena, el segundo argumento del método.

Entonces, tenemos estos datos en nuestra tabla de cadenas:

  • baseUrl
  • nombre de cadena de política
  • "com.acme.project.shared.MyService"
  • "filterStrings"
  • "java.util.ArrayList"
  • "java.lang.String"
  • cada cadena en el argumento strings, y la cadena startsWith, aunque como no sabemos si hay duplicados, no podemos saber si habrá el mismo número de cadenas.

En nuestra carga útil, digamos que llamamos a filterStrings(["a", "ab", "abc", "a"], "ab"), tendremos las referencias 1 (índice de cadena de baseurl), 2 (índice de cadena de nombre seguro de política), 3 (índice de cadena de nombre de servicio), 4 (índice de cadena de nombre de método) , 2 (int para el número de campos a esperar), 5 (nombre de clase del argumento de lista strings), 4 (número de elementos en la lista), 6 (tipo del primer elemento en la lista, cadena) , 7 (contenido de "a"), 6 (tipo de cadena), 8 (contenido de "ab"), 6 (tipo de cadena), 9 (contenido de "abc"), 6 (tipo de cadena), 7 (contenido de "a"), 6 (tipo de cadena del segundo argumento) y finalmente 8 (contenido de "ab" nuevamente).

¿Cómo se vería esto para clases como Acción, DeclaracionJurada, etc.? Sin saber qué campos hay y en qué orden ocurren, no podemos decirlo con certeza. No hay una buena manera solo desde la carga útil para reconstruir el contenido, aunque si pudiera depurar la aplicación en ejecución justo antes de que se envíe la carga útil, podría observar la estructura del objeto a serializar y usarla para decidir qué ha encontrado en el arroyo. Observo que hay varios números negativos en el flujo de muestra, lo que sugiere que los valores negativos son importantes para el caso de uso, o hay referencias posteriores y que este no es un árbol de objetos simple, sino un gráfico completo, que hará que las cosas un poco más complicado.


El formato de serialización RPC no es complicado; recomiendo encarecidamente leer el código en las diversas subclases com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader para comprender lo que hace. A partir de ahí, debería poder analizar estas dos listas de valores (cadenas y referencias / primitivas) en objetos reales, además de la estructura de todas las clases que podrían enviarse por cable y volver a implementarla en cualquier lenguaje o marco. .

6
Community 20 jun. 2020 a las 09:12

Si no puede cambiar su capa de servicio para exportar un punto final REST más convencional, probablemente usaría un puente java usando algo como gwt-syncproxy, y exportando una interfaz fácilmente utilizable en php.

0
Jérémie B 27 ene. 2016 a las 21:36