All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.client.java.transcoder.subdoc.AbstractByteArrayFragmentTranscoder Maven / Gradle / Ivy

/*
 * Copyright (c) 2016 Couchbase, Inc.
 *
 * Licensed 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 com.couchbase.client.java.transcoder.subdoc;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Map;

import com.couchbase.client.deps.io.netty.buffer.ByteBuf;
import com.couchbase.client.deps.io.netty.buffer.ByteBufOutputStream;
import com.couchbase.client.deps.io.netty.buffer.Unpooled;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.error.TranscodingException;
import com.couchbase.client.java.subdoc.MultiValue;
import com.couchbase.client.java.transcoder.TranscoderUtils;

/**
 * An {@link AbstractFragmentTranscoder} that further implements decoding and encoding of messaging,
 * easing the implementation of a concrete {@link FragmentTranscoder} based on byte arrays zero-copied
 * from {@link ByteBuf}.
 *
 * Note that the serialization/deserialization mean should be able to work with byte arrays and write to
 * an {@link OutputStream}, and that byte arrays should be treated as transient (eg. not used to back long
 * living objects), as they may be tied to the original {@link ByteBuf} which will get released.
 *
 * @author Simon Baslé
 * @since 2.3
 */
public abstract class AbstractByteArrayFragmentTranscoder extends AbstractFragmentTranscoder {

    @Override
    public  T decodeWithMessage(ByteBuf encoded, Class clazz, String transcodingErrorMessage) throws TranscodingException {
        try {
            TranscoderUtils.ByteBufToArray toArray = TranscoderUtils.byteBufToByteArray(encoded);
            if (Object.class.equals(clazz)) {
                //generic path that will transform dictionaries to JsonObject and arrays to JsonArray
                return (T) byteArrayToGenericObject(toArray.byteArray, toArray.offset, toArray.length);
            } else {
                return byteArrayToClass(toArray.byteArray, toArray.offset, toArray.length, clazz);
            }
        } catch (Exception e) {
            throw new TranscodingException(transcodingErrorMessage, e);
        }
    }

    @Override
    protected  ByteBuf doEncodeSingle(T value, String transcodingErrorMessage) throws TranscodingException {
        try {
            return Unpooled.wrappedBuffer(writeValueAsBytes(value));
        } catch (Exception e) {
            throw new TranscodingException(transcodingErrorMessage, e);
        }
    }

    @Override
    protected ByteBuf doEncodeMulti(MultiValue multiValue, String transcodingErrorMessage) throws TranscodingException {
        //initial capacity is very roughly and arbitrarily initialized (4 bytes on average per value)
        final ByteBufOutputStream out = new ByteBufOutputStream(Unpooled.buffer(4 * multiValue.size()));
        //Note this OutputStream implementation doesn't implement flush() nor close(), so they are left out.
        try {
            for (Iterator iterator = multiValue.iterator(); iterator.hasNext(); ) {
                Object o = iterator.next();
                writeValueIntoStream(out, o);
                if (iterator.hasNext()) {
                   out.writeBytes(",");
                }
            }
            return out.buffer();
        } catch (Exception e) {
            throw new TranscodingException(transcodingErrorMessage, e);
        }
        //changing the OutputStream concrete implementation would probably require to close() in a finally block
    }

    /**
     * Deserializes a byte array into a generic Object. The provided offset and length must be considered when
     * processing the array, which may hold more data that just the value to deserialize.
     *
     * Note that he byte array should not be considered reliable for long-term usage (eg. backing a String) as
     * it might be tied to the original {@link ByteBuf}, which will get released from the heap.
     *
     * If the array represents a non-scalar value, implementations may choose different classes like a
     * {@link JsonObject} or a {@link Map} to instantiate it.
     *
     * This method is called by {@link #decodeWithMessage(ByteBuf, Class, String)} when the clazz parameter is
     * Object.class.
     *
     * @param byteArray the array of bytes containing the value to deserialize (you'll need to copy it if long term
     *                  usage is needed).
     * @param offset the offset in the array at which the value starts.
     * @param length the number of bytes after the offset that represents the value.
     * @return an instance of a suitable generic Object representation of the value.
     */
    protected abstract Object byteArrayToGenericObject(byte[] byteArray, int offset, int length) throws IOException;

    /**
     * Deserializes a byte array into a specific class instance. The provided offset and length must be considered
     * when processing the array, which may hold more data that just the value to deserialize.
     *
     * Note that he byte array should not be considered reliable for long-term usage (eg. backing a String) as
     * it might be tied to the original {@link ByteBuf}, which will get released from the heap.
     *
     * This method is called by {@link #decodeWithMessage(ByteBuf, Class, String)} when the clazz parameter isn't
     * Object.class.
     *
     * @param byteArray the array of bytes containing the value to deserialize (you'll need to copy it if long term
     *                  usage is needed).
     * @param offset the offset in the array at which the value starts.
     * @param length the number of bytes after the offset that represents the value.
     * @param clazz the {@link Class} to deserialize to.
     * @return an instance of a suitable generic Object representation of the value.
     */
    protected abstract  T byteArrayToClass(byte[] byteArray, int offset, int length, Class clazz) throws IOException;

    /**
     * Serializes a single value object as an array of bytes. The array will be backing a {@link ByteBuf}, so
     * modifications to the array will be visible in the ByteBuf.
     *
     * @param value the value object to serialize.
     * @return the array of bytes representing the serialized value object.
     */
    protected abstract  byte[] writeValueAsBytes(T value) throws IOException;

    /**
     * Serializes a single object out of a sequence of multiple values, into the sequence's {@link OutputStream}.
     * Implementation should simply write the bytes corresponding to the serialized value object into the stream.
     *
     * @param out the {@link OutputStream} of bytes representing a JSON sequence of serialized values.
     * @param o the value among the sequence that is currently serialized.
     */
    protected abstract void writeValueIntoStream(OutputStream out, Object o) throws IOException;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy