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<User>
- * 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<User>
+ * 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);
+ }
+}