You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2019/01/14 03:18:13 UTC

[servicecomb-java-chassis] 07/16: [SCB-1071][WIP] refactor any schema

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git

commit 94652588ff958032e6ae9d69e651828bd1f148e5
Author: wujimin <wu...@huawei.com>
AuthorDate: Sat Jan 5 20:37:06 2019 +0800

    [SCB-1071][WIP] refactor any schema
---
 .../protobuf/internal/schema/AnyEntrySchema.java   | 119 --------------
 .../protobuf/internal/schema/AnySchema.java        | 102 ------------
 .../internal/schema/{ => any}/AnyEntry.java        |   2 +-
 .../internal/schema/any/AnyEntrySchema.java        | 177 +++++++++++++++++++++
 .../protobuf/internal/schema/any/AnySchema.java    |  68 ++++++++
 5 files changed, 246 insertions(+), 222 deletions(-)

diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntrySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntrySchema.java
deleted file mode 100644
index f4828e8..0000000
--- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntrySchema.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.servicecomb.foundation.protobuf.internal.schema;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.servicecomb.foundation.protobuf.ProtoMapper;
-import org.apache.servicecomb.foundation.protobuf.RootSerializer;
-import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst;
-
-import io.protostuff.CustomSchema;
-import io.protostuff.Input;
-import io.protostuff.Output;
-
-public class AnyEntrySchema extends CustomSchema<Object> {
-  private final ProtoMapper protoMapper;
-
-  public AnyEntrySchema(ProtoMapper protoMapper) {
-    super(null);
-    this.protoMapper = protoMapper;
-  }
-
-  @Override
-  public boolean isInitialized(Object message) {
-    return true;
-  }
-
-  @Override
-  public Object newMessage() {
-    return new AnyEntry();
-  }
-
-  @Override
-  public void mergeFrom(Input input, Object message) throws IOException {
-    input.readFieldNumber(null);
-    String typeUrl = input.readString();
-
-    input.readFieldNumber(null);
-    byte[] bytes = input.readByteArray();
-
-    input.readFieldNumber(null);
-
-    AnyEntry anyEntry = (AnyEntry) message;
-    anyEntry.setTypeUrl(typeUrl);
-    anyEntry.setValue(bytes);
-  }
-
-  protected String getInputActualTypeName(Object input) {
-    if (!(input instanceof Map)) {
-      return input.getClass().getSimpleName();
-    }
-
-    // @JsonTypeInfo(use = Id.NAME)
-    Object actualTypeName = ((Map<?, ?>) input).get(ProtoConst.JSON_ID_NAME);
-    if (actualTypeName != null && actualTypeName instanceof String) {
-      return (String) actualTypeName;
-    }
-
-    return null;
-  }
-
-  /**
-   * <pre>
-   * if message is type of CustomGeneric&lt;User&gt;
-   * we can not get any information of "User" from message.getClass()
-   *
-   * when use with ServiceComb
-   * proto definition convert from swagger, the proto type will be "CustomGenericUser"
-   * is not match to "CustomGeneric"
-   * so message will be serialized with json schema
-   * </pre>
-   * @param output
-   * @param message
-   * @throws IOException
-   */
-  @Override
-  public void writeTo(Output output, Object message) throws IOException {
-    String actualTypeName = getInputActualTypeName(message);
-    RootSerializer actualValueSerializer = protoMapper.findRootSerializer(actualTypeName);
-    if (actualValueSerializer != null) {
-      standardPack(output, message, actualValueSerializer);
-      return;
-    }
-
-    // not standard, protobuf can not support or not define this type , just extend
-    jsonExtend(output, message);
-  }
-
-  protected void standardPack(Output output, Object message, RootSerializer actualValueSerializer) throws IOException {
-    output.writeString(1,
-        ProtoConst.PACK_SCHEMA + actualValueSerializer.getSchema().getMessage().getCanonicalName(),
-        false);
-
-    byte[] bytes = actualValueSerializer.serialize(message);
-    output.writeByteArray(2, bytes, false);
-  }
-
-  protected void jsonExtend(Output output, Object input) throws IOException {
-    output.writeString(1, ProtoConst.JSON_SCHEMA + input.getClass().getName(), false);
-
-    byte[] bytes = protoMapper.getJsonMapper().writeValueAsBytes(input);
-    output.writeByteArray(2, bytes, false);
-  }
-}
diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnySchema.java
deleted file mode 100644
index cae8c36..0000000
--- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnySchema.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.servicecomb.foundation.protobuf.internal.schema;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
-import org.apache.servicecomb.foundation.protobuf.ProtoMapper;
-import org.apache.servicecomb.foundation.protobuf.RootDeserializer;
-import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst;
-
-import com.fasterxml.jackson.databind.JavaType;
-
-import io.protostuff.Input;
-import io.protostuff.Output;
-import io.protostuff.compiler.model.Field;
-import io.protostuff.compiler.model.Message;
-
-public class AnySchema extends FieldSchema {
-  private final ProtoMapper protoMapper;
-
-  private final AnyEntrySchema anyEntrySchema;
-
-  // key is message canonical name
-  private final Map<String, RootDeserializer> rootDeserializers = new ConcurrentHashMapEx<>();
-
-  public AnySchema(ProtoMapper protoMapper, Field protoField) {
-    super(protoField);
-    this.protoMapper = protoMapper;
-    this.anyEntrySchema = new AnyEntrySchema(protoMapper);
-  }
-
-  @Override
-  public Object readFrom(Input input) throws IOException {
-    AnyEntry anyEntry = (AnyEntry) input.mergeObject(null, anyEntrySchema);
-    if (anyEntry.getTypeUrl().startsWith(ProtoConst.PACK_SCHEMA)) {
-      return standardUnpack(anyEntry.getTypeUrl(), anyEntry.getValue());
-    }
-
-    return jsonExtendMergeFrom(anyEntry.getTypeUrl(), anyEntry.getValue());
-  }
-
-  @Override
-  public void mergeFrom(Input input, Object message) throws IOException {
-    Object anyValue = readFrom(input);
-    setter.set(message, anyValue);
-  }
-
-  @SuppressWarnings("unchecked")
-  protected Object standardUnpack(String typeUrl, byte[] bytes) throws IOException {
-    String msgCanonicalName = typeUrl.substring(ProtoConst.PACK_SCHEMA.length());
-    RootDeserializer valueDeserializer = rootDeserializers
-        .computeIfAbsent(msgCanonicalName, this::createRootDeserializerFromCanonicaName);
-    Object value = valueDeserializer.deserialize(bytes);
-    if (value instanceof Map) {
-      ((Map<String, Object>) value).put(ProtoConst.JSON_ID_NAME, valueDeserializer.getSchema().messageName());
-    }
-    return value;
-  }
-
-  protected RootDeserializer createRootDeserializerFromCanonicaName(String msgCanonicalName) {
-    Message message = protoMapper.getMessageFromCanonicaName(msgCanonicalName);
-    if (message == null) {
-      throw new IllegalStateException(
-          "can not find proto message to create deserializer, name=" + msgCanonicalName);
-    }
-
-    JavaType javaType = protoMapper.getAnyTypes().get(msgCanonicalName);
-    if (javaType == null) {
-      javaType = ProtoConst.MAP_TYPE;
-    }
-    return protoMapper.createRootDeserializer(javaType, message);
-  }
-
-  protected Object jsonExtendMergeFrom(String typeUrl, byte[] bytes) throws IOException {
-    return protoMapper.getJsonMapper().readValue(bytes, Object.class);
-  }
-
-  @Override
-  public void writeTo(Output output, Object value) throws IOException {
-    if (value == null) {
-      return;
-    }
-
-    output.writeObject(number, value, anyEntrySchema, repeated);
-  }
-}
diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntry.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java
similarity index 99%
rename from foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntry.java
rename to foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java
index afd5307..36178de 100644
--- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/AnyEntry.java
+++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.foundation.protobuf.internal.schema;
+package org.apache.servicecomb.foundation.protobuf.internal.schema.any;
 
 public class AnyEntry {
   private String typeUrl;
diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java
new file mode 100644
index 0000000..e371c7c
--- /dev/null
+++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.foundation.protobuf.internal.schema.any;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.protobuf.ProtoMapper;
+import org.apache.servicecomb.foundation.protobuf.RootDeserializer;
+import org.apache.servicecomb.foundation.protobuf.RootSerializer;
+import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+import io.protostuff.InputEx;
+import io.protostuff.OutputEx;
+import io.protostuff.SchemaEx;
+import io.protostuff.SchemaWriter;
+import io.protostuff.WireFormat;
+import io.protostuff.compiler.model.Message;
+
+public class AnyEntrySchema implements SchemaEx<Object> {
+  private final ProtoMapper protoMapper;
+
+  // key is message short name
+  private final Map<String, SchemaWriter<Object>> anyEntrySserializers = new ConcurrentHashMapEx<>();
+
+  // key is message canonical name
+  private final Map<String, RootDeserializer<Object>> rootDeserializers = new ConcurrentHashMapEx<>();
+
+  private final int keyTag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+
+  private final int valueTag = WireFormat.makeTag(2, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+
+  public AnyEntrySchema(ProtoMapper protoMapper) {
+    this.protoMapper = protoMapper;
+  }
+
+  @Override
+  public void init() {
+
+  }
+
+  @Override
+  public void mergeFrom(InputEx input, Object message) throws IOException {
+    input.readFieldNumber();
+    String typeUrl = input.readString();
+
+    input.readFieldNumber();
+    byte[] bytes = input.readByteArray();
+
+    input.readFieldNumber();
+
+    AnyEntry anyEntry = (AnyEntry) message;
+    anyEntry.setTypeUrl(typeUrl);
+    anyEntry.setValue(bytes);
+  }
+
+  public Object deseriaze(InputEx input) throws IOException {
+    AnyEntry anyEntry = new AnyEntry();
+    input.mergeObject(anyEntry, this);
+
+    if (anyEntry.getTypeUrl().startsWith(ProtoConst.PACK_SCHEMA)) {
+      return standardUnpack(anyEntry.getTypeUrl(), anyEntry.getValue());
+    }
+
+    return jsonExtendMergeFrom(anyEntry.getTypeUrl(), anyEntry.getValue());
+  }
+
+  @SuppressWarnings("unchecked")
+  protected Object standardUnpack(String typeUrl, byte[] bytes) throws IOException {
+    String msgCanonicalName = typeUrl.substring(ProtoConst.PACK_SCHEMA.length());
+    RootDeserializer<Object> valueDeserializer = rootDeserializers
+        .computeIfAbsent(msgCanonicalName, this::createRootDeserializerFromCanonicaName);
+    Object value = valueDeserializer.deserialize(bytes);
+    if (value instanceof Map) {
+      ((Map<String, Object>) value).put(ProtoConst.JSON_ID_NAME, valueDeserializer.getSchema().messageName());
+    }
+    return value;
+  }
+
+  protected RootDeserializer<Object> createRootDeserializerFromCanonicaName(String msgCanonicalName) {
+    Message message = protoMapper.getMessageFromCanonicaName(msgCanonicalName);
+    if (message == null) {
+      throw new IllegalStateException(
+          "can not find proto message to create deserializer, name=" + msgCanonicalName);
+    }
+
+    JavaType javaType = protoMapper.getAnyTypes().getOrDefault(msgCanonicalName, ProtoConst.MAP_TYPE);
+    return protoMapper.createRootDeserializer(message, javaType);
+  }
+
+  protected Object jsonExtendMergeFrom(String typeUrl, byte[] bytes) throws IOException {
+    return protoMapper.getJsonMapper().readValue(bytes, Object.class);
+  }
+
+  protected String getInputActualTypeName(Object input) {
+    if (!(input instanceof Map)) {
+      return input.getClass().getSimpleName();
+    }
+
+    // @JsonTypeInfo(use = Id.NAME)
+    Object actualTypeName = ((Map<?, ?>) input).get(ProtoConst.JSON_ID_NAME);
+    if (actualTypeName instanceof String) {
+      return (String) actualTypeName;
+    }
+
+    return null;
+  }
+
+  /**
+   * <pre>
+   * if message is type of CustomGeneric&lt;User&gt;
+   * we can not get any information of "User" from message.getClass()
+   *
+   * when use with ServiceComb
+   * proto definition convert from swagger, the proto type will be "CustomGenericUser"
+   * is not match to "CustomGeneric"
+   * so message will be serialized with json schema
+   * </pre>
+   * @param output
+   * @param value
+   * @throws IOException
+   */
+  @Override
+  public void writeTo(OutputEx output, Object value) throws IOException {
+    String actualTypeName = getInputActualTypeName(value);
+    SchemaWriter<Object> entryWriter = actualTypeName == null ? this::jsonExtend : anyEntrySserializers
+        .computeIfAbsent(actualTypeName, n -> createEntryWriter(n, value));
+    entryWriter.writeTo(output, value);
+  }
+
+  private SchemaWriter<Object> createEntryWriter(String actualTypeName, Object _value) {
+    Message message = protoMapper.getProto().getMessage(actualTypeName);
+    if (message == null) {
+      // not standard, protobuf can not support or not define this type , just extend
+      return this::jsonExtend;
+    }
+
+    // standard pack
+    RootSerializer valueSerializer = protoMapper.createRootSerializer(message, _value.getClass());
+    String valueCanonicalName = message.getCanonicalName();
+    return (output, value) -> {
+      standardPack(output, value, valueCanonicalName, valueSerializer);
+    };
+  }
+
+  protected void standardPack(OutputEx output, Object message, String canonicalName, RootSerializer valueSerializer)
+      throws IOException {
+    output.writeString(keyTag, 1, ProtoConst.PACK_SCHEMA + canonicalName);
+
+    byte[] bytes = valueSerializer.serialize(message);
+    output.writeByteArray(valueTag, 1, bytes);
+  }
+
+  protected void jsonExtend(OutputEx output, Object input) throws IOException {
+    output.writeString(keyTag, 1, ProtoConst.JSON_SCHEMA + input.getClass().getName());
+
+    byte[] bytes = protoMapper.getJsonMapper().writeValueAsBytes(input);
+    output.writeByteArray(valueTag, 1, bytes);
+  }
+}
diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java
new file mode 100644
index 0000000..41ba967
--- /dev/null
+++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.foundation.protobuf.internal.schema.any;
+
+import java.io.IOException;
+
+import org.apache.servicecomb.foundation.common.utils.bean.Getter;
+import org.apache.servicecomb.foundation.common.utils.bean.Setter;
+import org.apache.servicecomb.foundation.protobuf.ProtoMapper;
+import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor;
+
+import io.protostuff.InputEx;
+import io.protostuff.OutputEx;
+import io.protostuff.compiler.model.Field;
+import io.protostuff.runtime.FieldSchema;
+
+public class AnySchema<T> extends FieldSchema<T> {
+  private final AnyEntrySchema anyEntrySchema;
+
+  private final Getter<T, Object> getter;
+
+  private final Setter<T, Object> setter;
+
+  public AnySchema(ProtoMapper protoMapper, Field protoField, PropertyDescriptor propertyDescriptor) {
+    super(protoField, propertyDescriptor.getJavaType());
+
+    this.anyEntrySchema = new AnyEntrySchema(protoMapper);
+    this.getter = propertyDescriptor.getGetter();
+    this.setter = propertyDescriptor.getSetter();
+  }
+
+  @Override
+  public final int mergeFrom(InputEx input, T message) throws IOException {
+    Object anyValue = anyEntrySchema.deseriaze(input);
+    setter.set(message, anyValue);
+
+    return input.readFieldNumber();
+  }
+
+  @Override
+  public void getAndWriteTo(OutputEx output, T message) throws IOException {
+    Object anyEntry = getter.get(message);
+    if (anyEntry == null) {
+      return;
+    }
+
+    output.writeObject(tag, tagSize, anyEntry, anyEntrySchema);
+  }
+
+  @Override
+  public final void writeTo(OutputEx output, Object value) throws IOException {
+    output.writeObject(tag, tagSize, value, anyEntrySchema);
+  }
+}