You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vk...@apache.org on 2015/08/27 02:11:38 UTC

[13/59] [abbrv] ignite git commit: ignite-1258: portable objects API support in Ignite

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderReader.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderReader.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderReader.java
new file mode 100644
index 0000000..3e0286f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderReader.java
@@ -0,0 +1,775 @@
+/*
+ * 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.ignite.internal.portable;
+
+import org.apache.ignite.portable.*;
+
+import java.sql.*;
+import java.util.Date;
+import java.util.*;
+
+import static java.nio.charset.StandardCharsets.*;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.*;
+
+/**
+ *
+ */
+class PortableBuilderReader {
+    /** */
+    private static final PortablePrimitives PRIM = PortablePrimitives.get();
+
+    /** */
+    private final Map<Integer, PortableBuilderImpl> objMap = new HashMap<>();
+
+    /** */
+    private final PortableContext ctx;
+
+    /** */
+    private final PortableReaderExImpl reader;
+
+    /** */
+    private byte[] arr;
+
+    /** */
+    private int pos;
+
+    /**
+     * @param objImpl Portable object
+     */
+    PortableBuilderReader(PortableObjectImpl objImpl) {
+        ctx = objImpl.context();
+        arr = objImpl.array();
+        pos = objImpl.start();
+
+        // TODO: IGNITE-1272 - Is class loader needed here?
+        reader = new PortableReaderExImpl(portableContext(), arr, pos, null);
+    }
+
+    /**
+     * @return Portable context.
+     */
+    public PortableContext portableContext() {
+        return ctx;
+    }
+
+    /**
+     * @param obj Mutable portable object.
+     */
+    public void registerObject(PortableBuilderImpl obj) {
+        objMap.put(obj.start(), obj);
+    }
+
+    /**
+     * @return Read int value.
+     */
+    public int readInt() {
+        int res = readInt(0);
+
+        pos += 4;
+
+        return res;
+    }
+
+    /**
+     * @return Read int value.
+     */
+    public byte readByte() {
+        return arr[pos++];
+    }
+
+    /**
+     * @return Read boolean value.
+     */
+    public boolean readBoolean() {
+        return readByte() == 1;
+    }
+
+    /**
+     * @return Read int value.
+     */
+    public byte readByte(int off) {
+        return arr[pos + off];
+    }
+
+    /**
+     * @param off Offset related to {@link #pos}
+     * @return Read int value.
+     */
+    public int readInt(int off) {
+        return PRIM.readInt(arr, pos + off);
+    }
+
+    /**
+     * @param pos Position in the source array.
+     * @return Read int value.
+     */
+    public int readIntAbsolute(int pos) {
+        return PRIM.readInt(arr, pos);
+    }
+
+    /**
+     * @return Read length of array.
+     */
+    public int readLength() {
+        return PRIM.readInt(arr, pos);
+    }
+
+    /**
+     * Read string length.
+     *
+     * @return String length.
+     */
+    public int readStringLength() {
+        boolean utf = PRIM.readBoolean(arr, pos);
+
+        int arrLen = PRIM.readInt(arr, pos + 1);
+
+        return 1 + (utf ? arrLen : arrLen << 1);
+    }
+
+    /**
+     * Reads string.
+     *
+     * @return String.
+     */
+    public String readString() {
+        byte flag = readByte();
+
+        if (flag == NULL)
+            return null;
+
+        if (flag != STRING)
+            throw new PortableException("Failed to deserialize String.");
+
+        boolean convert = readBoolean();
+        int len = readInt();
+
+        String str;
+
+        if (convert) {
+            str = new String(arr, pos, len, UTF_8);
+
+            pos += len;
+        }
+        else {
+            str = String.valueOf(PRIM.readCharArray(arr, pos, len));
+
+            pos += len << 1;
+        }
+
+        return str;
+    }
+
+    /**
+     *
+     */
+    public void skipValue() {
+        byte type = arr[pos++];
+
+        int len;
+
+        switch (type) {
+            case GridPortableMarshaller.NULL:
+                return;
+
+            case GridPortableMarshaller.OBJ:
+                pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS - 1) - 1;
+
+                return;
+
+            case GridPortableMarshaller.BOOLEAN:
+            case GridPortableMarshaller.BYTE:
+                len = 1;
+                break;
+
+            case GridPortableMarshaller.CHAR:
+            case GridPortableMarshaller.SHORT:
+                len = 2;
+
+                break;
+
+            case GridPortableMarshaller.HANDLE:
+            case GridPortableMarshaller.FLOAT:
+            case GridPortableMarshaller.INT:
+                len = 4;
+
+                break;
+
+            case GridPortableMarshaller.ENUM:
+                //skipping type id and ordinal value
+                len = 8;
+
+                break;
+
+            case GridPortableMarshaller.LONG:
+            case GridPortableMarshaller.DOUBLE:
+                len = 8;
+
+                break;
+
+            case GridPortableMarshaller.BYTE_ARR:
+            case GridPortableMarshaller.BOOLEAN_ARR:
+                len = 4 + readLength();
+
+                break;
+
+            case GridPortableMarshaller.STRING:
+                len = 4 + readStringLength();
+
+                break;
+
+            case GridPortableMarshaller.DECIMAL:
+                len = /** scale */ 4  + /** mag len */ 4  + /** mag bytes count */ readInt(4);
+
+                break;
+
+            case GridPortableMarshaller.UUID:
+                len = 8 + 8;
+
+                break;
+
+            case GridPortableMarshaller.DATE:
+                len = 8 + 4;
+
+                break;
+
+            case GridPortableMarshaller.CHAR_ARR:
+            case GridPortableMarshaller.SHORT_ARR:
+                len = 4 + readLength() * 2;
+
+                break;
+
+            case GridPortableMarshaller.INT_ARR:
+            case GridPortableMarshaller.FLOAT_ARR:
+                len = 4 + readLength() * 4;
+
+                break;
+
+            case GridPortableMarshaller.LONG_ARR:
+            case GridPortableMarshaller.DOUBLE_ARR:
+                len = 4 + readLength() * 8;
+
+                break;
+
+            case GridPortableMarshaller.DECIMAL_ARR:
+            case GridPortableMarshaller.DATE_ARR:
+            case GridPortableMarshaller.OBJ_ARR:
+            case GridPortableMarshaller.ENUM_ARR:
+            case GridPortableMarshaller.UUID_ARR:
+            case GridPortableMarshaller.STRING_ARR: {
+                int size = readInt();
+
+                for (int i = 0; i < size; i++)
+                    skipValue();
+
+                return;
+            }
+
+            case GridPortableMarshaller.COL: {
+                int size = readInt();
+
+                pos++; // skip collection type
+
+                for (int i = 0; i < size; i++)
+                    skipValue();
+
+                return;
+            }
+
+            case GridPortableMarshaller.MAP: {
+                int size = readInt();
+
+                pos++; // skip collection type
+
+                for (int i = 0; i < size; i++) {
+                    skipValue(); // skip key.
+                    skipValue(); // skip value.
+                }
+
+                return;
+            }
+
+            case GridPortableMarshaller.MAP_ENTRY:
+                skipValue();
+                skipValue();
+
+                return;
+
+            case GridPortableMarshaller.PORTABLE_OBJ:
+                len = readInt() + 4;
+
+                break;
+
+            default:
+                throw new PortableException("Invalid flag value: " + type);
+        }
+
+        pos += len;
+    }
+
+    /**
+     * @param pos Position.
+     * @param len Length.
+     * @return Object.
+     */
+    public Object getValueQuickly(int pos, int len) {
+        byte type = arr[pos];
+
+        switch (type) {
+            case GridPortableMarshaller.NULL:
+                return null;
+
+            case GridPortableMarshaller.HANDLE: {
+                int objStart = pos - readIntAbsolute(pos + 1);
+
+                PortableBuilderImpl res = objMap.get(objStart);
+
+                if (res == null) {
+                    res = new PortableBuilderImpl(this, objStart);
+
+                    objMap.put(objStart, res);
+                }
+
+                return res;
+            }
+
+            case GridPortableMarshaller.OBJ: {
+                PortableBuilderImpl res = objMap.get(pos);
+
+                if (res == null) {
+                    res = new PortableBuilderImpl(this, pos);
+
+                    objMap.put(pos, res);
+                }
+
+                return res;
+            }
+
+            case GridPortableMarshaller.BYTE:
+                return arr[pos + 1];
+
+            case GridPortableMarshaller.SHORT:
+                return PRIM.readShort(arr, pos + 1);
+
+            case GridPortableMarshaller.INT:
+                return PRIM.readInt(arr, pos + 1);
+
+            case GridPortableMarshaller.LONG:
+                return PRIM.readLong(arr, pos + 1);
+
+            case GridPortableMarshaller.FLOAT:
+                return PRIM.readFloat(arr, pos + 1);
+
+            case GridPortableMarshaller.DOUBLE:
+                return PRIM.readDouble(arr, pos + 1);
+
+            case GridPortableMarshaller.CHAR:
+                return PRIM.readChar(arr, pos + 1);
+
+            case GridPortableMarshaller.BOOLEAN:
+                return arr[pos + 1] != 0;
+
+            case GridPortableMarshaller.DECIMAL:
+            case GridPortableMarshaller.STRING:
+            case GridPortableMarshaller.UUID:
+            case GridPortableMarshaller.DATE:
+            case GridPortableMarshaller.BYTE_ARR:
+            case GridPortableMarshaller.SHORT_ARR:
+            case GridPortableMarshaller.INT_ARR:
+            case GridPortableMarshaller.LONG_ARR:
+            case GridPortableMarshaller.FLOAT_ARR:
+            case GridPortableMarshaller.DOUBLE_ARR:
+            case GridPortableMarshaller.CHAR_ARR:
+            case GridPortableMarshaller.BOOLEAN_ARR:
+            case GridPortableMarshaller.DECIMAL_ARR:
+            case GridPortableMarshaller.DATE_ARR:
+            case GridPortableMarshaller.UUID_ARR:
+            case GridPortableMarshaller.STRING_ARR:
+                return new PortablePlainLazyValue(this, pos, len);
+
+            case GridPortableMarshaller.COL:
+            case GridPortableMarshaller.OBJ_ARR:
+            case GridPortableMarshaller.MAP:
+            case GridPortableMarshaller.ENUM_ARR:
+            case GridPortableMarshaller.MAP_ENTRY:
+                return new LazyCollection(pos);
+
+            case GridPortableMarshaller.ENUM: {
+                if (len == 1) {
+                    assert readByte(pos) == GridPortableMarshaller.NULL;
+
+                    return null;
+                }
+
+                int mark = position();
+                position(pos + 1);
+
+                PortableBuilderEnum builderEnum = new PortableBuilderEnum(this);
+
+                position(mark);
+
+                return builderEnum;
+            }
+
+            case GridPortableMarshaller.PORTABLE_OBJ: {
+                int size = readIntAbsolute(pos + 1);
+
+                int start = readIntAbsolute(pos + 4 + size);
+
+                PortableObjectImpl portableObj = new PortableObjectImpl(ctx, arr, pos + 4 + start);
+
+                return new PortablePlainPortableObject(portableObj);
+            }
+
+            default:
+                throw new PortableException("Invalid flag value: " + type);
+        }
+    }
+
+    /**
+     * @return Parsed value.
+     */
+    public Object parseValue() {
+        int valPos = pos;
+
+        byte type = arr[pos++];
+
+        int plainLazyValLen;
+
+        switch (type) {
+            case GridPortableMarshaller.NULL:
+                return null;
+
+            case GridPortableMarshaller.HANDLE: {
+                int objStart = pos - 1 - readInt();
+
+                PortableBuilderImpl res = objMap.get(objStart);
+
+                if (res == null) {
+                    res = new PortableBuilderImpl(this, objStart);
+
+                    objMap.put(objStart, res);
+                }
+
+                return res;
+            }
+
+            case GridPortableMarshaller.OBJ: {
+                pos--;
+
+                PortableBuilderImpl res = objMap.get(pos);
+
+                if (res == null) {
+                    res = new PortableBuilderImpl(this, pos);
+
+                    objMap.put(pos, res);
+                }
+
+                pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS);
+
+                return res;
+            }
+
+            case GridPortableMarshaller.BYTE:
+                return arr[pos++];
+
+            case GridPortableMarshaller.SHORT: {
+                Object res = PRIM.readShort(arr, pos);
+                pos += 2;
+                return res;
+            }
+
+            case GridPortableMarshaller.INT:
+                return readInt();
+
+            case GridPortableMarshaller.LONG:
+                plainLazyValLen = 8;
+
+                break;
+
+            case GridPortableMarshaller.FLOAT:
+                plainLazyValLen = 4;
+
+                break;
+
+            case GridPortableMarshaller.DOUBLE:
+                plainLazyValLen = 8;
+
+                break;
+
+            case GridPortableMarshaller.CHAR:
+                plainLazyValLen = 2;
+
+                break;
+
+            case GridPortableMarshaller.BOOLEAN:
+                return arr[pos++] != 0;
+
+            case GridPortableMarshaller.DECIMAL:
+                plainLazyValLen = /** scale */ 4  + /** mag len */ 4  + /** mag bytes count */ readInt(4);
+
+                break;
+
+            case GridPortableMarshaller.STRING:
+                plainLazyValLen = 4 + readStringLength();
+
+                break;
+
+            case GridPortableMarshaller.UUID:
+                plainLazyValLen = 8 + 8;
+
+                break;
+
+            case GridPortableMarshaller.DATE:
+                plainLazyValLen = 8 + 4;
+
+                break;
+
+            case GridPortableMarshaller.BYTE_ARR:
+                plainLazyValLen = 4 + readLength();
+
+                break;
+
+            case GridPortableMarshaller.SHORT_ARR:
+                plainLazyValLen = 4 + readLength() * 2;
+
+                break;
+
+            case GridPortableMarshaller.INT_ARR:
+                plainLazyValLen = 4 + readLength() * 4;
+
+                break;
+
+            case GridPortableMarshaller.LONG_ARR:
+                plainLazyValLen = 4 + readLength() * 8;
+
+                break;
+
+            case GridPortableMarshaller.FLOAT_ARR:
+                plainLazyValLen = 4 + readLength() * 4;
+
+                break;
+
+            case GridPortableMarshaller.DOUBLE_ARR:
+                plainLazyValLen = 4 + readLength() * 8;
+
+                break;
+
+            case GridPortableMarshaller.CHAR_ARR:
+                plainLazyValLen = 4 + readLength() * 2;
+
+                break;
+
+            case GridPortableMarshaller.BOOLEAN_ARR:
+                plainLazyValLen = 4 + readLength();
+
+                break;
+
+            case GridPortableMarshaller.OBJ_ARR:
+                return new PortableObjectArrayLazyValue(this);
+
+            case GridPortableMarshaller.DATE_ARR: {
+                int size = readInt();
+
+                Date[] res = new Date[size];
+
+                for (int i = 0; i < res.length; i++) {
+                    byte flag = arr[pos++];
+
+                    if (flag == GridPortableMarshaller.NULL) continue;
+
+                    if (flag != GridPortableMarshaller.DATE)
+                        throw new PortableException("Invalid flag value: " + flag);
+
+                    long time = PRIM.readLong(arr, pos);
+
+                    pos += 8;
+
+                    if (ctx.isUseTimestamp()) {
+                        Timestamp ts = new Timestamp(time);
+
+                        ts.setNanos(ts.getNanos() + readInt());
+
+                        res[i] = ts;
+                    }
+                    else {
+                        res[i] = new Date(time);
+
+                        pos += 4;
+                    }
+                }
+
+                return res;
+            }
+
+            case GridPortableMarshaller.UUID_ARR:
+            case GridPortableMarshaller.STRING_ARR:
+            case GridPortableMarshaller.DECIMAL_ARR: {
+                int size = readInt();
+
+                for (int i = 0; i < size; i++) {
+                    byte flag = arr[pos++];
+
+                    if (flag == GridPortableMarshaller.UUID)
+                        pos += 8 + 8;
+                    else if (flag == GridPortableMarshaller.STRING)
+                        pos += 4 + readStringLength();
+                    else if (flag == GridPortableMarshaller.DECIMAL)
+                        pos += 4 + readLength();
+                    else
+                        assert flag == GridPortableMarshaller.NULL;
+                }
+
+                return new PortablePlainLazyValue(this, valPos, pos - valPos);
+            }
+
+            case GridPortableMarshaller.COL: {
+                int size = readInt();
+                byte colType = arr[pos++];
+
+                switch (colType) {
+                    case GridPortableMarshaller.USER_COL:
+                    case GridPortableMarshaller.ARR_LIST:
+                        return new PortableLazyArrayList(this, size);
+
+                    case GridPortableMarshaller.LINKED_LIST:
+                        return new PortableLazyLinkedList(this, size);
+
+                    case GridPortableMarshaller.HASH_SET:
+                    case GridPortableMarshaller.LINKED_HASH_SET:
+                    case GridPortableMarshaller.TREE_SET:
+                    case GridPortableMarshaller.CONC_SKIP_LIST_SET:
+                        return new PortableLazySet(this, size);
+                }
+
+                throw new PortableException("Unknown collection type: " + colType);
+            }
+
+            case GridPortableMarshaller.MAP:
+                return PortableLazyMap.parseMap(this);
+
+            case GridPortableMarshaller.ENUM:
+                return new PortableBuilderEnum(this);
+
+            case GridPortableMarshaller.ENUM_ARR:
+                return new PortableEnumArrayLazyValue(this);
+
+            case GridPortableMarshaller.MAP_ENTRY:
+                return new PortableLazyMapEntry(this);
+
+            case GridPortableMarshaller.PORTABLE_OBJ: {
+                int size = readInt();
+
+                pos += size;
+
+                int start = readInt();
+
+                PortableObjectImpl portableObj = new PortableObjectImpl(ctx, arr,
+                    pos - 4 - size + start);
+
+                return new PortablePlainPortableObject(portableObj);
+            }
+
+
+            default:
+                throw new PortableException("Invalid flag value: " + type);
+        }
+
+        PortablePlainLazyValue res = new PortablePlainLazyValue(this, valPos, 1 + plainLazyValLen);
+
+        pos += plainLazyValLen;
+
+        return res;
+    }
+
+    /**
+     * @return Array.
+     */
+    public byte[] array() {
+        return arr;
+    }
+
+    /**
+     * @return Position of reader.
+     */
+    public int position() {
+        return pos;
+    }
+
+    /**
+     * @param pos New pos.
+     */
+    public void position(int pos) {
+        this.pos = pos;
+    }
+
+    /**
+     * @param n Number of bytes to skip.
+     */
+    public void skip(int n) {
+        pos += n;
+    }
+
+    /**
+     * @return Reader.
+     */
+    PortableReaderExImpl reader() {
+        return reader;
+    }
+
+    /**
+     *
+     */
+    private class LazyCollection implements PortableLazyValue {
+        /** */
+        private final int valOff;
+
+        /** */
+        private Object col;
+
+        /**
+         * @param valOff Value.
+         */
+        protected LazyCollection(int valOff) {
+            this.valOff = valOff;
+        }
+
+        /**
+         * @return Object.
+         */
+        private Object wrappedCollection() {
+            if (col == null) {
+                position(valOff);
+
+                col = parseValue();
+            }
+
+            return col;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+            ctx.writeValue(writer, wrappedCollection());
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object value() {
+            return PortableUtils.unwrapLazy(wrappedCollection());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializationAware.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializationAware.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializationAware.java
new file mode 100644
index 0000000..dadbcf2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializationAware.java
@@ -0,0 +1,29 @@
+/*
+ * 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.ignite.internal.portable;
+
+/**
+ *
+ */
+interface PortableBuilderSerializationAware {
+    /**
+     * @param writer Writer.
+     * @param ctx Context.
+     */
+    public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializer.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializer.java
new file mode 100644
index 0000000..01edc5e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderSerializer.java
@@ -0,0 +1,210 @@
+/*
+ * 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.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.*;
+import org.apache.ignite.portable.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableBuilderSerializer {
+    /** */
+    private final Map<PortableBuilderImpl, Integer> objToPos = new IdentityHashMap<>();
+
+    /** */
+    private Map<PortableObject, PortableBuilderImpl> portableObjToWrapper;
+
+    /**
+     * @param obj Mutable object.
+     * @param posInResArr Object position in the array.
+     */
+    public void registerObjectWriting(PortableBuilderImpl obj, int posInResArr) {
+        objToPos.put(obj, posInResArr);
+    }
+
+    /**
+     * @param writer Writer.
+     * @param val Value.
+     */
+    public void writeValue(PortableWriterExImpl writer, Object val) {
+        if (val == null) {
+            writer.writeByte(GridPortableMarshaller.NULL);
+
+            return;
+        }
+
+        if (val instanceof PortableBuilderSerializationAware) {
+            ((PortableBuilderSerializationAware)val).writeTo(writer, this);
+
+            return;
+        }
+
+        if (val instanceof PortableObjectEx) {
+            if (portableObjToWrapper == null)
+                portableObjToWrapper = new IdentityHashMap<>();
+
+            PortableBuilderImpl wrapper = portableObjToWrapper.get(val);
+
+            if (wrapper == null) {
+                wrapper = PortableBuilderImpl.wrap((PortableObject)val);
+
+                portableObjToWrapper.put((PortableObject)val, wrapper);
+            }
+
+            val = wrapper;
+        }
+
+        if (val instanceof PortableBuilderImpl) {
+            PortableBuilderImpl obj = (PortableBuilderImpl)val;
+
+            Integer posInResArr = objToPos.get(obj);
+
+            if (posInResArr == null) {
+                objToPos.put(obj, writer.outputStream().position());
+
+                obj.serializeTo(writer.newWriter(obj.typeId()), this);
+            }
+            else {
+                int handle = writer.outputStream().position() - posInResArr;
+
+                writer.writeByte(GridPortableMarshaller.HANDLE);
+                writer.writeInt(handle);
+            }
+
+            return;
+        }
+
+        if (val.getClass().isEnum()) {
+            writer.writeByte(GridPortableMarshaller.ENUM);
+            writer.writeInt(writer.context().typeId(val.getClass().getName()));
+            writer.writeInt(((Enum)val).ordinal());
+
+            return;
+        }
+
+        if (val instanceof Collection) {
+            Collection<?> c = (Collection<?>)val;
+
+            writer.writeByte(GridPortableMarshaller.COL);
+            writer.writeInt(c.size());
+
+            byte colType;
+
+            if (c instanceof GridConcurrentSkipListSet)
+                colType = GridPortableMarshaller.CONC_SKIP_LIST_SET;
+            else
+                colType = writer.context().collectionType(c.getClass());
+
+
+            writer.writeByte(colType);
+
+            for (Object obj : c)
+                writeValue(writer, obj);
+
+            return;
+        }
+
+        if (val instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>)val;
+
+            writer.writeByte(GridPortableMarshaller.MAP);
+            writer.writeInt(map.size());
+
+            writer.writeByte(writer.context().mapType(map.getClass()));
+
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                writeValue(writer, entry.getKey());
+                writeValue(writer, entry.getValue());
+            }
+
+            return;
+        }
+
+        Byte flag = PortableUtils.PLAIN_CLASS_TO_FLAG.get(val.getClass());
+
+        if (flag != null) {
+            PortableUtils.writePlainObject(writer, val);
+
+            return;
+        }
+
+        if (val instanceof Object[]) {
+            int compTypeId = writer.context().typeId(((Object[])val).getClass().getComponentType().getName());
+
+            if (val instanceof PortableBuilderEnum[]) {
+                writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, compTypeId);
+
+                return;
+            }
+
+            if (((Object[])val).getClass().getComponentType().isEnum()) {
+                Enum[] enumArr = (Enum[])val;
+
+                writer.writeByte(GridPortableMarshaller.ENUM_ARR);
+                writer.writeInt(compTypeId);
+                writer.writeInt(enumArr.length);
+
+                for (Enum anEnum : enumArr)
+                    writeValue(writer, anEnum);
+
+                return;
+            }
+
+            writeArray(writer, GridPortableMarshaller.OBJ_ARR, (Object[])val, compTypeId);
+
+            return;
+        }
+
+        writer.doWriteObject(val, false);
+    }
+
+    /**
+     * @param writer Writer.
+     * @param elementType Element type.
+     * @param arr The array.
+     * @param compTypeId Component type ID.
+     */
+    public void writeArray(PortableWriterExImpl writer, byte elementType, Object[] arr, int compTypeId) {
+        writer.writeByte(elementType);
+        writer.writeInt(compTypeId);
+        writer.writeInt(arr.length);
+
+        for (Object obj : arr)
+            writeValue(writer, obj);
+    }
+
+    /**
+     * @param writer Writer.
+     * @param elementType Element type.
+     * @param arr The array.
+     * @param clsName Component class name.
+     */
+    public void writeArray(PortableWriterExImpl writer, byte elementType, Object[] arr, String clsName) {
+        writer.writeByte(elementType);
+        writer.writeInt(GridPortableMarshaller.UNREGISTERED_TYPE_ID);
+        writer.writeString(clsName);
+        writer.writeInt(arr.length);
+
+        for (Object obj : arr)
+            writeValue(writer, obj);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
new file mode 100644
index 0000000..fb2bdc2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
@@ -0,0 +1,1344 @@
+/*
+ * 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.ignite.internal.portable;
+
+import org.apache.ignite.*;
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.math.*;
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+
+import static java.lang.reflect.Modifier.*;
+
+/**
+ * Portable class descriptor.
+ */
+class PortableClassDescriptor {
+    /** */
+    private final PortableContext ctx;
+
+    /** */
+    private final Class<?> cls;
+
+    /** */
+    private final PortableSerializer serializer;
+
+    /** */
+    private final Mode mode;
+
+    /** */
+    private final boolean userType;
+
+    /** */
+    private final int typeId;
+
+    /** */
+    private final String typeName;
+
+    /** */
+    private final Constructor<?> ctor;
+
+    /** */
+    private final Collection<FieldInfo> fields;
+
+    /** */
+    private final Method writeReplaceMtd;
+
+    /** */
+    private final Method readResolveMtd;
+
+    /** */
+    private final boolean useTs;
+
+    /** */
+    private final Map<String, String> fieldsMeta;
+
+    /** */
+    private final boolean keepDeserialized;
+
+    /** */
+    private final boolean registered;
+
+    /** */
+    private final boolean excluded;
+
+    /**
+     * @param ctx Context.
+     * @param cls Class.
+     * @param userType User type flag.
+     * @param typeId Type ID.
+     * @param typeName Type name.
+     * @param idMapper ID mapper.
+     * @param serializer Serializer.
+     * @param useTs Use timestamp flag.
+     * @param metaDataEnabled Metadata enabled flag.
+     * @param keepDeserialized Keep deserialized flag.
+     * @throws PortableException In case of error.
+     */
+    PortableClassDescriptor(
+        PortableContext ctx,
+        Class<?> cls,
+        boolean userType,
+        int typeId,
+        String typeName,
+        @Nullable PortableIdMapper idMapper,
+        @Nullable PortableSerializer serializer,
+        boolean useTs,
+        boolean metaDataEnabled,
+        boolean keepDeserialized
+    ) throws PortableException {
+        this(ctx, cls, userType, typeId, typeName, idMapper, serializer, useTs, metaDataEnabled, keepDeserialized,
+             true);
+    }
+
+    /**
+     * @param ctx Context.
+     * @param cls Class.
+     * @param userType User type flag.
+     * @param typeId Type ID.
+     * @param typeName Type name.
+     * @param idMapper ID mapper.
+     * @param serializer Serializer.
+     * @param useTs Use timestamp flag.
+     * @param metaDataEnabled Metadata enabled flag.
+     * @param keepDeserialized Keep deserialized flag.
+     * @param registered Whether typeId has been successfully registered by MarshallerContext or not.
+     * @throws PortableException In case of error.
+     */
+    PortableClassDescriptor(
+        PortableContext ctx,
+        Class<?> cls,
+        boolean userType,
+        int typeId,
+        String typeName,
+        @Nullable PortableIdMapper idMapper,
+        @Nullable PortableSerializer serializer,
+        boolean useTs,
+        boolean metaDataEnabled,
+        boolean keepDeserialized,
+        boolean registered
+    ) throws PortableException {
+        assert ctx != null;
+        assert cls != null;
+
+        this.ctx = ctx;
+        this.cls = cls;
+        this.userType = userType;
+        this.typeId = typeId;
+        this.typeName = typeName;
+        this.serializer = serializer;
+        this.useTs = useTs;
+        this.keepDeserialized = keepDeserialized;
+        this.registered = registered;
+
+        excluded = MarshallerExclusions.isExcluded(cls);
+
+        if (excluded)
+            mode = Mode.EXCLUSION;
+        else
+            mode = serializer != null ? Mode.PORTABLE : mode(cls);
+
+        switch (mode) {
+            case BYTE:
+            case SHORT:
+            case INT:
+            case LONG:
+            case FLOAT:
+            case DOUBLE:
+            case CHAR:
+            case BOOLEAN:
+            case DECIMAL:
+            case STRING:
+            case UUID:
+            case DATE:
+            case BYTE_ARR:
+            case SHORT_ARR:
+            case INT_ARR:
+            case LONG_ARR:
+            case FLOAT_ARR:
+            case DOUBLE_ARR:
+            case CHAR_ARR:
+            case BOOLEAN_ARR:
+            case DECIMAL_ARR:
+            case STRING_ARR:
+            case UUID_ARR:
+            case DATE_ARR:
+            case OBJ_ARR:
+            case COL:
+            case MAP:
+            case MAP_ENTRY:
+            case PORTABLE_OBJ:
+            case ENUM:
+            case ENUM_ARR:
+            case CLASS:
+            case EXCLUSION:
+                ctor = null;
+                fields = null;
+                fieldsMeta = null;
+
+                break;
+
+            case PORTABLE:
+            case EXTERNALIZABLE:
+                ctor = constructor(cls);
+                fields = null;
+                fieldsMeta = null;
+
+                break;
+
+            case OBJECT:
+                assert idMapper != null;
+
+                ctor = constructor(cls);
+                fields = new ArrayList<>();
+                fieldsMeta = metaDataEnabled ? new HashMap<String, String>() : null;
+
+                Collection<String> names = new HashSet<>();
+                Collection<Integer> ids = new HashSet<>();
+
+                for (Class<?> c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
+                    for (Field f : c.getDeclaredFields()) {
+                        int mod = f.getModifiers();
+
+                        if (!isStatic(mod) && !isTransient(mod)) {
+                            f.setAccessible(true);
+
+                            String name = f.getName();
+
+                            if (!names.add(name))
+                                throw new PortableException("Duplicate field name: " + name);
+
+                            int fieldId = idMapper.fieldId(typeId, name);
+
+                            if (!ids.add(fieldId))
+                                throw new PortableException("Duplicate field ID: " + name);
+
+                            FieldInfo fieldInfo = new FieldInfo(f, fieldId);
+
+                            fields.add(fieldInfo);
+
+                            if (metaDataEnabled)
+                                fieldsMeta.put(name, fieldInfo.fieldMode().typeName());
+                        }
+                    }
+                }
+
+                break;
+
+            default:
+                // Should never happen.
+                throw new PortableException("Invalid mode: " + mode);
+        }
+
+        if (mode == Mode.PORTABLE || mode == Mode.EXTERNALIZABLE || mode == Mode.OBJECT) {
+            readResolveMtd = U.findNonPublicMethod(cls, "readResolve");
+            writeReplaceMtd = U.findNonPublicMethod(cls, "writeReplace");
+        }
+        else {
+            readResolveMtd = null;
+            writeReplaceMtd = null;
+        }
+    }
+
+    /**
+     * @return Described class.
+     */
+    Class<?> describedClass() {
+        return cls;
+    }
+
+    /**
+     * @return Type ID.
+     */
+    int typeId() {
+        return typeId;
+    }
+
+    /**
+     * @return Fields meta data.
+     */
+    Map<String, String> fieldsMeta() {
+        return fieldsMeta;
+    }
+
+    /**
+     * @return Use timestamp flag.
+     */
+    boolean isUseTimestamp() {
+        return useTs;
+    }
+
+    /**
+     * @return Keep deserialized flag.
+     */
+    boolean keepDeserialized() {
+        return keepDeserialized;
+    }
+
+    /**
+     * @return Whether typeId has been successfully registered by MarshallerContext or not.
+     */
+    public boolean isRegistered() {
+        return registered;
+    }
+
+    /**
+     * Checks whether the class values are explicitly excluded from marshalling.
+     *
+     * @return {@code true} if excluded, {@code false} otherwise.
+     */
+    public boolean excluded() {
+        return excluded;
+    }
+
+    /**
+     * @return portableWriteReplace() method
+     */
+    @Nullable Method getWriteReplaceMethod() {
+        return writeReplaceMtd;
+    }
+
+    /**
+     * @return portableReadResolve() method
+     */
+    @Nullable Method getReadResolveMethod() {
+        return readResolveMtd;
+    }
+
+    /**
+     * @param obj Object.
+     * @param writer Writer.
+     * @throws PortableException In case of error.
+     */
+    void write(Object obj, PortableWriterExImpl writer) throws PortableException {
+        assert obj != null;
+        assert writer != null;
+
+        switch (mode) {
+            case BYTE:
+                writer.doWriteByte(GridPortableMarshaller.BYTE);
+                writer.doWriteByte((byte)obj);
+
+                break;
+
+            case SHORT:
+                writer.doWriteByte(GridPortableMarshaller.SHORT);
+                writer.doWriteShort((short)obj);
+
+                break;
+
+            case INT:
+                writer.doWriteByte(GridPortableMarshaller.INT);
+                writer.doWriteInt((int)obj);
+
+                break;
+
+            case LONG:
+                writer.doWriteByte(GridPortableMarshaller.LONG);
+                writer.doWriteLong((long)obj);
+
+                break;
+
+            case FLOAT:
+                writer.doWriteByte(GridPortableMarshaller.FLOAT);
+                writer.doWriteFloat((float)obj);
+
+                break;
+
+            case DOUBLE:
+                writer.doWriteByte(GridPortableMarshaller.DOUBLE);
+                writer.doWriteDouble((double)obj);
+
+                break;
+
+            case CHAR:
+                writer.doWriteByte(GridPortableMarshaller.CHAR);
+                writer.doWriteChar((char)obj);
+
+                break;
+
+            case BOOLEAN:
+                writer.doWriteByte(GridPortableMarshaller.BOOLEAN);
+                writer.doWriteBoolean((boolean)obj);
+
+                break;
+
+            case DECIMAL:
+                writer.doWriteDecimal((BigDecimal) obj);
+
+                break;
+
+            case STRING:
+                writer.doWriteString((String)obj);
+
+                break;
+
+            case UUID:
+                writer.doWriteUuid((UUID)obj);
+
+                break;
+
+            case DATE:
+                if (obj instanceof Timestamp)
+                    writer.doWriteTimestamp((Timestamp)obj);
+                else
+                    writer.doWriteDate((Date)obj);
+
+                break;
+
+            case BYTE_ARR:
+                writer.doWriteByteArray((byte[])obj);
+
+                break;
+
+            case SHORT_ARR:
+                writer.doWriteShortArray((short[])obj);
+
+                break;
+
+            case INT_ARR:
+                writer.doWriteIntArray((int[])obj);
+
+                break;
+
+            case LONG_ARR:
+                writer.doWriteLongArray((long[])obj);
+
+                break;
+
+            case FLOAT_ARR:
+                writer.doWriteFloatArray((float[])obj);
+
+                break;
+
+            case DOUBLE_ARR:
+                writer.doWriteDoubleArray((double[])obj);
+
+                break;
+
+            case CHAR_ARR:
+                writer.doWriteCharArray((char[])obj);
+
+                break;
+
+            case BOOLEAN_ARR:
+                writer.doWriteBooleanArray((boolean[])obj);
+
+                break;
+
+            case DECIMAL_ARR:
+                writer.doWriteDecimalArray((BigDecimal[])obj);
+
+                break;
+
+            case STRING_ARR:
+                writer.doWriteStringArray((String[])obj);
+
+                break;
+
+            case UUID_ARR:
+                writer.doWriteUuidArray((UUID[])obj);
+
+                break;
+
+            case DATE_ARR:
+                writer.doWriteDateArray((Date[])obj);
+
+                break;
+
+            case OBJ_ARR:
+                writer.doWriteObjectArray((Object[])obj);
+
+                break;
+
+            case COL:
+                writer.doWriteCollection((Collection<?>)obj);
+
+                break;
+
+            case MAP:
+                writer.doWriteMap((Map<?, ?>)obj);
+
+                break;
+
+            case MAP_ENTRY:
+                writer.doWriteMapEntry((Map.Entry<?, ?>)obj);
+
+                break;
+
+            case ENUM:
+                writer.doWriteEnum((Enum<?>)obj);
+
+                break;
+
+            case ENUM_ARR:
+                writer.doWriteEnumArray((Object[])obj);
+
+                break;
+
+            case CLASS:
+                writer.doWriteClass((Class)obj);
+
+                break;
+
+            case PORTABLE_OBJ:
+                writer.doWritePortableObject((PortableObjectImpl)obj);
+
+                break;
+
+            case PORTABLE:
+                if (writeHeader(obj, writer)) {
+                    if (serializer != null)
+                        serializer.writePortable(obj, writer);
+                    else
+                        ((PortableMarshalAware)obj).writePortable(writer);
+
+                    writer.writeRawOffsetIfNeeded();
+                    writer.writeLength();
+
+                    if (obj.getClass() != PortableMetaDataImpl.class
+                        && ctx.isMetaDataChanged(typeId, writer.metaDataHashSum())) {
+                        PortableMetaDataCollector metaCollector = new PortableMetaDataCollector(typeName);
+
+                        if (serializer != null)
+                            serializer.writePortable(obj, metaCollector);
+                        else
+                            ((PortableMarshalAware)obj).writePortable(metaCollector);
+
+                        ctx.updateMetaData(typeId, typeName, metaCollector.meta());
+                    }
+                }
+
+                break;
+
+            case EXTERNALIZABLE:
+                if (writeHeader(obj, writer)) {
+                    try {
+                        ((Externalizable)obj).writeExternal(writer);
+                    }
+                    catch (IOException e) {
+                        throw new PortableException("Failed to write Externalizable object: " + obj, e);
+                    }
+
+                    writer.writeLength();
+                }
+
+                break;
+
+            case OBJECT:
+                if (writeHeader(obj, writer)) {
+                    for (FieldInfo info : fields)
+                        info.write(obj, writer);
+
+                    writer.writeRawOffsetIfNeeded();
+                    writer.writeLength();
+                }
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+        }
+    }
+
+    /**
+     * @param reader Reader.
+     * @return Object.
+     * @throws PortableException If failed.
+     */
+    Object read(PortableReaderExImpl reader) throws PortableException {
+        assert reader != null;
+
+        Object res;
+
+        switch (mode) {
+            case PORTABLE:
+                res = newInstance();
+
+                reader.setHandler(res);
+
+                if (serializer != null)
+                    serializer.readPortable(res, reader);
+                else
+                    ((PortableMarshalAware)res).readPortable(reader);
+
+                break;
+
+            case EXTERNALIZABLE:
+                res = newInstance();
+
+                reader.setHandler(res);
+
+                try {
+                    ((Externalizable)res).readExternal(reader);
+                }
+                catch (IOException | ClassNotFoundException e) {
+                    throw new PortableException("Failed to read Externalizable object: " +
+                        res.getClass().getName(), e);
+                }
+
+                break;
+
+            case OBJECT:
+                res = newInstance();
+
+                reader.setHandler(res);
+
+                for (FieldInfo info : fields)
+                    info.read(res, reader);
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+
+                return null;
+        }
+
+        if (readResolveMtd != null) {
+            try {
+                res = readResolveMtd.invoke(res);
+
+                reader.setHandler(res);
+            }
+            catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+            catch (InvocationTargetException e) {
+                if (e.getTargetException() instanceof PortableException)
+                    throw (PortableException)e.getTargetException();
+
+                throw new PortableException("Failed to execute readResolve() method on " + res, e);
+            }
+        }
+
+        return res;
+    }
+
+    /**
+     * @param obj Object.
+     * @param writer Writer.
+     * @return Whether further write is needed.
+     */
+    private boolean writeHeader(Object obj, PortableWriterExImpl writer) {
+        int handle = writer.handle(obj);
+
+        if (handle >= 0) {
+            writer.doWriteByte(GridPortableMarshaller.HANDLE);
+            writer.doWriteInt(handle);
+
+            return false;
+        }
+        else {
+            int pos = writer.position();
+
+            writer.doWriteByte(GridPortableMarshaller.OBJ);
+            writer.doWriteBoolean(userType);
+            writer.doWriteInt(registered ? typeId : GridPortableMarshaller.UNREGISTERED_TYPE_ID);
+            writer.doWriteInt(obj instanceof CacheObjectImpl ? 0 : obj.hashCode());
+
+            // For length and raw offset.
+            int reserved = writer.reserve(8);
+
+            // Class name in case if typeId registration is failed.
+            if (!registered)
+                writer.doWriteString(cls.getName());
+
+            int current = writer.position();
+            int len = current - pos;
+
+            // Default raw offset (equal to header length).
+            writer.position(reserved + 4);
+            writer.doWriteInt(len);
+            writer.position(current);
+
+            return true;
+        }
+    }
+
+    /**
+     * @return Instance.
+     * @throws PortableException In case of error.
+     */
+    private Object newInstance() throws PortableException {
+        assert ctor != null;
+
+        try {
+            return ctor.newInstance();
+        }
+        catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
+            throw new PortableException("Failed to instantiate instance: " + cls, e);
+        }
+    }
+
+    /**
+     * @param cls Class.
+     * @return Constructor.
+     * @throws PortableException If constructor doesn't exist.
+     */
+    @Nullable private static Constructor<?> constructor(Class<?> cls) throws PortableException {
+        assert cls != null;
+
+        try {
+            Constructor<?> ctor = U.forceEmptyConstructor(cls);
+
+            ctor.setAccessible(true);
+
+            return ctor;
+        }
+        catch (IgniteCheckedException e) {
+            throw new PortableException("Failed to get constructor for class: " + cls.getName(), e);
+        }
+    }
+
+    /**
+     * @param cls Class.
+     * @return Mode.
+     */
+    @SuppressWarnings("IfMayBeConditional")
+    private static Mode mode(Class<?> cls) {
+        assert cls != null;
+
+        if (cls == byte.class || cls == Byte.class)
+            return Mode.BYTE;
+        else if (cls == short.class || cls == Short.class)
+            return Mode.SHORT;
+        else if (cls == int.class || cls == Integer.class)
+            return Mode.INT;
+        else if (cls == long.class || cls == Long.class)
+            return Mode.LONG;
+        else if (cls == float.class || cls == Float.class)
+            return Mode.FLOAT;
+        else if (cls == double.class || cls == Double.class)
+            return Mode.DOUBLE;
+        else if (cls == char.class || cls == Character.class)
+            return Mode.CHAR;
+        else if (cls == boolean.class || cls == Boolean.class)
+            return Mode.BOOLEAN;
+        else if (cls == BigDecimal.class)
+            return Mode.DECIMAL;
+        else if (cls == String.class)
+            return Mode.STRING;
+        else if (cls == UUID.class)
+            return Mode.UUID;
+        else if (cls == Timestamp.class || cls == Date.class)
+            return Mode.DATE;
+        else if (cls == byte[].class)
+            return Mode.BYTE_ARR;
+        else if (cls == short[].class)
+            return Mode.SHORT_ARR;
+        else if (cls == int[].class)
+            return Mode.INT_ARR;
+        else if (cls == long[].class)
+            return Mode.LONG_ARR;
+        else if (cls == float[].class)
+            return Mode.FLOAT_ARR;
+        else if (cls == double[].class)
+            return Mode.DOUBLE_ARR;
+        else if (cls == char[].class)
+            return Mode.CHAR_ARR;
+        else if (cls == boolean[].class)
+            return Mode.BOOLEAN_ARR;
+        else if (cls == BigDecimal[].class)
+            return Mode.DECIMAL_ARR;
+        else if (cls == String[].class)
+            return Mode.STRING_ARR;
+        else if (cls == UUID[].class)
+            return Mode.UUID_ARR;
+        else if (cls == Date[].class)
+            return Mode.DATE_ARR;
+        else if (cls.isArray())
+            return cls.getComponentType().isEnum() ? Mode.ENUM_ARR : Mode.OBJ_ARR;
+        else if (cls == PortableObjectImpl.class)
+            return Mode.PORTABLE_OBJ;
+        else if (PortableMarshalAware.class.isAssignableFrom(cls))
+           return Mode.PORTABLE;
+        else if (Externalizable.class.isAssignableFrom(cls))
+            return Mode.EXTERNALIZABLE;
+        else if (Map.Entry.class.isAssignableFrom(cls))
+            return Mode.MAP_ENTRY;
+        else if (Collection.class.isAssignableFrom(cls))
+            return Mode.COL;
+        else if (Map.class.isAssignableFrom(cls))
+            return Mode.MAP;
+        else if (cls == PortableObjectImpl.class)
+            return Mode.PORTABLE_OBJ;
+        else if (cls.isEnum())
+            return Mode.ENUM;
+        else if (cls == Class.class)
+            return Mode.CLASS;
+        else
+            return Mode.OBJECT;
+    }
+
+    /** */
+    private static class FieldInfo {
+        /** */
+        private final Field field;
+
+        /** */
+        private final int id;
+
+        /** */
+        private final Mode mode;
+
+        /**
+         * @param field Field.
+         * @param id Field ID.
+         */
+        private FieldInfo(Field field, int id) {
+            assert field != null;
+
+            this.field = field;
+            this.id = id;
+
+            Class<?> type = field.getType();
+
+            mode = mode(type);
+        }
+
+        /**
+         * @return Field mode.
+         */
+        public Mode fieldMode() {
+            return mode;
+        }
+
+        /**
+         * @param obj Object.
+         * @param writer Writer.
+         * @throws PortableException In case of error.
+         */
+        public void write(Object obj, PortableWriterExImpl writer) throws PortableException {
+            assert obj != null;
+            assert writer != null;
+
+            writer.doWriteInt(id);
+
+            Object val;
+
+            try {
+                val = field.get(obj);
+            }
+            catch (IllegalAccessException e) {
+                throw new PortableException("Failed to get value for field: " + field, e);
+            }
+
+            switch (mode) {
+                case BYTE:
+                    writer.writeByteField((Byte)val);
+
+                    break;
+
+                case SHORT:
+                    writer.writeShortField((Short)val);
+
+                    break;
+
+                case INT:
+                    writer.writeIntField((Integer)val);
+
+                    break;
+
+                case LONG:
+                    writer.writeLongField((Long)val);
+
+                    break;
+
+                case FLOAT:
+                    writer.writeFloatField((Float)val);
+
+                    break;
+
+                case DOUBLE:
+                    writer.writeDoubleField((Double)val);
+
+                    break;
+
+                case CHAR:
+                    writer.writeCharField((Character)val);
+
+                    break;
+
+                case BOOLEAN:
+                    writer.writeBooleanField((Boolean)val);
+
+                    break;
+
+                case DECIMAL:
+                    writer.writeDecimalField((BigDecimal)val);
+
+                    break;
+
+                case STRING:
+                    writer.writeStringField((String)val);
+
+                    break;
+
+                case UUID:
+                    writer.writeUuidField((UUID)val);
+
+                    break;
+
+                case DATE:
+                    if (val instanceof Timestamp)
+                        writer.writeTimestampField((Timestamp)val);
+                    else
+                        writer.writeDateField((Date)val);
+
+                    break;
+
+                case BYTE_ARR:
+                    writer.writeByteArrayField((byte[])val);
+
+                    break;
+
+                case SHORT_ARR:
+                    writer.writeShortArrayField((short[])val);
+
+                    break;
+
+                case INT_ARR:
+                    writer.writeIntArrayField((int[])val);
+
+                    break;
+
+                case LONG_ARR:
+                    writer.writeLongArrayField((long[])val);
+
+                    break;
+
+                case FLOAT_ARR:
+                    writer.writeFloatArrayField((float[])val);
+
+                    break;
+
+                case DOUBLE_ARR:
+                    writer.writeDoubleArrayField((double[])val);
+
+                    break;
+
+                case CHAR_ARR:
+                    writer.writeCharArrayField((char[])val);
+
+                    break;
+
+                case BOOLEAN_ARR:
+                    writer.writeBooleanArrayField((boolean[])val);
+
+                    break;
+
+                case DECIMAL_ARR:
+                    writer.writeDecimalArrayField((BigDecimal[])val);
+
+                    break;
+
+                case STRING_ARR:
+                    writer.writeStringArrayField((String[])val);
+
+                    break;
+
+                case UUID_ARR:
+                    writer.writeUuidArrayField((UUID[])val);
+
+                    break;
+
+                case DATE_ARR:
+                    writer.writeDateArrayField((Date[])val);
+
+                    break;
+
+                case OBJ_ARR:
+                    writer.writeObjectArrayField((Object[])val);
+
+                    break;
+
+                case COL:
+                    writer.writeCollectionField((Collection<?>)val);
+
+                    break;
+
+                case MAP:
+                    writer.writeMapField((Map<?, ?>)val);
+
+                    break;
+
+                case MAP_ENTRY:
+                    writer.writeMapEntryField((Map.Entry<?, ?>)val);
+
+                    break;
+
+                case PORTABLE_OBJ:
+                    writer.writePortableObjectField((PortableObjectImpl)val);
+
+                    break;
+
+                case ENUM:
+                    writer.writeEnumField((Enum<?>)val);
+
+                    break;
+
+                case ENUM_ARR:
+                    writer.writeEnumArrayField((Object[])val);
+
+                    break;
+
+                case PORTABLE:
+                case EXTERNALIZABLE:
+                case OBJECT:
+                    writer.writeObjectField(val);
+
+                    break;
+
+                case CLASS:
+                    writer.writeClassField((Class)val);
+
+                    break;
+
+                default:
+                    assert false : "Invalid mode: " + mode;
+            }
+        }
+
+        /**
+         * @param obj Object.
+         * @param reader Reader.
+         * @throws PortableException In case of error.
+         */
+        public void read(Object obj, PortableReaderExImpl reader) throws PortableException {
+            Object val = null;
+
+            switch (mode) {
+                case BYTE:
+                    val = reader.readByte(id);
+
+                    break;
+
+                case SHORT:
+                    val = reader.readShort(id);
+
+                    break;
+
+                case INT:
+                    val = reader.readInt(id);
+
+                    break;
+
+                case LONG:
+                    val = reader.readLong(id);
+
+                    break;
+
+                case FLOAT:
+                    val = reader.readFloat(id);
+
+                    break;
+
+                case DOUBLE:
+                    val = reader.readDouble(id);
+
+                    break;
+
+                case CHAR:
+                    val = reader.readChar(id);
+
+                    break;
+
+                case BOOLEAN:
+                    val = reader.readBoolean(id);
+
+                    break;
+
+                case DECIMAL:
+                    val = reader.readDecimal(id);
+
+                    break;
+
+                case STRING:
+                    val = reader.readString(id);
+
+                    break;
+
+                case UUID:
+                    val = reader.readUuid(id);
+
+                    break;
+
+                case DATE:
+                    val = field.getType() == Timestamp.class ? reader.readTimestamp(id) : reader.readDate(id);
+
+                    break;
+
+                case BYTE_ARR:
+                    val = reader.readByteArray(id);
+
+                    break;
+
+                case SHORT_ARR:
+                    val = reader.readShortArray(id);
+
+                    break;
+
+                case INT_ARR:
+                    val = reader.readIntArray(id);
+
+                    break;
+
+                case LONG_ARR:
+                    val = reader.readLongArray(id);
+
+                    break;
+
+                case FLOAT_ARR:
+                    val = reader.readFloatArray(id);
+
+                    break;
+
+                case DOUBLE_ARR:
+                    val = reader.readDoubleArray(id);
+
+                    break;
+
+                case CHAR_ARR:
+                    val = reader.readCharArray(id);
+
+                    break;
+
+                case BOOLEAN_ARR:
+                    val = reader.readBooleanArray(id);
+
+                    break;
+
+                case DECIMAL_ARR:
+                    val = reader.readDecimalArray(id);
+
+                    break;
+
+                case STRING_ARR:
+                    val = reader.readStringArray(id);
+
+                    break;
+
+                case UUID_ARR:
+                    val = reader.readUuidArray(id);
+
+                    break;
+
+                case DATE_ARR:
+                    val = reader.readDateArray(id);
+
+                    break;
+
+                case OBJ_ARR:
+                    val = reader.readObjectArray(id);
+
+                    break;
+
+                case COL:
+                    val = reader.readCollection(id, null);
+
+                    break;
+
+                case MAP:
+                    val = reader.readMap(id, null);
+
+                    break;
+
+                case MAP_ENTRY:
+                    val = reader.readMapEntry(id);
+
+                    break;
+
+                case PORTABLE_OBJ:
+                    val = reader.readPortableObject(id);
+
+                    break;
+
+                case ENUM:
+                    val = reader.readEnum(id, field.getType());
+
+                    break;
+
+                case ENUM_ARR:
+                    val = reader.readEnumArray(id, field.getType().getComponentType());
+
+                    break;
+
+                case PORTABLE:
+                case EXTERNALIZABLE:
+                case OBJECT:
+                    val = reader.readObject(id);
+
+                    break;
+
+                case CLASS:
+                    val = reader.readClass(id);
+
+                    break;
+
+                default:
+                    assert false : "Invalid mode: " + mode;
+            }
+
+            try {
+                if (val != null || !field.getType().isPrimitive())
+                    field.set(obj, val);
+            }
+            catch (IllegalAccessException e) {
+                throw new PortableException("Failed to set value for field: " + field, e);
+            }
+        }
+    }
+
+    /** */
+    enum Mode {
+        /** */
+        BYTE("byte"),
+
+        /** */
+        SHORT("short"),
+
+        /** */
+        INT("int"),
+
+        /** */
+        LONG("long"),
+
+        /** */
+        FLOAT("float"),
+
+        /** */
+        DOUBLE("double"),
+
+        /** */
+        CHAR("char"),
+
+        /** */
+        BOOLEAN("boolean"),
+
+        /** */
+        DECIMAL("decimal"),
+
+        /** */
+        STRING("String"),
+
+        /** */
+        UUID("UUID"),
+
+        /** */
+        DATE("Date"),
+
+        /** */
+        BYTE_ARR("byte[]"),
+
+        /** */
+        SHORT_ARR("short[]"),
+
+        /** */
+        INT_ARR("int[]"),
+
+        /** */
+        LONG_ARR("long[]"),
+
+        /** */
+        FLOAT_ARR("float[]"),
+
+        /** */
+        DOUBLE_ARR("double[]"),
+
+        /** */
+        CHAR_ARR("char[]"),
+
+        /** */
+        BOOLEAN_ARR("boolean[]"),
+
+        /** */
+        DECIMAL_ARR("decimal[]"),
+
+        /** */
+        STRING_ARR("String[]"),
+
+        /** */
+        UUID_ARR("UUID[]"),
+
+        /** */
+        DATE_ARR("Date[]"),
+
+        /** */
+        OBJ_ARR("Object[]"),
+
+        /** */
+        COL("Collection"),
+
+        /** */
+        MAP("Map"),
+
+        /** */
+        MAP_ENTRY("Entry"),
+
+        /** */
+        PORTABLE_OBJ("Object"),
+
+        /** */
+        ENUM("Enum"),
+
+        /** */
+        ENUM_ARR("Enum[]"),
+
+        /** */
+        CLASS("Class"),
+
+        /** */
+        PORTABLE("Object"),
+
+        /** */
+        EXTERNALIZABLE("Object"),
+
+        /** */
+        OBJECT("Object"),
+
+        /** */
+        EXCLUSION("Exclusion");
+
+        /** */
+        private final String typeName;
+
+        /**
+         * @param typeName Type name.
+         */
+        Mode(String typeName) {
+            this.typeName = typeName;
+        }
+
+        /**
+         * @return Type name.
+         */
+        String typeName() {
+            return typeName;
+        }
+    }
+}