/* * Copyright (c) 2015-2016, Christoph Engelbert (aka noctarius) and * contributors. All rights reserved. * * 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.noctarius.tengi.spi.serialization; import com.noctarius.tengi.core.serialization.codec.Decoder; import com.noctarius.tengi.core.serialization.codec.Encoder; import com.noctarius.tengi.core.serialization.marshaller.MarshallerReader; import com.noctarius.tengi.core.serialization.marshaller.MarshallerWriter; /** * <p>The <tt>Protocol</tt> interface describes a common wire-protocol for * object translation. It handles writing and translation of type-ids as * well as the common contract for nullable and non-nullable objects.</p> * <p>In addition, protocols normally take care of registered * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller}s * and type registration.</p> */ public interface Protocol { /** * Returns the mime-type of the <tt>Protocol</tt> for HTTP based protocols. * * @return the protocol's mime-type */ String getMimeType(); /** * Writes the type-id of the given object to the encoder. The default * protocol uses the type <tt>short</tt> for type-ids but other protocols * are free to use any other type. * * @param value the value of which the type-id will be written * @param encoder the <tt>Encoder</tt> to write the type-id to */ void writeTypeId(Object value, Encoder encoder); /** * <p>Resolves the next type-id in the stream (represented by the <tt>Decoder</tt>) * to a class. If the class cannot be found an {@link java.lang.ClassNotFoundException} * is thrown.</p> * <p>The default protocol uses the type <tt>short</tt> for type-ids but other protocols * are free to use any other type.</p> * * @param decoder the <tt>Decoder</tt> to read the type-id from * @param <T> the type of the following up element * @return the class of the following up type */ <T> Class<T> readTypeId(Decoder decoder); /** * <p>Resolves the next type-id in the stream (represented by the <tt>Decoder</tt>) * to a class first and then tries to instantiate it. This can be used from * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} implementations * to first create an instance of the following up element and then read back in the * values.</p> * * @param decoder the <tt>Decoder</tt> to read the type-id from * @param <T> the type of the following up element * @return the instance of the following up type */ <T> T readTypeObject(Decoder decoder); /** * <p>Reads the content of a <b>non-null</b> object from the underlying byte-stream buffer. The content itself * will be deserialized using a registered {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} * or as an internally handled object type. It is up to the * {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to read a type tag for the * object inside the data stream.</p> * <p>If the underlying buffer is to small to read all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * <p><b>The given <tt>fieldName</tt> is strictly used for debugging purpose.</b> The implementation * of the {@link com.noctarius.tengi.core.serialization.debugger.SerializationDebugger} might have * decided to write the value to the stream but this is not required.</p> * * @param <O> the type of the object to write * @param fieldName the name of the field to be read, strictly for debugging purpose only * @param decoder the <tt>Decoder</tt> to read the object from * @return the non-null object value read from the buffer * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to read all elements * @throws java.lang.Exception whenever any other unexpected situation occurs */ <O> O readObject(String fieldName, Decoder decoder) throws Exception; /** * <p>Transfers a <b>non-null</b> object to the underlying byte-stream buffer. The content itself will * be serialized using a registered {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} * or as an internally handled object type. It is up to the * {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to tag the type of the * object inside the data stream.</p> * <p>If the underlying buffer is to small to store all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * <p><b>The given <tt>fieldName</tt> is strictly used for debugging purpose.</b> The implementation * of the {@link com.noctarius.tengi.core.serialization.debugger.SerializationDebugger} might decide * to write the value to the stream but is not required to.</p> * * @param <O> the type of the object to write * @param object the object value to be written to the buffer * @param fieldName the name of the field to be written, strictly for debugging purpose only * @param encoder the <tt>Encoder</tt> to write the object to * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to store all elements * @throws java.lang.NullPointerException whenever the given object is null * @throws java.lang.Exception whenever any other unexpected situation occurs */ <O> void writeObject(String fieldName, O object, Encoder encoder) throws Exception; /** * <p>Transfers a <b>nullable</b> object to the underlying byte-stream buffer. A marker bit is written * to the stream to identify the object was <tt>null</tt> or actual object content is about to follow up. * The content itself will be serialized using a registered * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} or as an internally handled object * type. It is up to the {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to * tag the type of the object inside the data stream.</p> * <p>If the underlying buffer is to small to store all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * <p><b>The given <tt>fieldName</tt> is strictly used for debugging purpose.</b> The implementation * of the {@link com.noctarius.tengi.core.serialization.debugger.SerializationDebugger} might decide * to write the value to the stream but is not required to.</p> * * @param <O> the type of the object to write * @param object the object value to be written to the buffer * @param encoder the <tt>Encoder</tt> to write to * @param writer the <tt>MarshallerWriter</tt> to write the object's content * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to store all elements * @throws java.lang.Exception whenever any other unexpected situation occurs */ default <O> void writeNullable(O object, Encoder encoder, MarshallerWriter<O> writer) throws Exception { if (object == null) { encoder.writeByte(0); return; } encoder.writeByte(1); writer.marshall(object, encoder, this); } /** * <p>Reads the content of a <b>nullable</b> object from the underlying byte-stream buffer. A marker bit is read * from the stream to identify the object was <tt>null</tt> or actual object content is about to follow up. The * content itself will be deserialized using a registered * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} or as an internally handled object type. * It is up to the {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to read a type * tag for the object inside the data stream.</p> * <p>If the underlying buffer is to small to read all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * * @param <O> the type of the object to read * @param decoder the <tt>Decoder</tt> to read the object from * @param reader the <tt>MarshallerReader</tt> to read the object's content * @return the an object value read from the buffer or null * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to read all elements * @throws java.lang.Exception whenever any other unexpected situation occurs */ default <O> O readNullable(Decoder decoder, MarshallerReader<O> reader) throws Exception { if (decoder.readByte() == 1) { return reader.unmarshall(decoder, this); } return null; } /** * <p>Transfers a <b>nullable</b> object to the underlying byte-stream buffer. A marker bit is written * to the stream to identify the object was <tt>null</tt> or actual object content is about to follow up. * The content itself will be serialized using a registered * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} or as an internally handled object * type. It is up to the {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to * tag the type of the object inside the data stream.</p> * <p>If the underlying buffer is to small to store all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * <p><b>The given <tt>fieldName</tt> is strictly used for debugging purpose.</b> The implementation * of the {@link com.noctarius.tengi.core.serialization.debugger.SerializationDebugger} might decide * to write the value to the stream but is not required to.</p> * * @param <O> the type of the object to write * @param fieldName the name of the field to be written, strictly for debugging purpose only * @param object the object value to be written to the buffer * @param encoder the <tt>Encoder</tt> to write to * @param writer the <tt>MarshallerWriter</tt> to write the object's content * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to store all elements * @throws java.lang.Exception whenever any other unexpected situation occurs */ default <O> void writeNullable(String fieldName, O object, Encoder encoder, MarshallerWriter<O> writer) throws Exception { if (object == null) { encoder.writeByte("nullable", 0); return; } encoder.writeByte("nullable", 1); writer.marshall(fieldName, object, encoder, this); } /** * <p>Reads the content of a <b>nullable</b> object from the underlying byte-stream buffer. A marker bit is read * from the stream to identify the object was <tt>null</tt> or actual object content is about to follow up. The * content itself will be deserialized using a registered * {@link com.noctarius.tengi.core.serialization.marshaller.Marshaller} or as an internally handled object type. * It is up to the {@link com.noctarius.tengi.spi.serialization.codec.Codec} implementation on how to read a type * tag for the object inside the data stream.</p> * <p>If the underlying buffer is to small to read all of the content, an * {@link java.lang.IndexOutOfBoundsException} is thrown.</p> * <p><b>The given <tt>fieldName</tt> is strictly used for debugging purpose.</b> The implementation * of the {@link com.noctarius.tengi.core.serialization.debugger.SerializationDebugger} might have * decided to write the value to the stream but this is not required.</p> * * @param <O> the type of the object to read * @param fieldName the name of the field to be read, strictly for debugging purpose only * @param decoder the <tt>Decoder</tt> to read the object from * @param reader the <tt>MarshallerReader</tt> to read the object's content * @return the an object value read from the buffer or null * @throws java.lang.IndexOutOfBoundsException whenever the buffer is too small to read all elements * @throws java.lang.Exception whenever any other unexpected situation occurs */ default <O> O readNullable(String fieldName, Decoder decoder, MarshallerReader<O> reader) throws Exception { if (decoder.readByte("nullable") == 1) { return reader.unmarshall(fieldName, decoder, this); } return null; } }