You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/08/25 09:05:53 UTC

[12/14] 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/PortableContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java
new file mode 100644
index 0000000..83dd01d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java
@@ -0,0 +1,1089 @@
+/*
+ * 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.*;
+import org.apache.ignite.internal.processors.cache.portable.*;
+import org.apache.ignite.internal.util.*;
+import org.apache.ignite.internal.util.lang.*;
+import org.apache.ignite.internal.util.typedef.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.lang.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.marshaller.optimized.*;
+import org.apache.ignite.marshaller.portable.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+import org.jsr166.*;
+
+import java.io.*;
+import java.math.*;
+import java.net.*;
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+import java.util.concurrent.*;
+import java.util.jar.*;
+
+/**
+ * Portable context.
+ */
+public class PortableContext implements Externalizable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    static final PortableIdMapper DFLT_ID_MAPPER = new IdMapperWrapper(null);
+
+    /** */
+    static final PortableIdMapper BASIC_CLS_ID_MAPPER = new BasicClassIdMapper();
+
+    /** */
+    static final char[] LOWER_CASE_CHARS;
+
+    /** */
+    static final char MAX_LOWER_CASE_CHAR = 0x7e;
+
+    /**
+     *
+     */
+    static {
+        LOWER_CASE_CHARS = new char[MAX_LOWER_CASE_CHAR + 1];
+
+        for (char c = 0; c <= MAX_LOWER_CASE_CHAR; c++)
+            LOWER_CASE_CHARS[c] = Character.toLowerCase(c);
+    }
+
+    /** */
+    private final ConcurrentMap<Integer, Collection<Integer>> metaDataCache = new ConcurrentHashMap8<>();
+
+    /** */
+    private final ConcurrentMap<Class<?>, PortableClassDescriptor> descByCls = new ConcurrentHashMap8<>();
+
+    /** */
+    private final ConcurrentMap<Integer, PortableClassDescriptor> userTypes = new ConcurrentHashMap8<>(0);
+
+    /** */
+    private final Map<Integer, PortableClassDescriptor> predefinedTypes = new HashMap<>();
+
+    /** */
+    private final Set<Class> predefinedClasses = new HashSet<>();
+
+    /** */
+    private final Map<Class<? extends Collection>, Byte> colTypes = new HashMap<>();
+
+    /** */
+    private final Map<Class<? extends Map>, Byte> mapTypes = new HashMap<>();
+
+    /** */
+    private final Map<Integer, PortableIdMapper> mappers = new ConcurrentHashMap8<>(0);
+
+    /** */
+    private final Map<String, PortableIdMapper> typeMappers = new ConcurrentHashMap8<>(0);
+
+    /** */
+    private Map<Integer, Boolean> metaEnabled = new HashMap<>(0);
+
+    /** */
+    private Set<Integer> usingTs = new HashSet<>();
+
+    /** */
+    private PortableMetaDataHandler metaHnd;
+
+    /** */
+    private MarshallerContext marshCtx;
+
+    /** */
+    private String gridName;
+
+    /** */
+    private PortableMarshaller marsh;
+
+    /** */
+    private final OptimizedMarshaller optmMarsh = new OptimizedMarshaller();
+
+    /**
+     * For {@link Externalizable}.
+     */
+    public PortableContext() {
+        // No-op.
+    }
+
+    /**
+     * @param metaHnd Meta data handler.
+     * @param gridName Grid name.
+     */
+    public PortableContext(PortableMetaDataHandler metaHnd, @Nullable String gridName) {
+        assert metaHnd != null;
+
+        this.metaHnd = metaHnd;
+        this.gridName = gridName;
+
+        colTypes.put(ArrayList.class, GridPortableMarshaller.ARR_LIST);
+        colTypes.put(LinkedList.class, GridPortableMarshaller.LINKED_LIST);
+        colTypes.put(HashSet.class, GridPortableMarshaller.HASH_SET);
+        colTypes.put(LinkedHashSet.class, GridPortableMarshaller.LINKED_HASH_SET);
+        colTypes.put(TreeSet.class, GridPortableMarshaller.TREE_SET);
+        colTypes.put(ConcurrentSkipListSet.class, GridPortableMarshaller.CONC_SKIP_LIST_SET);
+
+        mapTypes.put(HashMap.class, GridPortableMarshaller.HASH_MAP);
+        mapTypes.put(LinkedHashMap.class, GridPortableMarshaller.LINKED_HASH_MAP);
+        mapTypes.put(TreeMap.class, GridPortableMarshaller.TREE_MAP);
+        mapTypes.put(ConcurrentHashMap.class, GridPortableMarshaller.CONC_HASH_MAP);
+        mapTypes.put(ConcurrentHashMap8.class, GridPortableMarshaller.CONC_HASH_MAP);
+        mapTypes.put(Properties.class, GridPortableMarshaller.PROPERTIES_MAP);
+
+        registerPredefinedType(Byte.class, GridPortableMarshaller.BYTE);
+        registerPredefinedType(Boolean.class, GridPortableMarshaller.BOOLEAN);
+        registerPredefinedType(Short.class, GridPortableMarshaller.SHORT);
+        registerPredefinedType(Character.class, GridPortableMarshaller.CHAR);
+        registerPredefinedType(Integer.class, GridPortableMarshaller.INT);
+        registerPredefinedType(Long.class, GridPortableMarshaller.LONG);
+        registerPredefinedType(Float.class, GridPortableMarshaller.FLOAT);
+        registerPredefinedType(Double.class, GridPortableMarshaller.DOUBLE);
+        registerPredefinedType(String.class, GridPortableMarshaller.STRING);
+        registerPredefinedType(BigDecimal.class, GridPortableMarshaller.DECIMAL);
+        registerPredefinedType(Date.class, GridPortableMarshaller.DATE);
+        registerPredefinedType(UUID.class, GridPortableMarshaller.UUID);
+        // TODO: How to handle timestamp? It has the same ID in .Net.
+        registerPredefinedType(Timestamp.class, GridPortableMarshaller.DATE);
+
+        registerPredefinedType(byte[].class, GridPortableMarshaller.BYTE_ARR);
+        registerPredefinedType(short[].class, GridPortableMarshaller.SHORT_ARR);
+        registerPredefinedType(int[].class, GridPortableMarshaller.INT_ARR);
+        registerPredefinedType(long[].class, GridPortableMarshaller.LONG_ARR);
+        registerPredefinedType(float[].class, GridPortableMarshaller.FLOAT_ARR);
+        registerPredefinedType(double[].class, GridPortableMarshaller.DOUBLE_ARR);
+        registerPredefinedType(char[].class, GridPortableMarshaller.CHAR_ARR);
+        registerPredefinedType(boolean[].class, GridPortableMarshaller.BOOLEAN_ARR);
+        registerPredefinedType(BigDecimal[].class, GridPortableMarshaller.DECIMAL_ARR);
+        registerPredefinedType(String[].class, GridPortableMarshaller.STRING_ARR);
+        registerPredefinedType(UUID[].class, GridPortableMarshaller.UUID_ARR);
+        registerPredefinedType(Date[].class, GridPortableMarshaller.DATE_ARR);
+        registerPredefinedType(Object[].class, GridPortableMarshaller.OBJ_ARR);
+
+        registerPredefinedType(ArrayList.class, 0);
+        registerPredefinedType(LinkedList.class, 0);
+        registerPredefinedType(HashSet.class, 0);
+        registerPredefinedType(LinkedHashSet.class, 0);
+        registerPredefinedType(TreeSet.class, 0);
+        registerPredefinedType(ConcurrentSkipListSet.class, 0);
+
+        registerPredefinedType(HashMap.class, 0);
+        registerPredefinedType(LinkedHashMap.class, 0);
+        registerPredefinedType(TreeMap.class, 0);
+        registerPredefinedType(ConcurrentHashMap.class, 0);
+        registerPredefinedType(ConcurrentHashMap8.class, 0);
+
+        registerPredefinedType(GridMapEntry.class, 60);
+        registerPredefinedType(IgniteBiTuple.class, 61);
+        registerPredefinedType(T2.class, 62);
+
+        registerPredefinedType(PortableObjectImpl.class, 63);
+
+        registerPredefinedType(PortableMetaDataImpl.class, 64);
+
+// TODO: IGNITE-1258
+//        registerPredefinedType(DrSenderAttributes.class, 65);
+//        registerPredefinedType(DrSenderRemoteAttributes.class, 66);
+//
+//        registerPredefinedType(InteropClusterNode.class, 67);
+//        registerPredefinedType(InteropClusterMetrics.class, 68);
+//        registerPredefinedType(InteropTransactionMetrics.class, 69);
+//        registerPredefinedType(InteropMetadata.class, 70);
+//
+//        registerPredefinedType(InteropDotNetConfiguration.class, 71);
+//        registerPredefinedType(InteropDotNetPortableConfiguration.class, 72);
+//        registerPredefinedType(InteropDotNetPortableTypeConfiguration.class, 73);
+//        registerPredefinedType(InteropIgniteProxy.class, 74);
+//        registerPredefinedType(InteropCacheMetrics.class, 75);
+//        registerPredefinedType(InteropProductLicence.class, 78);
+    }
+
+    /**
+     * @param marsh Portable marshaller.
+     * @throws PortableException In case of error.
+     */
+    public void configure(PortableMarshaller marsh) throws PortableException {
+        if (marsh == null)
+            return;
+
+        this.marsh = marsh;
+        marshCtx = marsh.getContext();
+
+        assert marshCtx != null;
+
+        optmMarsh.setContext(marshCtx);
+
+        PortableIdMapper globalIdMapper = marsh.getIdMapper();
+        PortableSerializer globalSerializer = marsh.getSerializer();
+        boolean globalUseTs = marsh.isUseTimestamp();
+        boolean globalMetaDataEnabled = marsh.isMetaDataEnabled();
+        boolean globalKeepDeserialized = marsh.isKeepDeserialized();
+
+        TypeDescriptors descs = new TypeDescriptors();
+
+        if (marsh.getClassNames() != null) {
+            PortableIdMapper idMapper = new IdMapperWrapper(globalIdMapper);
+
+            for (String clsName : marsh.getClassNames()) {
+                if (clsName.endsWith(".*")) { // Package wildcard
+                    String pkgName = clsName.substring(0, clsName.length() - 2);
+
+                    for (String clsName0 : classesInPackage(pkgName))
+                        descs.add(clsName0, idMapper, null, null, globalUseTs, globalMetaDataEnabled,
+                                  globalKeepDeserialized, true);
+                }
+                else // Regular single class
+                    descs.add(clsName, idMapper, null, null, globalUseTs, globalMetaDataEnabled,
+                              globalKeepDeserialized, true);
+            }
+        }
+
+        if (marsh.getTypeConfigurations() != null) {
+            for (PortableTypeConfiguration typeCfg : marsh.getTypeConfigurations()) {
+                String clsName = typeCfg.getClassName();
+
+                if (clsName == null)
+                    throw new PortableException("Class name is required for portable type configuration.");
+
+                PortableIdMapper idMapper = globalIdMapper;
+
+                if (typeCfg.getIdMapper() != null)
+                    idMapper = typeCfg.getIdMapper();
+
+                idMapper = new IdMapperWrapper(idMapper);
+
+                PortableSerializer serializer = globalSerializer;
+
+                if (typeCfg.getSerializer() != null)
+                    serializer = typeCfg.getSerializer();
+
+                boolean useTs = typeCfg.isUseTimestamp() != null ? typeCfg.isUseTimestamp() : globalUseTs;
+                boolean metaDataEnabled = typeCfg.isMetaDataEnabled() != null ? typeCfg.isMetaDataEnabled() :
+                    globalMetaDataEnabled;
+                boolean keepDeserialized = typeCfg.isKeepDeserialized() != null ? typeCfg.isKeepDeserialized() :
+                    globalKeepDeserialized;
+
+                if (clsName.endsWith(".*")) {
+                    String pkgName = clsName.substring(0, clsName.length() - 2);
+
+                    for (String clsName0 : classesInPackage(pkgName))
+                        descs.add(clsName0, idMapper, serializer, typeCfg.getAffinityKeyFieldName(), useTs,
+                                  metaDataEnabled, keepDeserialized, true);
+                }
+                else
+                    descs.add(clsName, idMapper, serializer, typeCfg.getAffinityKeyFieldName(), useTs,
+                              metaDataEnabled, keepDeserialized, false);
+            }
+        }
+
+        for (TypeDescriptor desc : descs.descriptors())
+            registerUserType(desc.clsName, desc.idMapper, desc.serializer, desc.affKeyFieldName, desc.useTs,
+                             desc.metadataEnabled, desc.keepDeserialized);
+    }
+
+    /**
+     * @param pkgName Package name.
+     * @return Class names.
+     */
+    @SuppressWarnings("ConstantConditions")
+    private static Iterable<String> classesInPackage(String pkgName) {
+        assert pkgName != null;
+
+        Collection<String> clsNames = new ArrayList<>();
+
+        ClassLoader ldr = U.gridClassLoader();
+
+        if (ldr instanceof URLClassLoader) {
+            String pkgPath = pkgName.replaceAll("\\.", "/");
+
+            URL[] urls = ((URLClassLoader)ldr).getURLs();
+
+            for (URL url : urls) {
+                String proto = url.getProtocol().toLowerCase();
+
+                if ("file".equals(proto)) {
+                    try {
+                        File cpElement = new File(url.toURI());
+
+                        if (cpElement.isDirectory()) {
+                            File pkgDir = new File(cpElement, pkgPath);
+
+                            if (pkgDir.isDirectory()) {
+                                for (File file : pkgDir.listFiles()) {
+                                    String fileName = file.getName();
+
+                                    if (file.isFile() && fileName.toLowerCase().endsWith(".class"))
+                                        clsNames.add(pkgName + '.' + fileName.substring(0, fileName.length() - 6));
+                                }
+                            }
+                        }
+                        else if (cpElement.isFile()) {
+                            try {
+                                JarFile jar = new JarFile(cpElement);
+
+                                Enumeration<JarEntry> entries = jar.entries();
+
+                                while (entries.hasMoreElements()) {
+                                    String entry = entries.nextElement().getName();
+
+                                    if (entry.startsWith(pkgPath) && entry.endsWith(".class")) {
+                                        String clsName = entry.substring(pkgPath.length() + 1, entry.length() - 6);
+
+                                        if (!clsName.contains("/") && !clsName.contains("\\"))
+                                            clsNames.add(pkgName + '.' + clsName);
+                                    }
+                                }
+                            }
+                            catch (IOException ignored) {
+                                // No-op.
+                            }
+                        }
+                    }
+                    catch (URISyntaxException ignored) {
+                        // No-op.
+                    }
+                }
+            }
+        }
+
+        return clsNames;
+    }
+
+    /**
+     * @param cls Class.
+     * @return Class descriptor.
+     * @throws PortableException In case of error.
+     */
+    public PortableClassDescriptor descriptorForClass(Class<?> cls)
+        throws PortableException {
+        assert cls != null;
+
+        PortableClassDescriptor desc = descByCls.get(cls);
+
+        if (desc == null || !desc.isRegistered())
+            desc = registerClassDescriptor(cls);
+
+        return desc;
+    }
+
+    /**
+     * @param userType User type or not.
+     * @param typeId Type ID.
+     * @param ldr Class loader.
+     * @return Class descriptor.
+     */
+    public PortableClassDescriptor descriptorForTypeId(boolean userType, int typeId, ClassLoader ldr) {
+        assert typeId != GridPortableMarshaller.UNREGISTERED_TYPE_ID;
+
+        PortableClassDescriptor desc = userType ? userTypes.get(typeId) : predefinedTypes.get(typeId);
+
+        if (desc != null)
+            return desc;
+
+        Class cls;
+
+        try {
+            cls = marshCtx.getClass(typeId, ldr);
+
+            desc = descByCls.get(cls);
+        }
+        catch (ClassNotFoundException e) {
+            throw new PortableInvalidClassException(e);
+        }
+        catch (IgniteCheckedException e) {
+            throw new PortableException("Failed resolve class for ID: " + typeId, e);
+        }
+
+        if (desc == null) {
+            desc = registerClassDescriptor(cls);
+
+            assert desc.typeId() == typeId;
+        }
+
+        return desc;
+    }
+
+    /**
+     * Creates and registers {@link PortableClassDescriptor} for the given {@code class}.
+     *
+     * @param cls Class.
+     * @return Class descriptor.
+     */
+    private PortableClassDescriptor registerClassDescriptor(Class<?> cls) {
+        PortableClassDescriptor desc;
+
+        String clsName = cls.getName();
+
+        if (marshCtx.isSystemType(clsName)) {
+            desc = new PortableClassDescriptor(this,
+                cls,
+                false,
+                clsName.hashCode(),
+                clsName,
+                BASIC_CLS_ID_MAPPER,
+                null,
+                marsh.isUseTimestamp(),
+                marsh.isMetaDataEnabled(),
+                marsh.isKeepDeserialized());
+
+            PortableClassDescriptor old = descByCls.putIfAbsent(cls, desc);
+
+            if (old != null)
+                desc = old;
+        }
+        else
+            desc = registerUserClassDescriptor(cls);
+
+        return desc;
+    }
+
+    /**
+     * Creates and registers {@link PortableClassDescriptor} for the given user {@code class}.
+     *
+     * @param cls Class.
+     * @return Class descriptor.
+     */
+    private PortableClassDescriptor registerUserClassDescriptor(Class<?> cls) {
+        PortableClassDescriptor desc;
+
+        boolean registered;
+
+        String typeName = typeName(cls.getName());
+
+        PortableIdMapper idMapper = idMapper(typeName);
+
+        int typeId = idMapper.typeId(typeName);
+
+        try {
+            registered = marshCtx.registerClass(typeId, cls);
+
+        } catch (IgniteCheckedException e) {
+            throw new PortableException("Failed to register class.", e);
+        }
+
+        desc = new PortableClassDescriptor(this,
+            cls,
+            true,
+            typeId,
+            typeName,
+            idMapper,
+            null,
+            marsh.isUseTimestamp(),
+            marsh.isMetaDataEnabled(),
+            marsh.isKeepDeserialized(),
+            registered);
+
+        // perform put() instead of putIfAbsent() because "registered" flag may have been changed.
+        userTypes.put(typeId, desc);
+        descByCls.put(cls, desc);
+
+        return desc;
+    }
+
+    /**
+     * @param cls Collection class.
+     * @return Collection type ID.
+     */
+    public byte collectionType(Class<? extends Collection> cls) {
+        assert cls != null;
+
+        Byte type = colTypes.get(cls);
+
+        if (type != null)
+            return type;
+
+        return Set.class.isAssignableFrom(cls) ? GridPortableMarshaller.USER_SET : GridPortableMarshaller.USER_COL;
+    }
+
+    /**
+     * @param cls Map class.
+     * @return Map type ID.
+     */
+    public byte mapType(Class<? extends Map> cls) {
+        assert cls != null;
+
+        Byte type = mapTypes.get(cls);
+
+        return type != null ? type : GridPortableMarshaller.USER_COL;
+    }
+
+    /**
+     * @param typeName Type name.
+     * @return Type ID.
+     */
+    public int typeId(String typeName) {
+        int id;
+
+        if (marshCtx.isSystemType(typeName))
+            id = typeName.hashCode();
+
+        else {
+            typeName = typeName(typeName);
+
+            id = idMapper(typeName).typeId(typeName);
+        }
+
+        return id;
+    }
+
+    /**
+     * @param cls Class.
+     * @return Type ID.
+     * @throws PortableException In case of error.
+     */
+    public Type typeId(Class cls) throws PortableException {
+        String clsName = cls.getName();
+
+        if (marshCtx.isSystemType(clsName))
+            return new Type(clsName.hashCode(), true);
+
+        if (predefinedClasses.contains(cls))
+            return new Type(DFLT_ID_MAPPER.typeId(typeName(clsName)), true);
+
+        PortableClassDescriptor desc = descByCls.get(cls);
+
+        boolean registered = desc != null && desc.isRegistered();
+
+        if (!registered)
+            // forces to register the class and fill up all required data structures
+            desc = registerUserClassDescriptor(cls);
+
+        return new Type(desc.typeId(), desc.isRegistered());
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @param fieldName Field name.
+     * @return Field ID.
+     */
+    public int fieldId(int typeId, String fieldName) {
+        return idMapper(typeId).fieldId(typeId, fieldName);
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @return Instance of ID mapper.
+     */
+    public PortableIdMapper idMapper(int typeId) {
+        PortableIdMapper idMapper = mappers.get(typeId);
+
+        if (idMapper != null)
+            return idMapper;
+
+        if (userTypes.containsKey(typeId) || predefinedTypes.containsKey(typeId))
+            return DFLT_ID_MAPPER;
+
+        return BASIC_CLS_ID_MAPPER;
+    }
+
+    /**
+     * @param typeName Type name.
+     * @return Instance of ID mapper.
+     */
+    private PortableIdMapper idMapper(String typeName) {
+        PortableIdMapper idMapper = typeMappers.get(typeName);
+
+        return idMapper != null ? idMapper : DFLT_ID_MAPPER;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        U.writeString(out, gridName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        gridName = U.readString(in);
+    }
+
+    /**
+     * @return Portable context.
+     * @throws ObjectStreamException In case of error.
+     */
+    protected Object readResolve() throws ObjectStreamException {
+        try {
+            IgniteKernal g = IgnitionEx.gridx(gridName);
+
+            if (g == null)
+                throw new IllegalStateException("Failed to find grid for name: " + gridName);
+
+            return ((CacheObjectPortableProcessorImpl)g.context().cacheObjects()).portableContext();
+        }
+        catch (IllegalStateException e) {
+            throw U.withCause(new InvalidObjectException(e.getMessage()), e);
+        }
+    }
+
+    /**
+     * @param cls Class.
+     * @param id Type ID.
+     * @return PortableClassDescriptor.
+     */
+    private PortableClassDescriptor registerPredefinedType(Class<?> cls, int id) {
+        PortableClassDescriptor desc = new PortableClassDescriptor(
+            this,
+            cls,
+            false,
+            id,
+            typeName(cls.getName()),
+            DFLT_ID_MAPPER,
+            null,
+            false,
+            false,
+            false
+        );
+
+        predefinedClasses.add(cls);
+
+        predefinedTypes.put(id, desc);
+        descByCls.put(cls, desc);
+
+        return desc;
+    }
+
+    /**
+     * @param clsName Class name.
+     * @param idMapper ID mapper.
+     * @param serializer Serializer.
+     * @param affKeyFieldName Affinity key field name.
+     * @param useTs Use timestamp flag.
+     * @param metaDataEnabled Metadata enabled flag.
+     * @param keepDeserialized Keep deserialized flag.
+     * @throws PortableException In case of error.
+     */
+    @SuppressWarnings("ErrorNotRethrown")
+    public void registerUserType(String clsName,
+        PortableIdMapper idMapper,
+        @Nullable PortableSerializer serializer,
+        @Nullable String affKeyFieldName,
+        boolean useTs,
+        boolean metaDataEnabled,
+        boolean keepDeserialized)
+        throws PortableException {
+        assert idMapper != null;
+
+        Class<?> cls = null;
+
+        try {
+            cls = Class.forName(clsName);
+        }
+        catch (ClassNotFoundException | NoClassDefFoundError ignored) {
+            // No-op.
+        }
+
+        int id = idMapper.typeId(clsName);
+
+        if (mappers.put(id, idMapper) != null)
+            throw new PortableException("Duplicate type ID [clsName=" + clsName + ", id=" + id + ']');
+
+        if (useTs)
+            usingTs.add(id);
+
+        String typeName = typeName(clsName);
+
+        typeMappers.put(typeName, idMapper);
+
+        metaEnabled.put(id, metaDataEnabled);
+
+        Map<String, String> fieldsMeta = null;
+
+        if (cls != null) {
+            PortableClassDescriptor desc = new PortableClassDescriptor(
+                this,
+                cls,
+                true,
+                id,
+                typeName,
+                idMapper,
+                serializer,
+                useTs,
+                metaDataEnabled,
+                keepDeserialized);
+
+            fieldsMeta = desc.fieldsMeta();
+
+            userTypes.put(id, desc);
+            descByCls.put(cls, desc);
+        }
+
+        metaHnd.addMeta(id, new PortableMetaDataImpl(typeName, fieldsMeta, affKeyFieldName));
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @return Meta data.
+     * @throws PortableException In case of error.
+     */
+    @Nullable public PortableMetadata metaData(int typeId) throws PortableException {
+        return metaHnd != null ? metaHnd.metadata(typeId) : null;
+    }
+
+    /**
+     * @return Whether meta data is globally enabled.
+     */
+    boolean isMetaDataEnabled() {
+        return marsh.isMetaDataEnabled();
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @return Whether meta data is enabled.
+     */
+    boolean isMetaDataEnabled(int typeId) {
+        Boolean enabled = metaEnabled.get(typeId);
+
+        return enabled != null ? enabled : true;
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @param metaHashSum Meta data hash sum.
+     * @return Whether meta is changed.
+     */
+    boolean isMetaDataChanged(int typeId, @Nullable Integer metaHashSum) {
+        if (metaHashSum == null)
+            return false;
+
+        Collection<Integer> hist = metaDataCache.get(typeId);
+
+        if (hist == null) {
+            Collection<Integer> old = metaDataCache.putIfAbsent(typeId, hist = new GridConcurrentHashSet<>());
+
+            if (old != null)
+                hist = old;
+        }
+
+        return hist.add(metaHashSum);
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @param typeName Type name.
+     * @param fields Fields map.
+     * @throws PortableException In case of error.
+     */
+    void updateMetaData(int typeId, String typeName, Map<String, String> fields) throws PortableException {
+        updateMetaData(typeId, new PortableMetaDataImpl(typeName, fields, null));
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @param meta Meta data.
+     * @throws PortableException In case of error.
+     */
+    public void updateMetaData(int typeId, PortableMetaDataImpl meta) throws PortableException {
+        metaHnd.addMeta(typeId, meta);
+    }
+
+    /**
+     * @return Use timestamp flag.
+     */
+    public boolean isUseTimestamp() {
+        return marsh.isUseTimestamp();
+    }
+
+    /**
+     * @param typeId Type ID.
+     * @return If timestamp used.
+     */
+    public boolean isUseTimestamp(int typeId) {
+        return usingTs.contains(typeId);
+    }
+
+    /**
+     * @return Whether to convert string to UTF8 bytes.
+     */
+    public boolean isConvertString() {
+        return marsh.isConvertStringToBytes();
+    }
+
+    /**
+     * Returns whether {@code cls} is predefined in the context or not.
+     *
+     * @param cls Class.
+     * @return {@code true} if predefined, {@code false} otherwise.
+     */
+    public boolean isPredefinedClass(Class<?> cls) {
+        return predefinedClasses.contains(cls);
+    }
+
+    /**
+     * Returns instance of {@link OptimizedMarshaller}.
+     *
+     * @return Optimized marshaller.
+     */
+    OptimizedMarshaller optimizedMarsh() {
+        return optmMarsh;
+    }
+
+    /**
+     * @param clsName Class name.
+     * @return Type name.
+     */
+    public static String typeName(String clsName) {
+        assert clsName != null;
+
+        int idx = clsName.lastIndexOf('$');
+
+        String typeName;
+
+        if (idx >= 0) {
+            typeName = clsName.substring(idx + 1);
+
+            try {
+                Integer.parseInt(typeName);
+
+                // This is an anonymous class. Don't cut off enclosing class name for it.
+                idx = -1;
+            }
+            catch (NumberFormatException e) {
+                return typeName;
+            }
+        }
+
+        if (idx < 0)
+            idx = clsName.lastIndexOf('.');
+
+        return idx >= 0 ? clsName.substring(idx + 1) : clsName;
+    }
+
+    /**
+     * @param str String.
+     * @return Hash code for given string converted to lower case.
+     */
+    private static int lowerCaseHashCode(String str) {
+        int len = str.length();
+
+        int h = 0;
+
+        for (int i = 0; i < len; i++) {
+            int c = str.charAt(i);
+
+            c = c <= MAX_LOWER_CASE_CHAR ? LOWER_CASE_CHARS[c] : Character.toLowerCase(c);
+
+            h = 31 * h + c;
+        }
+
+        return h;
+    }
+
+    /**
+     */
+    private static class IdMapperWrapper implements PortableIdMapper {
+        /** */
+        private final PortableIdMapper mapper;
+
+        /**
+         * @param mapper Custom ID mapper.
+         */
+        private IdMapperWrapper(@Nullable PortableIdMapper mapper) {
+            this.mapper = mapper;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int typeId(String clsName) {
+            int id = 0;
+
+            if (mapper != null)
+                id = mapper.typeId(clsName);
+
+            return id != 0 ? id : lowerCaseHashCode(typeName(clsName));
+        }
+
+        /** {@inheritDoc} */
+        @Override public int fieldId(int typeId, String fieldName) {
+            int id = 0;
+
+            if (mapper != null)
+                id = mapper.fieldId(typeId, fieldName);
+
+            return id != 0 ? id : lowerCaseHashCode(fieldName);
+        }
+    }
+
+    private static class BasicClassIdMapper implements PortableIdMapper {
+        /** {@inheritDoc} */
+        @Override public int typeId(String clsName) {
+            return clsName.hashCode();
+        }
+
+        /** {@inheritDoc} */
+        @Override public int fieldId(int typeId, String fieldName) {
+            return lowerCaseHashCode(fieldName);
+        }
+    }
+    /**
+     * Type descriptors.
+     */
+    private static class TypeDescriptors {
+        /** Descriptors map. */
+        private final Map<String, TypeDescriptor> descs = new HashMap<>();
+
+        /**
+         * Add type descriptor.
+         *
+         * @param clsName Class name.
+         * @param idMapper ID mapper.
+         * @param serializer Serializer.
+         * @param affKeyFieldName Affinity key field name.
+         * @param useTs Use timestamp flag.
+         * @param metadataEnabled Metadata enabled flag.
+         * @param keepDeserialized Keep deserialized flag.
+         * @param canOverride Whether this descriptor can be override.
+         * @throws PortableException If failed.
+         */
+        private void add(String clsName,
+            PortableIdMapper idMapper,
+            PortableSerializer serializer,
+            String affKeyFieldName,
+            boolean useTs,
+            boolean metadataEnabled,
+            boolean keepDeserialized,
+            boolean canOverride)
+            throws PortableException {
+            TypeDescriptor desc = new TypeDescriptor(clsName,
+                idMapper,
+                serializer,
+                affKeyFieldName,
+                useTs,
+                metadataEnabled,
+                keepDeserialized,
+                canOverride);
+
+            TypeDescriptor oldDesc = descs.get(clsName);
+
+            if (oldDesc == null)
+                descs.put(clsName, desc);
+            else
+                oldDesc.override(desc);
+        }
+
+        /**
+         * Get all collected descriptors.
+         *
+         * @return Descriptors.
+         */
+        private Iterable<TypeDescriptor> descriptors() {
+            return descs.values();
+        }
+    }
+
+    /**
+     * Type descriptor.
+     */
+    private static class TypeDescriptor {
+        /** Class name. */
+        private final String clsName;
+
+        /** ID mapper. */
+        private PortableIdMapper idMapper;
+
+        /** Serializer. */
+        private PortableSerializer serializer;
+
+        /** Affinity key field name. */
+        private String affKeyFieldName;
+
+        /** Use timestamp flag. */
+        private boolean useTs;
+
+        /** Metadata enabled flag. */
+        private boolean metadataEnabled;
+
+        /** Keep deserialized flag. */
+        private boolean keepDeserialized;
+
+        /** Whether this descriptor can be override. */
+        private boolean canOverride;
+
+        /**
+         * Constructor.
+         *
+         * @param clsName Class name.
+         * @param idMapper ID mapper.
+         * @param serializer Serializer.
+         * @param affKeyFieldName Affinity key field name.
+         * @param useTs Use timestamp flag.
+         * @param metadataEnabled Metadata enabled flag.
+         * @param keepDeserialized Keep deserialized flag.
+         * @param canOverride Whether this descriptor can be override.
+         */
+        private TypeDescriptor(String clsName, PortableIdMapper idMapper, PortableSerializer serializer,
+            String affKeyFieldName, boolean useTs, boolean metadataEnabled, boolean keepDeserialized,
+            boolean canOverride) {
+            this.clsName = clsName;
+            this.idMapper = idMapper;
+            this.serializer = serializer;
+            this.affKeyFieldName = affKeyFieldName;
+            this.useTs = useTs;
+            this.metadataEnabled = metadataEnabled;
+            this.keepDeserialized = keepDeserialized;
+            this.canOverride = canOverride;
+        }
+
+        /**
+         * Override portable class descriptor.
+         *
+         * @param other Other descriptor.
+         * @throws PortableException If failed.
+         */
+        private void override(TypeDescriptor other) throws PortableException {
+            assert clsName.equals(other.clsName);
+
+            if (canOverride) {
+                idMapper = other.idMapper;
+                serializer = other.serializer;
+                affKeyFieldName = other.affKeyFieldName;
+                useTs = other.useTs;
+                metadataEnabled = other.metadataEnabled;
+                keepDeserialized = other.keepDeserialized;
+                canOverride = other.canOverride;
+            }
+            else if (!other.canOverride)
+                throw new PortableException("Duplicate explicit class definition in configuration: " + clsName);
+        }
+    }
+
+    /**
+     * Type id wrapper.
+     */
+    static class Type {
+        /** Type id*/
+        private int id;
+
+        /** Whether the following type is registered in a cache or not */
+        private boolean registered;
+
+        public Type(int id, boolean registered) {
+            this.id = id;
+            this.registered = registered;
+        }
+
+        public int id() {
+            return id;
+        }
+
+        public boolean registered() {
+            return registered;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
new file mode 100644
index 0000000..7fa04e8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
@@ -0,0 +1,111 @@
+/*
+ * 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.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+/**
+ *
+ */
+class PortableEnumArrayLazyValue extends PortableAbstractLazyValue {
+    /** */
+    private final int len;
+
+    /** */
+    private final int compTypeId;
+
+    /** */
+    private final String clsName;
+
+    /**
+     * @param reader Reader.
+     */
+    protected PortableEnumArrayLazyValue(PortableBuilderReader reader) {
+        super(reader, reader.position() - 1);
+
+        int typeId = reader.readInt();
+
+        if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+            clsName = reader.readString();
+
+            Class cls;
+
+            try {
+                // TODO: IGNITE-1272 - Is class loader needed here?
+                cls = U.forName(reader.readString(), null);
+            }
+            catch (ClassNotFoundException e) {
+                throw new PortableInvalidClassException("Failed to load the class: " + clsName, e);
+            }
+
+            compTypeId = reader.portableContext().descriptorForClass(cls).typeId();
+        }
+        else {
+            compTypeId = typeId;
+            clsName = null;
+        }
+
+        int size = reader.readInt();
+
+        for (int i = 0; i < size; i++)
+            reader.skipValue();
+
+        len = reader.position() - valOff;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Object init() {
+        reader.position(valOff + 1);
+
+        //skipping component type id
+        reader.readInt();
+
+        int size = reader.readInt();
+
+        PortableBuilderEnum[] res = new PortableBuilderEnum[size];
+
+        for (int i = 0; i < size; i++) {
+            byte flag = reader.readByte();
+
+            if (flag == GridPortableMarshaller.NULL)
+                continue;
+
+            if (flag != GridPortableMarshaller.ENUM)
+                throw new PortableException("Invalid flag value: " + flag);
+
+            res[i] = new PortableBuilderEnum(reader);
+        }
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (val != null) {
+            if (clsName != null)
+                ctx.writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, clsName);
+            else
+                ctx.writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, compTypeId);
+
+            return;
+        }
+
+        writer.write(reader.array(), valOff, len);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
new file mode 100644
index 0000000..56dfba9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
@@ -0,0 +1,156 @@
+/*
+ * 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 java.util.*;
+
+/**
+ *
+ */
+class PortableLazyArrayList extends AbstractList<Object> implements PortableBuilderSerializationAware {
+    /** */
+    private final PortableBuilderReader reader;
+
+    /** */
+    private final int off;
+
+    /** */
+    private List<Object> delegate;
+
+    /**
+     * @param reader Reader.
+     * @param size Size,
+     */
+    PortableLazyArrayList(PortableBuilderReader reader, int size) {
+        this.reader = reader;
+        off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+        assert size >= 0;
+
+        for (int i = 0; i < size; i++)
+            reader.skipValue();
+    }
+
+    /**
+     *
+     */
+    private void ensureDelegateInit() {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+            delegate = new ArrayList<>(size);
+
+            for (int i = 0; i < size; i++)
+                delegate.add(reader.parseValue());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object get(int idx) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.get(idx));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean add(Object o) {
+        ensureDelegateInit();
+
+        return delegate.add(o);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void add(int idx, Object element) {
+        ensureDelegateInit();
+
+        delegate.add(idx, element);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object set(int idx, Object element) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.set(idx, element));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object remove(int idx) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.remove(idx));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        if (delegate == null)
+            delegate = new ArrayList<>();
+        else
+            delegate.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean addAll(int idx, Collection<?> c) {
+        return delegate.addAll(idx, c);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void removeRange(int fromIdx, int toIdx) {
+        ensureDelegateInit();
+
+        delegate.subList(fromIdx, toIdx).clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        if (delegate == null)
+            return reader.readIntAbsolute(off + 1);
+
+        return delegate.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+
+            writer.write(reader.array(), off, hdrSize);
+
+            reader.position(off + hdrSize);
+
+            for (int i = 0; i < size; i++) {
+                Object o = reader.parseValue();
+
+                ctx.writeValue(writer, o);
+            }
+        }
+        else {
+            writer.writeByte(GridPortableMarshaller.COL);
+            writer.writeInt(delegate.size());
+
+            byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+            writer.writeByte(colType);
+
+            for (Object o : delegate)
+                ctx.writeValue(writer, o);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java
new file mode 100644
index 0000000..9395aeb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.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 java.util.*;
+
+/**
+ *
+ */
+class PortableLazyLinkedList extends AbstractList<Object> implements PortableBuilderSerializationAware {
+    /** */
+    private final PortableBuilderReader reader;
+
+    /** */
+    private final int off;
+
+    /** */
+    private List<Object> delegate;
+
+    /**
+     * @param reader Reader.
+     * @param size Size,
+     */
+    PortableLazyLinkedList(PortableBuilderReader reader, int size) {
+        this.reader = reader;
+        off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+        assert size >= 0;
+
+        for (int i = 0; i < size; i++)
+            reader.skipValue();
+    }
+
+    /**
+     *
+     */
+    private void ensureDelegateInit() {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+            delegate = new LinkedList<>();
+
+            for (int i = 0; i < size; i++)
+                delegate.add(reader.parseValue());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object get(int idx) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.get(idx));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean add(Object o) {
+        ensureDelegateInit();
+
+        return delegate.add(o);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void add(int idx, Object element) {
+        ensureDelegateInit();
+
+        delegate.add(idx, element);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object set(int idx, Object element) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.set(idx, element));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object remove(int idx) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.remove(idx));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        if (delegate == null)
+            delegate = new LinkedList<>();
+        else
+            delegate.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean addAll(int idx, Collection<?> c) {
+        ensureDelegateInit();
+
+        return delegate.addAll(idx, c);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void removeRange(int fromIdx, int toIdx) {
+        ensureDelegateInit();
+
+        delegate.subList(fromIdx, toIdx).clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        if (delegate == null)
+            return reader.readIntAbsolute(off + 1);
+
+        return delegate.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public ListIterator<Object> listIterator(final int idx) {
+        ensureDelegateInit();
+
+        return new ListIterator<Object>() {
+            /** */
+            private final ListIterator<Object> delegate = PortableLazyLinkedList.super.listIterator(idx);
+
+            @Override public boolean hasNext() {
+                return delegate.hasNext();
+            }
+
+            @Override public Object next() {
+                return PortableUtils.unwrapLazy(delegate.next());
+            }
+
+            @Override public boolean hasPrevious() {
+                return delegate.hasPrevious();
+            }
+
+            @Override public Object previous() {
+                return PortableUtils.unwrapLazy(delegate.previous());
+            }
+
+            @Override public int nextIndex() {
+                return delegate.nextIndex();
+            }
+
+            @Override public int previousIndex() {
+                return delegate.previousIndex();
+            }
+
+            @Override public void remove() {
+                delegate.remove();
+            }
+
+            @Override public void set(Object o) {
+                delegate.set(o);
+            }
+
+            @Override public void add(Object o) {
+                delegate.add(o);
+            }
+        };
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator<Object> iterator() {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazyIterator(super.iterator());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+            writer.write(reader.array(), off, hdrSize);
+
+            reader.position(off + hdrSize);
+
+            for (int i = 0; i < size; i++) {
+                Object o = reader.parseValue();
+
+                ctx.writeValue(writer, o);
+            }
+        }
+        else {
+            writer.writeByte(GridPortableMarshaller.COL);
+            writer.writeInt(delegate.size());
+
+            byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+            writer.writeByte(colType);
+
+            for (Object o : delegate)
+                ctx.writeValue(writer, o);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
new file mode 100644
index 0000000..e7f7727
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
@@ -0,0 +1,214 @@
+/*
+ * 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.jetbrains.annotations.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazyMap extends AbstractMap<Object, Object> implements PortableBuilderSerializationAware {
+    /** */
+    private final PortableBuilderReader reader;
+
+    /** */
+    private final int off;
+
+    /** */
+    private Map<Object, Object> delegate;
+
+    /**
+     * @param reader Reader.
+     * @param off Offset.
+     */
+    private PortableLazyMap(PortableBuilderReader reader, int off) {
+        this.reader = reader;
+        this.off = off;
+    }
+
+    /**
+     * @param reader Reader.
+     * @return PortableLazyMap.
+     */
+    @Nullable public static PortableLazyMap parseMap(PortableBuilderReader reader) {
+        int off = reader.position() - 1;
+
+        int size = reader.readInt();
+
+        reader.skip(1); // map type.
+
+        for (int i = 0; i < size; i++) {
+            reader.skipValue(); // skip key
+            reader.skipValue(); // skip value
+        }
+
+        return new PortableLazyMap(reader, off);
+    }
+
+    /**
+     *
+     */
+    private void ensureDelegateInit() {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+            delegate = new LinkedHashMap<>();
+
+            for (int i = 0; i < size; i++)
+                delegate.put(PortableUtils.unwrapLazy(reader.parseValue()), reader.parseValue());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (delegate == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+            writer.write(reader.array(), off, hdrSize);
+
+            reader.position(off + hdrSize);
+
+            for (int i = 0; i < size; i++) {
+                ctx.writeValue(writer, reader.parseValue()); // key
+                ctx.writeValue(writer, reader.parseValue()); // value
+            }
+        }
+        else {
+            writer.writeByte(GridPortableMarshaller.MAP);
+            writer.writeInt(delegate.size());
+
+            byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+
+            writer.writeByte(colType);
+
+            for (Entry<Object, Object> entry : delegate.entrySet()) {
+                ctx.writeValue(writer, entry.getKey());
+                ctx.writeValue(writer, entry.getValue());
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        if (delegate == null)
+            return reader.readIntAbsolute(off + 1);
+
+        return delegate.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(Object key) {
+        ensureDelegateInit();
+
+        return delegate.containsKey(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsValue(Object val) {
+        return values().contains(val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set<Object> keySet() {
+        ensureDelegateInit();
+
+        return delegate.keySet();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        if (delegate == null)
+            delegate = new LinkedHashMap<>();
+        else
+            delegate.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object get(Object key) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.get(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object put(Object key, Object val) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.put(key, val));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object remove(Object key) {
+        ensureDelegateInit();
+
+        return PortableUtils.unwrapLazy(delegate.remove(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set<Entry<Object, Object>> entrySet() {
+        ensureDelegateInit();
+
+        return new AbstractSet<Entry<Object, Object>>() {
+            @Override public boolean contains(Object o) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override public Iterator<Entry<Object, Object>> iterator() {
+                return new Iterator<Entry<Object, Object>>() {
+                    /** */
+                    private final Iterator<Entry<Object, Object>> itr = delegate.entrySet().iterator();
+
+                    @Override public boolean hasNext() {
+                        return itr.hasNext();
+                    }
+
+                    @Override public Entry<Object, Object> next() {
+                        Entry<Object, Object> res = itr.next();
+
+                        final Object val = res.getValue();
+
+                        if (val instanceof PortableLazyValue) {
+                            return new SimpleEntry<Object, Object>(res.getKey(), val) {
+                                private static final long serialVersionUID = 0L;
+
+                                @Override public Object getValue() {
+                                    return ((PortableLazyValue)val).value();
+                                }
+                            };
+                        }
+
+                        return res;
+                    }
+
+                    @Override public void remove() {
+                        itr.remove();
+                    }
+                };
+            }
+
+            @Override public int size() {
+                return delegate.size();
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
new file mode 100644
index 0000000..389ab33
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
@@ -0,0 +1,66 @@
+/*
+ * 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 java.util.*;
+
+/**
+ *
+ */
+class PortableLazyMapEntry implements Map.Entry<Object, Object>, PortableBuilderSerializationAware {
+    /** */
+    private final Object key;
+
+    /** */
+    private Object val;
+
+    /**
+     * @param reader GridMutablePortableReader
+     */
+    PortableLazyMapEntry(PortableBuilderReader reader) {
+        key = reader.parseValue();
+        val = reader.parseValue();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getKey() {
+        return PortableUtils.unwrapLazy(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getValue() {
+        return PortableUtils.unwrapLazy(val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object setValue(Object val) {
+        Object res = getValue();
+
+        this.val = val;
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        writer.writeByte(GridPortableMarshaller.MAP_ENTRY);
+
+        ctx.writeValue(writer, key);
+        ctx.writeValue(writer, val);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
new file mode 100644
index 0000000..d5a59a0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
@@ -0,0 +1,89 @@
+/*
+ * 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.typedef.internal.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazySet extends PortableAbstractLazyValue {
+    /** */
+    private final int off;
+
+    /**
+     * @param reader Reader.
+     * @param size Size.
+     */
+    PortableLazySet(PortableBuilderReader reader, int size) {
+        super(reader, reader.position() - 1);
+
+        off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+        assert size >= 0;
+
+        for (int i = 0; i < size; i++)
+            reader.skipValue();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (val == null) {
+            int size = reader.readIntAbsolute(off + 1);
+
+            int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+            writer.write(reader.array(), off, hdrSize);
+
+            reader.position(off + hdrSize);
+
+            for (int i = 0; i < size; i++) {
+                Object o = reader.parseValue();
+
+                ctx.writeValue(writer, o);
+            }
+        }
+        else {
+            Collection<Object> c = (Collection<Object>)val;
+
+            writer.writeByte(GridPortableMarshaller.COL);
+            writer.writeInt(c.size());
+
+            byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+            writer.writeByte(colType);
+
+            for (Object o : c)
+                ctx.writeValue(writer, o);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Object init() {
+        int size = reader.readIntAbsolute(off + 1);
+
+        reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+        Set<Object> res = U.newLinkedHashSet(size);
+
+        for (int i = 0; i < size; i++)
+            res.add(PortableUtils.unwrapLazy(reader.parseValue()));
+
+        return res;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
new file mode 100644
index 0000000..fcce4a5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
@@ -0,0 +1,28 @@
+/*
+ * 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 PortableLazyValue extends PortableBuilderSerializationAware {
+    /**
+     * @return Value.
+     */
+    public Object value();
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
new file mode 100644
index 0000000..30978ad
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
@@ -0,0 +1,253 @@
+/*
+ * 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 org.jetbrains.annotations.*;
+
+import java.lang.reflect.*;
+import java.math.*;
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+
+/**
+ * Writer for meta data collection.
+ */
+class PortableMetaDataCollector implements PortableWriter {
+    /** */
+    private final Map<String, String> meta = new HashMap<>();
+
+    /** */
+    private final String typeName;
+
+    /**
+     * @param typeName Type name.
+     */
+    PortableMetaDataCollector(String typeName) {
+        this.typeName = typeName;
+    }
+
+    /**
+     * @return Field meta data.
+     */
+    Map<String, String> meta() {
+        return meta;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeByte(String fieldName, byte val) throws PortableException {
+        add(fieldName, byte.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeShort(String fieldName, short val) throws PortableException {
+        add(fieldName, short.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeInt(String fieldName, int val) throws PortableException {
+        add(fieldName, int.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeLong(String fieldName, long val) throws PortableException {
+        add(fieldName, long.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeFloat(String fieldName, float val) throws PortableException {
+        add(fieldName, float.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDouble(String fieldName, double val) throws PortableException {
+        add(fieldName, double.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeChar(String fieldName, char val) throws PortableException {
+        add(fieldName, char.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBoolean(String fieldName, boolean val) throws PortableException {
+        add(fieldName, boolean.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDecimal(String fieldName, @Nullable BigDecimal val) throws PortableException {
+        add(fieldName, PortableClassDescriptor.Mode.DECIMAL.typeName());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeString(String fieldName, @Nullable String val) throws PortableException {
+        add(fieldName, String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeUuid(String fieldName, @Nullable UUID val) throws PortableException {
+        add(fieldName, UUID.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDate(String fieldName, @Nullable Date val) throws PortableException {
+        add(fieldName, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTimestamp(String fieldName, @Nullable Timestamp val) throws PortableException {
+        add(fieldName, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T extends Enum<?>> void writeEnum(String fieldName, T val) throws PortableException {
+        add(fieldName, Enum.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T extends Enum<?>> void writeEnumArray(String fieldName, T[] val) throws PortableException {
+        add(fieldName, Enum[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeObject(String fieldName, @Nullable Object obj) throws PortableException {
+        add(fieldName, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeByteArray(String fieldName, @Nullable byte[] val) throws PortableException {
+        add(fieldName, byte[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeShortArray(String fieldName, @Nullable short[] val) throws PortableException {
+        add(fieldName, short[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeIntArray(String fieldName, @Nullable int[] val) throws PortableException {
+        add(fieldName, int[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeLongArray(String fieldName, @Nullable long[] val) throws PortableException {
+        add(fieldName, long[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeFloatArray(String fieldName, @Nullable float[] val) throws PortableException {
+        add(fieldName, float[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDoubleArray(String fieldName, @Nullable double[] val) throws PortableException {
+        add(fieldName, double[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeCharArray(String fieldName, @Nullable char[] val) throws PortableException {
+        add(fieldName, char[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBooleanArray(String fieldName, @Nullable boolean[] val) throws PortableException {
+        add(fieldName, boolean[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDecimalArray(String fieldName, @Nullable BigDecimal[] val) throws PortableException {
+        add(fieldName, PortableClassDescriptor.Mode.DECIMAL_ARR.typeName());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeStringArray(String fieldName, @Nullable String[] val) throws PortableException {
+        add(fieldName, String[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeUuidArray(String fieldName, @Nullable UUID[] val) throws PortableException {
+        add(fieldName, UUID[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDateArray(String fieldName, @Nullable Date[] val) throws PortableException {
+        add(fieldName, Date[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeObjectArray(String fieldName, @Nullable Object[] val) throws PortableException {
+        add(fieldName, Object[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> void writeCollection(String fieldName, @Nullable Collection<T> col)
+        throws PortableException {
+        add(fieldName, Collection.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> void writeMap(String fieldName, @Nullable Map<K, V> map) throws PortableException {
+        add(fieldName, Map.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public PortableRawWriter rawWriter() {
+        return (PortableRawWriter)Proxy.newProxyInstance(getClass().getClassLoader(),
+            new Class<?>[] { PortableRawWriterEx.class },
+            new InvocationHandler() {
+                @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable {
+                    return null;
+                }
+            });
+    }
+
+    /**
+     * @param name Field name.
+     * @param fieldType Field type.
+     * @throws PortableException In case of error.
+     */
+    private void add(String name, Class<?> fieldType) throws PortableException {
+        assert fieldType != null;
+
+        add(name, fieldType.getSimpleName());
+    }
+
+    /**
+     * @param name Field name.
+     * @param fieldTypeName Field type name.
+     * @throws PortableException In case of error.
+     */
+    private void add(String name, String fieldTypeName) throws PortableException {
+        assert name != null;
+
+        String oldFieldTypeName = meta.put(name, fieldTypeName);
+
+        if (oldFieldTypeName != null && !oldFieldTypeName.equals(fieldTypeName)) {
+            throw new PortableException(
+                "Field is written twice with different types [" +
+                "typeName=" + typeName +
+                ", fieldName=" + name +
+                ", fieldTypeName1=" + oldFieldTypeName +
+                ", fieldTypeName2=" + fieldTypeName +
+                ']'
+            );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
new file mode 100644
index 0000000..e8154ab
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.*;
+
+/**
+ * Portable meta data handler.
+ */
+public interface PortableMetaDataHandler {
+    /**
+     * Adds meta data.
+     *
+     * @param typeId Type ID.
+     * @param meta Meta data.
+     * @throws PortableException In case of error.
+     */
+    public void addMeta(int typeId, PortableMetadata meta) throws PortableException;
+
+    /**
+     * Gets meta data for provided type ID.
+     *
+     * @param typeId Type ID.
+     * @return Meta data.
+     * @throws PortableException In case of error.
+     */
+    public PortableMetadata metadata(int typeId) throws PortableException;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
new file mode 100644
index 0000000..697a981
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
@@ -0,0 +1,140 @@
+/*
+ * 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.tostring.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Portable meta data implementation.
+ */
+public class PortableMetaDataImpl implements PortableMetadata, PortableMarshalAware, Externalizable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    private String typeName;
+
+    /** */
+    @GridToStringInclude
+    private Map<String, String> fields;
+
+    /** */
+    private volatile Map<Integer, String> fldIdToName;
+
+    /** */
+    private String affKeyFieldName;
+
+    /**
+     * For {@link Externalizable}.
+     */
+    public PortableMetaDataImpl() {
+        // No-op.
+    }
+
+    /**
+     * @param typeName Type name.
+     * @param fields Fields map.
+     * @param affKeyFieldName Affinity key field name.
+     */
+    public PortableMetaDataImpl(String typeName, @Nullable Map<String, String> fields,
+        @Nullable String affKeyFieldName) {
+        assert typeName != null;
+
+        this.typeName = typeName;
+        this.fields = fields;
+        this.affKeyFieldName = affKeyFieldName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String typeName() {
+        return typeName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<String> fields() {
+        return fields != null ? fields.keySet() : Collections.<String>emptyList();
+    }
+
+    /**
+     * @return Fields.
+     */
+    public Map<String, String> fields0() {
+        return fields != null ? fields : Collections.<String, String>emptyMap();
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public String fieldTypeName(String fieldName) {
+        return fields != null ? fields.get(fieldName) : null;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public String affinityKeyFieldName() {
+        return affKeyFieldName;
+    }
+
+    /**
+     * @return Fields meta data.
+     */
+    public Map<String, String> fieldsMeta() {
+        return fields != null ? fields : Collections.<String, String>emptyMap();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        U.writeString(out, typeName);
+        U.writeMap(out, fields);
+        U.writeString(out, affKeyFieldName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        typeName = U.readString(in);
+        fields = U.readMap(in);
+        affKeyFieldName = U.readString(in);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writePortable(PortableWriter writer) throws PortableException {
+        PortableRawWriter raw = writer.rawWriter();
+
+        raw.writeString(typeName);
+        raw.writeString(affKeyFieldName);
+        raw.writeMap(fields);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readPortable(PortableReader reader) throws PortableException {
+        PortableRawReader raw = reader.rawReader();
+
+        typeName = raw.readString();
+        affKeyFieldName = raw.readString();
+        fields = raw.readMap();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PortableMetaDataImpl.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
new file mode 100644
index 0000000..16038d9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
@@ -0,0 +1,89 @@
+/*
+ * 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.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+/**
+ *
+ */
+class PortableObjectArrayLazyValue extends PortableAbstractLazyValue {
+    /** */
+    private Object[] lazyValsArr;
+
+    /** */
+    private int compTypeId;
+
+    /** */
+    private String clsName;
+
+    /**
+     * @param reader Reader.
+     */
+    protected PortableObjectArrayLazyValue(PortableBuilderReader reader) {
+        super(reader, reader.position() - 1);
+
+        int typeId = reader.readInt();
+
+        if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+            clsName = reader.readString();
+
+            Class cls;
+
+            try {
+                // TODO: IGNITE-1272 - Is class loader needed here?
+                cls = U.forName(reader.readString(), null);
+            }
+            catch (ClassNotFoundException e) {
+                throw new PortableInvalidClassException("Failed to load the class: " + clsName, e);
+            }
+
+            compTypeId = reader.portableContext().descriptorForClass(cls).typeId();
+        }
+        else {
+            compTypeId = typeId;
+            clsName = null;
+        }
+
+        int size = reader.readInt();
+
+        lazyValsArr = new Object[size];
+
+        for (int i = 0; i < size; i++)
+            lazyValsArr[i] = reader.parseValue();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Object init() {
+        for (int i = 0; i < lazyValsArr.length; i++) {
+            if (lazyValsArr[i] instanceof PortableLazyValue)
+                lazyValsArr[i] = ((PortableLazyValue)lazyValsArr[i]).value();
+        }
+
+        return lazyValsArr;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+        if (clsName == null)
+            ctx.writeArray(writer, GridPortableMarshaller.OBJ_ARR, lazyValsArr, compTypeId);
+        else
+            ctx.writeArray(writer, GridPortableMarshaller.OBJ_ARR, lazyValsArr, clsName);
+    }
+}