繁体   English   中英

序列化RPC-GWT

[英]Serializing 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中/ 415975576​​0 | 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竞争者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 | destucion destino | direccion exportador | java.util.Date/3385151746 | chasis / | gov.senasa.embalajemadera.shared.domain.Importador Exportador/918958990|gonzalo@gmail.com|46326066|gov.senasa.embalajemadera.shared.domain.DatoAduana/2671264783|NRODESPACHO|IC01|gov.senasa.embalajemadera.shared.domain.LugarDeArribo/3008903128|NROMANIIMPO|gov.senasa。 embalajemadera.shared.domain.PuntoIngreso / 1183502123 | 717.3 | aduana origen | Terrestre camion | merca | gov.senasa.embalajemadera.shared.domain.Pais / 3238585366 |澳大利亚| AFGHANISTAN | nombre exportador | gov.senasa.embalajemadera.shared。 .TransportePatente / 1923027028 | acoplado |底盘| 1 | 2 | 3 | 4 | 1 | 5 | 6 | 7 | 0 | 0 | 8 | 1 | 9 | 0 | 10 | 1 | 10 | 10 | 11 | 10 | 12 | 0 | 0 | 11 | 10 | 126951 | 0 | 0 | 0 | 0 | 0 | 13 | 1 | 14 | 0 | 0 | 15 | 16 | 1 | 17 | 0 | 18 | 10 | 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 | 10 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -20 | 39 | 40 | 41 | 0 | 0 | 0 | 42 | 0 | 43 | 10 | 10 | 42 | 10 | 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兼容的方法,或者只是一个很好的解释,这样我才能制定自己的例程。 谢谢

编辑:我回答反序列化(您可能仍然需要,如果你打算如何处理来自服务器的结果的东西),但你问的系列化。 第一部分仍然是请求的反序列化,但是在中断之下,我将展示如何序列化请求,再次使用与本问题相同的信息限制


要求反序列化

它可能比文档中的示例大,但适用相同的规则。 对于这样的请求 ,我们将其分解为以下内容,将| 字符。

7

版本7。

0

没有设置标志。

48

接下来的48个标记是字符串表,用它们建立一个数组。 这些字符串将代表要传递的数据的类型,以及实际的Java字符串。

因此,我们将字符串读入String [48],然后所有剩余的数字要么是引用,要么是基元。 诸如ZRCrAAVnTkM$A类的数据可能是base64编码的long。 将此也视为字符串列表,当我们开始从数据中请求对象时,将使用它。

如文档所述,我们现在从第二个列表中读取引用(以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,然后在上面创建的String [48]中获得第(1-1)个字符串:

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

强策略名称是next 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进行对比,在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字段执行相同的操作,但对于字符串stringMap特别对待字符串键。 最后, stringTable本身,我们看到的所有字符串的列表,每个仅添加一次。

如果为诸如List<String> filterStrings(List<String> strings, String startsWith)类的服务方法分解编译的应用程序生成的Java,则会看到以下内容:

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的实现采用对象并首先记录其类型。 如果对象为null,则只需编写一个null字符串(也称为0 )即可。 否则,我们检查之前是否已经写过该对象(并写一个负数以查看有效负载中的位置),或者我们需要查找如何写该对象的其余部分并序列化每个字段。

每个可以序列化的对象都必须具有一个FieldSerializer,它描述如何对该对象进行编码和解码。 GWT中有许多CustomFieldSerializer,它们是针对特定目的的自定义实现,它们告诉RPC不要自动生成序列化器。 如果我们已经在ArrayList_CustomFieldSerializer传递了一个示例,则将它传递给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各个子类中的代码,以了解其作用。 从那里,您应该能够将这两个值列表(字符串和引用/基元)解析为实际对象,以及可能通过网络发送的所有类的结构,并以任何语言或框架重新实现。

如果您不能更改服务层以导出更传统的REST端点,则我可能会使用Java桥,使用gwt-syncproxy之的东西 ,并导出可在php中使用的接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM