blob: eea20c3534ba1f330d489a859671a39fa194a421 [file] [log] [blame]
/*
* 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.dubbo.common.serialize.protobuf.support;
import com.google.protobuf.Any;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.Empty;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.StringValue;
import org.apache.dubbo.common.serialize.protobuf.support.wrapper.MapValue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* ProtobufAttachmentUtils
*/
public class ProtobufAttachmentUtils {
private static Map<String, BuiltinMarshaller> marshallers = new HashMap<>();
private final static String NULL_CLASS_NAME = "null";
static {
marshaller(String.class, new StringMarshaller());
marshaller(Integer.class, new IntegerMarshaller());
marshaller(Long.class, new LongMarshaller());
marshaller(Boolean.class, new BooleanMarshaller());
marshaller(Float.class, new FloatMarshaller());
marshaller(Double.class, new DoubleMarshaller());
marshallers.put(NULL_CLASS_NAME, new NullMarshaller());
}
static void marshaller(Class<?> clazz, BuiltinMarshaller marshaller) {
marshallers.put(clazz.getCanonicalName(), marshaller);
}
static MapValue.Map wrap(Map<String, Object> attachments) throws IOException {
Map<String, Any> genericAttachments = new HashMap<>(attachments.size());
Map<String, String> stringAttachments = new HashMap<>(attachments.size());
for (Map.Entry<String, Object> entry : attachments.entrySet()) {
genericAttachments.put(entry.getKey(), marshal(entry.getValue()));
stringAttachments.put(entry.getKey(), String.valueOf(entry.getValue()));
}
return MapValue.Map.newBuilder().putAllAttachmentsV2(genericAttachments).putAllAttachments(stringAttachments).build();
}
static Map<String, Object> unwrap(MapValue.Map map) throws InvalidProtocolBufferException {
Map<String, Object> attachments = new HashMap<>();
//compatible with older version.
Map<String, String> stringAttachments = map.getAttachmentsMap();
if (stringAttachments != null) {
stringAttachments.forEach((k, v) -> attachments.put(k, v));
}
Map<String, Any> genericAttachments = map.getAttachmentsV2Map();
if (genericAttachments == null) {
return attachments;
}
for (Map.Entry<String, Any> entry : genericAttachments.entrySet()) {
attachments.put(entry.getKey(), unmarshal(entry.getValue()));
}
return attachments;
}
private static Any marshal(Object obj) throws IOException {
String className = NULL_CLASS_NAME;
if (obj != null) {
className = obj.getClass().getCanonicalName();
}
BuiltinMarshaller marshaller = marshallers.get(className);
if (marshaller == null) {
throw new IllegalStateException(className + " in attachment is not supported by protobuf.");
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ProtobufUtils.serialize(StringValue.newBuilder().setValue(className).build(), stream);
marshaller.marshal(obj, stream);
stream.flush();
return Any.newBuilder().setValue(ByteString.copyFrom(stream.toByteArray())).build();
}
private static Object unmarshal(Any any) throws InvalidProtocolBufferException {
InputStream stream = new ByteArrayInputStream(any.getValue().toByteArray());
String className = ProtobufUtils.deserialize(stream, StringValue.class).getValue();
BuiltinMarshaller marshaller = marshallers.get(className);
if (marshaller == null) {
throw new IllegalStateException(className + " in attachment is not supported by protobuf.");
}
return marshaller.unmarshal(stream);
}
private static interface BuiltinMarshaller<T> {
void marshal(T obj, OutputStream stream) throws IOException;
T unmarshal(InputStream stream) throws InvalidProtocolBufferException;
}
static class StringMarshaller implements BuiltinMarshaller<String> {
@Override
public void marshal(String obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(StringValue.newBuilder().setValue(obj).build(), stream);
}
@Override
public String unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, StringValue.class).getValue();
}
}
static class IntegerMarshaller implements BuiltinMarshaller<Integer> {
@Override
public void marshal(Integer obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(Int32Value.newBuilder().setValue(obj).build(), stream);
}
@Override
public Integer unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, Int32Value.class).getValue();
}
}
static class LongMarshaller implements BuiltinMarshaller<Long> {
@Override
public void marshal(Long obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(Int64Value.newBuilder().setValue(obj).build(), stream);
}
@Override
public Long unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, Int64Value.class).getValue();
}
}
static class BooleanMarshaller implements BuiltinMarshaller<Boolean> {
@Override
public void marshal(Boolean obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(BoolValue.newBuilder().setValue(obj).build(), stream);
}
@Override
public Boolean unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, BoolValue.class).getValue();
}
}
static class FloatMarshaller implements BuiltinMarshaller<Float> {
@Override
public void marshal(Float obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(FloatValue.newBuilder().setValue(obj).build(), stream);
}
@Override
public Float unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, FloatValue.class).getValue();
}
}
static class DoubleMarshaller implements BuiltinMarshaller<Double> {
@Override
public void marshal(Double obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(DoubleValue.newBuilder().setValue(obj).build(), stream);
}
@Override
public Double unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return ProtobufUtils.deserialize(stream, DoubleValue.class).getValue();
}
}
static class NullMarshaller implements BuiltinMarshaller<Object> {
@Override
public void marshal(Object obj, OutputStream stream) throws IOException {
ProtobufUtils.serialize(Empty.newBuilder().build(), stream);
}
@Override
public Object unmarshal(InputStream stream) throws InvalidProtocolBufferException {
return null;
}
}
}