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/06/16 15:45:13 UTC

[1/3] incubator-ignite git commit: ignite-950: checking whether a footer is supported for object or not

Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-950 204a408f9 -> 9e2943db8


ignite-950: checking whether a footer is supported for object or not


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/549a1a71
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/549a1a71
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/549a1a71

Branch: refs/heads/ignite-950
Commit: 549a1a716b5e618606e5fea30174bb2fea27f04a
Parents: 204a408
Author: Denis Magda <dm...@gridgain.com>
Authored: Tue Jun 16 11:13:19 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Tue Jun 16 11:13:19 2015 +0300

----------------------------------------------------------------------
 .../cacheobject/IgniteCacheObjectProcessor.java | 11 +++
 .../IgniteCacheObjectProcessorImpl.java         | 80 ++++++++++++++++++--
 .../optimized/OptimizedClassDescriptor.java     | 30 +++++---
 .../optimized/OptimizedMarshaller.java          | 21 +++++
 .../optimized/OptimizedMarshallerUtils.java     | 10 ++-
 .../optimized/OptimizedObjectInputStream.java   | 10 ++-
 .../optimized/OptimizedObjectOutputStream.java  | 29 ++++---
 7 files changed, 160 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
index dc0d1e5..c339037 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
@@ -97,6 +97,17 @@ public interface IgniteCacheObjectProcessor extends GridProcessor {
     public boolean hasField(Object obj, String fieldName);
 
     /**
+     * Checks whether a footer injection into a serialized form of the object is supported.
+     * Footer contains information on fields location in the serialized form, thus enabling fast queries without a need
+     * to deserialize the object.
+     *
+     * @param obj Object.
+     * @return {@code true} if the footer is supported.
+     * @throws IgniteCheckedException If failed.
+     */
+    public boolean footerSupported(Object obj) throws IgniteCheckedException;
+
+    /**
      * @param ctx Cache object context.
      * @param val Value.
      * @return Value bytes.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index c9f3444..a002d36 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -27,10 +27,13 @@ import org.apache.ignite.internal.processors.query.*;
 import org.apache.ignite.internal.util.*;
 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.jetbrains.annotations.*;
 
 import java.math.*;
 import java.util.*;
+import java.util.concurrent.*;
 
 import static org.apache.ignite.cache.CacheMemoryMode.*;
 
@@ -44,6 +47,21 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     /** Immutable classes. */
     private static final Collection<Class<?>> IMMUTABLE_CLS = new HashSet<>();
 
+    /** */
+    private static final OptimizedObjectMetadata EMPTY_META = new OptimizedObjectMetadata();
+
+    /** */
+    private volatile IgniteCacheProxy<OptimizedObjectMetadataKey, OptimizedObjectMetadata> metaDataCache;
+
+    /** Metadata updates collected before metadata cache is initialized. */
+    private final ConcurrentHashMap<Integer, OptimizedObjectMetadata> metaBuf = new ConcurrentHashMap<>();
+
+    /** */
+    private final CountDownLatch startLatch = new CountDownLatch(1);
+
+    /** */
+    private OptimizedMarshaller optMarsh;
+
     /**
      *
      */
@@ -62,6 +80,58 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
         IMMUTABLE_CLS.add(BigDecimal.class);
     }
 
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteCheckedException {
+        super.start();
+
+        Marshaller marsh = ctx.config().getMarshaller();
+
+        if (marsh instanceof OptimizedMarshaller) {
+            optMarsh = (OptimizedMarshaller)marsh;
+
+            OptimizedObjectMetadataHandler metaHandler = new OptimizedObjectMetadataHandler() {
+                @Override public void addMeta(int typeId, OptimizedObjectMetadata meta) {
+                    if (metaBuf.contains(typeId))
+                        return;
+
+                    metaBuf.put(typeId, meta);
+
+                    if (metaDataCache != null)
+                        metaDataCache.putIfAbsent(new OptimizedObjectMetadataKey(typeId), meta);
+                }
+
+                @Override public OptimizedObjectMetadata metadata(int typeId) {
+                    if (metaDataCache == null)
+                        U.awaitQuiet(startLatch);
+
+                    OptimizedObjectMetadata meta = metaBuf.get(typeId);
+
+                    if (meta != null)
+                        return meta == EMPTY_META ? null : meta;
+
+                    meta = metaDataCache.localPeek(new OptimizedObjectMetadataKey(typeId));
+
+                    if (meta == null)
+                        meta = EMPTY_META;
+
+                    return meta == EMPTY_META ? null : meta;
+                }
+            };
+
+            optMarsh.setMetadataHandler(metaHandler);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onUtilityCacheStarted() throws IgniteCheckedException {
+        metaDataCache = ctx.cache().jcache(CU.UTILITY_CACHE_NAME);
+
+        startLatch.countDown();
+
+        for (Map.Entry<Integer, OptimizedObjectMetadata> e : metaBuf.entrySet())
+            metaDataCache.putIfAbsent(new OptimizedObjectMetadataKey(e.getKey()), e.getValue());
+    }
+
     /**
      * @param ctx Context.
      */
@@ -213,11 +283,6 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     }
 
     /** {@inheritDoc} */
-    @Override public void onUtilityCacheStarted() throws IgniteCheckedException {
-        // No-op.
-    }
-
-    /** {@inheritDoc} */
     @Override public int typeId(String typeName) {
         return 0;
     }
@@ -253,6 +318,11 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
         return false;
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean footerSupported(Object obj) throws IgniteCheckedException {
+        return optMarsh != null && optMarsh.footerSupported(obj);
+    }
+
     /**
      * Wraps key provided by user, must be serialized before stored in cache.
      */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
index ba3dfcf..0ef39b1 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
@@ -119,6 +119,8 @@ class OptimizedClassDescriptor {
      * @param ctx Context.
      * @param mapper ID mapper.
      * @param metaHandler Metadata handler.
+     * @param tryEnableMeta Try to enable meta during {@code OptimizedClassDescriptor} registration. Meta is supported,
+     *                      only for objects that support footer injection is their serialized form.
      * @throws IOException In case of error.
      */
     @SuppressWarnings("ForLoopReplaceableByForEach")
@@ -127,7 +129,8 @@ class OptimizedClassDescriptor {
         ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
         MarshallerContext ctx,
         OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler)
+        OptimizedObjectMetadataHandler metaHandler,
+        boolean tryEnableMeta)
         throws IOException {
         this.cls = cls;
         this.typeId = typeId;
@@ -487,7 +490,7 @@ class OptimizedClassDescriptor {
 
                     this.fields = new Fields(fields, fieldsIndexingEnabled);
 
-                    if (fieldsIndexingEnabled && metaHandler.metadata(typeId) == null) {
+                    if (tryEnableMeta && fieldsIndexingEnabled && metaHandler.metadata(typeId) == null) {
                         OptimizedObjectMetadata meta = new OptimizedObjectMetadata();
 
                         for (ClassFields clsFields : this.fields.fields)
@@ -496,9 +499,6 @@ class OptimizedClassDescriptor {
 
                         U.debug("putting to cache: " + typeId);
 
-                        if (typeId == 2104067130) {
-                            System.out.println();
-                        }
                         metaHandler.addMeta(typeId, meta);
 
                         U.debug("put to cache: " + typeId);
@@ -659,9 +659,8 @@ class OptimizedClassDescriptor {
 
             case OBJ_ARR:
                 OptimizedClassDescriptor compDesc = classDescriptor(clsMap,
-                    obj.getClass().getComponentType(),
-                    ctx,
-                    mapper, metaHandler);
+                    obj.getClass().getComponentType(), ctx,
+                    mapper, metaHandler, false);
 
                 compDesc.writeTypeData(out);
 
@@ -720,7 +719,8 @@ class OptimizedClassDescriptor {
                 break;
 
             case CLS:
-                OptimizedClassDescriptor clsDesc = classDescriptor(clsMap, (Class<?>)obj, ctx, mapper, metaHandler);
+                OptimizedClassDescriptor clsDesc = classDescriptor(clsMap, (Class<?>)obj, ctx, mapper, metaHandler,
+                                                       false);
 
                 clsDesc.writeTypeData(out);
 
@@ -815,6 +815,18 @@ class OptimizedClassDescriptor {
     }
 
     /**
+     * Returns type ID.
+     *
+     * @return Type ID.
+     */
+    public int typeId() {
+        if (typeId == 0)
+            return resolveTypeId(cls.getName(), mapper);
+
+        return typeId;
+    }
+
+    /**
      * @param cls Class.
      * @return Type.
      */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
index a4971c4..4e5b1a7 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
@@ -144,6 +144,7 @@ public class OptimizedMarshaller extends AbstractMarshaller {
      * performance reason to avoid costly recreation for every serialization routine. If {@code 0} (default),
      * pool is not used and each thread has its own cached object stream which it keeps reusing.
      * <p>
+     *     
      * Since each stream has an internal buffer, creating a stream for each thread can lead to
      * high memory consumption if many large messages are marshalled or unmarshalled concurrently.
      * Consider using pool in this case. This will limit number of streams that can be created and,
@@ -289,6 +290,26 @@ public class OptimizedMarshaller extends AbstractMarshaller {
     }
 
     /**
+     * Checks whether a footer injection into a serialized form of the object is supported.
+     * Footer contains information on fields location in the serialized form, thus enabling fast queries without a need
+     * to deserialize the object.
+     *
+     * @param obj Object.
+     * @return {@code true} if the footer is supported.
+     */
+    public boolean footerSupported(Object obj) throws IgniteCheckedException {
+        try {
+            OptimizedClassDescriptor desc = OptimizedMarshallerUtils.classDescriptor(clsMap, obj.getClass(), ctx,
+                mapper, metaHandler, true);
+
+            return  metaHandler.metadata(desc.typeId()) != null;
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to get class descriptor.", e);
+        }
+    }
+
+    /**
      * Checks whether {@code GridOptimizedMarshaller} is able to work on the current JVM.
      * <p>
      * As long as {@code GridOptimizedMarshaller} uses JVM-private API, which is not guaranteed

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
index bf6a4fa..138e732 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
@@ -181,6 +181,8 @@ class OptimizedMarshallerUtils {
      * @param ctx Context.
      * @param mapper ID mapper.
      * @param metaHandler Metadata handler.
+     * @param tryEnableMeta Try to enable meta during {@code OptimizedClassDescriptor} registration. Meta is supported,
+     *                      only for objects that support footer injection is their serialized form.
      * @return Descriptor.
      * @throws IOException In case of error.
      */
@@ -189,7 +191,8 @@ class OptimizedMarshallerUtils {
         Class cls,
         MarshallerContext ctx,
         OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler)
+        OptimizedObjectMetadataHandler metaHandler,
+        boolean tryEnableMeta)
         throws IOException
     {
         OptimizedClassDescriptor desc = clsMap.get(cls);
@@ -206,7 +209,8 @@ class OptimizedMarshallerUtils {
                 throw new IOException("Failed to register class: " + cls.getName(), e);
             }
 
-            desc = new OptimizedClassDescriptor(cls, registered ? typeId : 0, clsMap, ctx, mapper, metaHandler);
+            desc = new OptimizedClassDescriptor(cls, registered ? typeId : 0, clsMap, ctx, mapper, metaHandler,
+                       tryEnableMeta);
 
             if (registered) {
                 OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc);
@@ -281,7 +285,7 @@ class OptimizedMarshallerUtils {
         if (desc == null) {
             OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc =
                 new OptimizedClassDescriptor(cls, resolveTypeId(cls.getName(), mapper), clsMap, ctx, mapper,
-                    metaHandler));
+                    metaHandler, false));
 
             if (old != null)
                 desc = old;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
index 846fb08..70a2f3f 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
@@ -250,7 +250,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                 int typeId = readInt();
 
                 OptimizedClassDescriptor desc = typeId == 0 ?
-                    classDescriptor(clsMap, U.forName(readUTF(), clsLdr), ctx, mapper, metaHandler):
+                    classDescriptor(clsMap, U.forName(readUTF(), clsLdr), ctx, mapper, metaHandler, false):
                     classDescriptor(clsMap, typeId, clsLdr, ctx, mapper, metaHandler);
 
                 curCls = desc.describedClass();
@@ -540,10 +540,12 @@ class OptimizedObjectInputStream extends ObjectInputStream {
             }
         }
 
-        int footerLen = in.readInt();
+        if (metaHandler.metadata(resolveTypeId(cls.getName(), mapper)) != null) {
+            int footerLen = in.readInt();
 
-        if (footerLen != EMPTY_FOOTER)
-            in.skipBytes(footerLen - 4);
+            if (footerLen != EMPTY_FOOTER)
+                in.skipBytes(footerLen - 4);
+        }
 
         return obj;
     }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/549a1a71/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
index 2ead955..05d3e3d 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
@@ -194,7 +194,8 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                     obj instanceof Object[] ? Object[].class : obj.getClass(),
                     ctx,
                     mapper,
-                    metaHandler);
+                    metaHandler,
+                    false);
 
                 if (desc.excluded()) {
                     writeByte(NULL);
@@ -220,7 +221,8 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                         obj instanceof Object[] ? Object[].class : obj.getClass(),
                         ctx,
                         mapper,
-                        metaHandler);
+                        metaHandler,
+                        false);
                 }
 
                 if (handle >= 0) {
@@ -317,9 +319,13 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     @SuppressWarnings("ForLoopReplaceableByForEach")
     void writeSerializable(Object obj, List<Method> mtds, OptimizedClassDescriptor.Fields fields, int headerPos)
         throws IOException {
-        Footer footer = new Footer(fields);
+        Footer footer = null;
 
-        footer.headerPos(headerPos);
+        if (metaHandler.metadata(resolveTypeId(obj.getClass().getName(), mapper)) != null) {
+            footer = new Footer(fields);
+
+            footer.headerPos(headerPos);
+        }
 
         for (int i = 0; i < mtds.size(); i++) {
             Method mtd = mtds.get(i);
@@ -343,7 +349,8 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                 writeFields(obj, fields.fields(i), footer);
         }
 
-        footer.write();
+        if (footer != null)
+            footer.write();
     }
 
     /**
@@ -553,12 +560,12 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                     if (t.field() != null) {
                         int handle = writeObject0(getObject(obj, t.offset()));
 
-                        if (handle >= 0)
+                        if (footer != null && handle >= 0)
                             footer.disable();
                     }
             }
 
-            if (t.field() != null) {
+            if (footer != null && t.field() != null) {
                 int fieldLen = out.size() - size;
 
                 footer.put(t.id(), t.type(), fieldLen);
@@ -813,13 +820,15 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                 case OTHER:
                     int handle = writeObject0(t.get2());
 
-                    if (handle >= 0)
+                    if (footer != null && handle >= 0)
                         footer.disable();
             }
 
-            int fieldLen = out.size() - size;
+            if (footer != null) {
+                int fieldLen = out.size() - size;
 
-            footer.put(t.get1().id(), t.get1().type(), fieldLen);
+                footer.put(t.get1().id(), t.get1().type(), fieldLen);
+            }
         }
     }
 


[2/3] incubator-ignite git commit: ignite-950: moved all footer related logic to dedicated class, unmarshalling enclosed objects, wrapping serializable fields with CacheObjectImpl

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadata.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadata.java
new file mode 100644
index 0000000..7154a25
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadata.java
@@ -0,0 +1,149 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.*;
+import org.apache.ignite.marshaller.optimized.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Metadata that keeps fields information. Used in conjunction with the footer that is added to some objects during
+ * marshalling.
+ */
+public class OptimizedObjectMetadata implements Externalizable {
+    /** */
+    private List<FieldInfo> fieldsInfo;
+
+    /** Constructor. */
+    public OptimizedObjectMetadata() {
+        // No-op
+    }
+
+    /**
+     * Adds meta for a new field.
+     *
+     * @param fieldId Field ID.
+     * @param fieldType Field type.
+     */
+    public void addMeta(int fieldId, OptimizedFieldType fieldType) {
+        if (fieldsInfo == null)
+            fieldsInfo = new ArrayList<>();
+
+        int len = 1;
+
+        switch (fieldType) {
+            case BYTE:
+            case BOOLEAN:
+                len += 1;
+                break;
+
+            case SHORT:
+            case CHAR:
+                len += 2;
+                break;
+
+            case INT:
+            case FLOAT:
+                len += 4;
+                break;
+
+            case LONG:
+            case DOUBLE:
+                len += 8;
+                break;
+
+            case OTHER:
+                len = OptimizedMarshallerExt.VARIABLE_LEN;
+                break;
+
+            default:
+                throw new IgniteException("Unknown field type: " + fieldType);
+        }
+
+        assert len != 1;
+
+        fieldsInfo.add(new FieldInfo(fieldId, len));
+    }
+
+    /**
+     * Gets {@link OptimizedObjectMetadata.FieldInfo} at the {@code index}.
+     *
+     * @param index Position.
+     * @return Field meta info.
+     */
+    public FieldInfo getMeta(int index) {
+        return fieldsInfo.get(index);
+    }
+    /**
+     * Returns all the metadata stored for the object.
+     *
+     * @return Metadata collection.
+     */
+    public List<FieldInfo> getMeta() {
+        return Collections.unmodifiableList(fieldsInfo);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        if (fieldsInfo == null) {
+            out.writeInt(0);
+            return;
+        }
+
+        out.writeInt(fieldsInfo.size());
+
+        for (FieldInfo fieldInfo : fieldsInfo) {
+            out.writeInt(fieldInfo.id);
+            out.writeInt(fieldInfo.len);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        int size = in.readInt();
+
+        fieldsInfo = new ArrayList<>(size);
+
+        for (int i = 0; i < size; i++)
+            fieldsInfo.add(new FieldInfo(in.readInt(), in.readInt()));
+    }
+
+    /**
+     * Field info.
+     */
+    public static class FieldInfo {
+        /** Field ID. */
+        int id;
+
+        /** Field type. */
+        int len;
+
+        /**
+         * Constructor.
+         *
+         * @param id Field ID.
+         * @param len Field len.
+         */
+        public FieldInfo(int id, int len) {
+            this.id = id;
+            this.len = len;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadataKey.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadataKey.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadataKey.java
new file mode 100644
index 0000000..b9fcd58
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectMetadataKey.java
@@ -0,0 +1,70 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.internal.processors.cache.*;
+
+import java.io.*;
+
+/**
+ * Optimized object metadata key.
+ */
+public class OptimizedObjectMetadataKey extends GridCacheUtilityKey<OptimizedObjectMetadataKey>
+    implements Externalizable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    private int typeId;
+
+    /**
+     * For {@link Externalizable}.
+     */
+    public OptimizedObjectMetadataKey() {
+        // No-op
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param typeId Type id.
+     */
+    public OptimizedObjectMetadataKey(int typeId) {
+        this.typeId = typeId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeInt(typeId);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        typeId = in.readInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected boolean equalsx(OptimizedObjectMetadataKey key) {
+        return typeId == key.typeId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return typeId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectOutputStreamExt.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectOutputStreamExt.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectOutputStreamExt.java
new file mode 100644
index 0000000..823caf5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectOutputStreamExt.java
@@ -0,0 +1,135 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.internal.util.io.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.marshaller.optimized.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import static org.apache.ignite.marshaller.optimized.ext.OptimizedMarshallerExt.*;
+import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*;
+
+/**
+ * TODO: IGNITE-950
+ */
+public class OptimizedObjectOutputStreamExt extends OptimizedObjectOutputStream {
+    /** */
+    private OptimizedMarshallerExtMetaHandler metaHandler;
+
+    /** {@inheritDoc} */
+    protected OptimizedObjectOutputStreamExt(GridDataOutput out) throws IOException {
+        super(out);
+    }
+
+    /**
+     * @param clsMap Class descriptors by class map.
+     * @param ctx Context.
+     * @param mapper ID mapper.
+     * @param requireSer Require {@link Serializable} flag.
+     * @param metaHandler Metadata handler.
+     */
+    protected void context(ConcurrentMap<Class, OptimizedClassDescriptor> clsMap, MarshallerContext ctx,
+        OptimizedMarshallerIdMapper mapper, boolean requireSer, OptimizedMarshallerExtMetaHandler metaHandler) {
+        context(clsMap, ctx, mapper, requireSer);
+
+        this.metaHandler = metaHandler;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void writeFieldType(byte type) throws IOException {
+        out.writeByte(type);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Footer createFooter(Class<?> cls) {
+        if (metaHandler.metadata(resolveTypeId(cls.getName(), mapper)) != null)
+            return new FooterImpl();
+        else
+            return null;
+    }
+
+    /**
+     *
+     */
+    private class FooterImpl implements OptimizedObjectOutputStream.Footer {
+        /** */
+        private ArrayList<Short> data;
+
+        /** */
+        private int headerPos;
+
+        /** {@inheritDoc} */
+        @Override public void fields(OptimizedClassDescriptor.Fields fields) {
+            if (fields.fieldsIndexingSupported())
+                data = new ArrayList<>();
+            else
+                data = null;
+        }
+
+        /** {@inheritDoc} */
+        public void headerPos(int pos) {
+            headerPos = pos;
+        }
+
+        /** {@inheritDoc} */
+        public void put(int fieldId, OptimizedFieldType fieldType, int len) {
+            if (data == null)
+                return;
+
+            // Considering that field's length will be no longer 2^15 (32 MB)
+            if (fieldType == OptimizedFieldType.OTHER)
+                data.add((short)len);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void putHandle(int fieldId, int handleId) {
+            disable();
+        }
+
+        /** {@inheritDoc} */
+        public void write() throws IOException {
+            if (data == null)
+                writeInt(EMPTY_FOOTER);
+            else {
+                //12 - 4 bytes for len at the beginning, 4 bytes for len at the end, 4 bytes for object len.
+                int footerLen = data.size() * 2 + 12;
+
+                writeInt(footerLen);
+
+                for (short fieldLen : data)
+                    writeShort(fieldLen);
+
+                // object total len
+                writeInt((out.size() - headerPos) + 8);
+
+                writeInt(footerLen);
+            }
+        }
+
+        /**
+         * Disable footer and indexing for the given Object.
+         */
+        private void disable() {
+            data = null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectStreamExtRegistry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectStreamExtRegistry.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectStreamExtRegistry.java
new file mode 100644
index 0000000..e07b4de
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectStreamExtRegistry.java
@@ -0,0 +1,226 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.*;
+import org.apache.ignite.internal.*;
+import org.apache.ignite.internal.util.io.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.marshaller.optimized.*;
+
+import java.io.*;
+import java.util.concurrent.*;
+
+/**
+ * Storage for object streams.
+ */
+class OptimizedObjectStreamExtRegistry {
+    /** Holders. */
+    private static final ThreadLocal<StreamHolder> holders = new ThreadLocal<>();
+
+    /** Holders pool. */
+    private static BlockingQueue<StreamHolder> pool;
+
+    /**
+     * Ensures singleton.
+     */
+    private OptimizedObjectStreamExtRegistry() {
+        // No-op.
+    }
+
+    /**
+     * Sets streams pool size.
+     *
+     * @param size Streams pool size.
+     */
+    static void poolSize(int size) {
+        if (size > 0) {
+            pool = new LinkedBlockingQueue<>(size);
+
+            for (int i = 0; i < size; i++) {
+                boolean b = pool.offer(new StreamHolder());
+
+                assert b;
+            }
+        }
+        else
+            pool = null;
+    }
+
+    /**
+     * Gets output stream.
+     *
+     * @return Object output stream.
+     * @throws IgniteInterruptedCheckedException If thread is interrupted while trying to take holder from pool.
+     */
+    static OptimizedObjectOutputStreamExt out() throws IgniteInterruptedCheckedException {
+        return holder().acquireOut();
+    }
+
+    /**
+     * Gets input stream.
+     *
+     * @return Object input stream.
+     * @throws IgniteInterruptedCheckedException If thread is interrupted while trying to take holder from pool.
+     */
+    static OptimizedObjectInputStreamExt in() throws IgniteInterruptedCheckedException {
+        return holder().acquireIn();
+    }
+
+    /**
+     * Closes and releases output stream.
+     *
+     * @param out Object output stream.
+     */
+    static void closeOut(OptimizedObjectOutputStream out) {
+        U.close(out, null);
+
+        StreamHolder holder = holders.get();
+
+        holder.releaseOut();
+
+        if (pool != null) {
+            holders.remove();
+
+            boolean b = pool.offer(holder);
+
+            assert b;
+        }
+    }
+
+    /**
+     * Closes and releases input stream.
+     *
+     * @param in Object input stream.
+     */
+    @SuppressWarnings("TypeMayBeWeakened")
+    static void closeIn(OptimizedObjectInputStream in) {
+        U.close(in, null);
+
+        StreamHolder holder = holders.get();
+
+        holder.releaseIn();
+
+        if (pool != null) {
+            holders.remove();
+
+            boolean b = pool.offer(holder);
+
+            assert b;
+        }
+    }
+
+    /**
+     * Gets holder from pool or thread local.
+     *
+     * @return Stream holder.
+     * @throws IgniteInterruptedCheckedException If thread is interrupted while trying to take holder from pool.
+     */
+    private static StreamHolder holder() throws IgniteInterruptedCheckedException {
+        StreamHolder holder = holders.get();
+
+        if (holder == null) {
+            try {
+                holders.set(holder = pool != null ? pool.take() : new StreamHolder());
+            }
+            catch (InterruptedException e) {
+                throw new IgniteInterruptedCheckedException(
+                    "Failed to take object stream from pool (thread interrupted).", e);
+            }
+        }
+
+        return holder;
+    }
+
+    /**
+     * Streams holder.
+     */
+    private static class StreamHolder {
+        /** Output stream. */
+        private final OptimizedObjectOutputStreamExt out = createOut();
+
+        /** Input stream. */
+        private final OptimizedObjectInputStreamExt in = createIn();
+
+        /** Output streams counter. */
+        private int outAcquireCnt;
+
+        /** Input streams counter. */
+        private int inAcquireCnt;
+
+        /**
+         * Gets output stream.
+         *
+         * @return Object output stream.
+         */
+        OptimizedObjectOutputStreamExt acquireOut() {
+            return outAcquireCnt++ > 0 ? createOut() : out;
+        }
+
+        /**
+         * Gets input stream.
+         *
+         * @return Object input stream.
+         */
+        OptimizedObjectInputStreamExt acquireIn() {
+            return inAcquireCnt++ > 0 ? createIn() : in;
+        }
+
+        /**
+         * Releases output stream.
+         */
+        void releaseOut() {
+            outAcquireCnt--;
+        }
+
+        /**
+         * Releases input stream.
+         */
+        void releaseIn() {
+            inAcquireCnt--;
+        }
+
+        /**
+         * Creates output stream.
+         *
+         * @return Object output stream.
+         */
+        private OptimizedObjectOutputStreamExt createOut() {
+            try {
+                return new OptimizedObjectOutputStreamExt(new GridUnsafeDataOutput(4 * 1024));
+            }
+            catch (IOException e) {
+                throw new IgniteException("Failed to create object output stream.", e);
+            }
+        }
+
+        /**
+         * Creates input stream.
+         *
+         * @return Object input stream.
+         */
+        private OptimizedObjectInputStreamExt createIn() {
+            try {
+                return new OptimizedObjectInputStreamExt(new GridUnsafeDataInput());
+            }
+            catch (IOException e) {
+                throw new IgniteException("Failed to create object input stream.", e);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/package-info.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
new file mode 100644
index 0000000..294ef7f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 description. -->
+ * Contains <b>extended</b> Optimized marshaller.
+ */
+package org.apache.ignite.marshaller.optimized.ext;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerSelfTest.java
index 6c78506..7d09070 100644
--- a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerSelfTest.java
@@ -111,19 +111,6 @@ public class OptimizedMarshallerSelfTest extends GridMarshallerAbstractTest {
     }
 
     /**
-     * @throws Exception If failed.
-     */
-    public void testFieldUnmarshalling() throws Exception {
-        TestObject2 obj = new TestObject2(5);
-
-        byte[] data = marshal(obj);
-
-        Integer i = ((OptimizedMarshaller)marsh).unmarshal("i", data, Thread.currentThread().getContextClassLoader());
-
-        assertEquals(obj.i, (int)i);
-    }
-
-    /**
      * Class for nested execution test.
      */
     private static class NestedTestObject implements Serializable {

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedObjectStreamSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedObjectStreamSelfTest.java b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedObjectStreamSelfTest.java
index 0324e2f..20391eb 100644
--- a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedObjectStreamSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedObjectStreamSelfTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.marshaller.optimized;
 
 import org.apache.ignite.*;
-import org.apache.ignite.internal.processors.cache.*;
 import org.apache.ignite.internal.util.io.*;
 import org.apache.ignite.internal.util.typedef.*;
 import org.apache.ignite.internal.util.typedef.internal.*;
@@ -47,17 +46,6 @@ public class OptimizedObjectStreamSelfTest extends GridCommonAbstractTest {
     /** */
     private ConcurrentMap<Class, OptimizedClassDescriptor> clsMap = new ConcurrentHashMap8<>();
 
-    /** */
-    private static final OptimizedObjectMetadataHandler META_HANDLER = new OptimizedObjectMetadataHandler() {
-        @Override public void addMeta(int typeId, OptimizedObjectMetadata meta) {
-
-        }
-
-        @Override public OptimizedObjectMetadata metadata(int typeId) {
-            return null;
-        }
-    };
-
     /**
      * @throws Exception If failed.
      */
@@ -1033,7 +1021,7 @@ public class OptimizedObjectStreamSelfTest extends GridCommonAbstractTest {
         try {
             out = OptimizedObjectStreamRegistry.out();
 
-            out.context(clsMap, CTX, null, META_HANDLER, true);
+            out.context(clsMap, CTX, null, true);
 
             out.writeObject(obj);
 
@@ -1041,7 +1029,7 @@ public class OptimizedObjectStreamSelfTest extends GridCommonAbstractTest {
 
             in = OptimizedObjectStreamRegistry.in();
 
-            in.context(clsMap, CTX, null, META_HANDLER, getClass().getClassLoader());
+            in.context(clsMap, CTX, null, getClass().getClassLoader());
 
             in.in().bytes(arr, arr.length);
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtSelfTest.java b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtSelfTest.java
new file mode 100644
index 0000000..d663c55
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtSelfTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.marshaller.optimized.*;
+import org.apache.ignite.testframework.junits.common.*;
+
+import java.util.concurrent.*;
+
+/**
+ * Optimized marshaller self test.
+ */
+@GridCommonTest(group = "Marshaller")
+public class OptimizedMarshallerExtSelfTest extends OptimizedMarshallerSelfTest {
+    /** {@inheritDoc} */
+    @Override protected Marshaller marshaller() {
+        return new OptimizedMarshallerExt(false);
+    }
+
+    /**
+     * @throws Exception In case of error.
+     */
+    public void testHasField() throws Exception {
+        OptimizedMarshallerExt marsh = (OptimizedMarshallerExt)OptimizedMarshallerExtSelfTest.marsh;
+
+        assertTrue(marsh.putMetaForClass(TestObject.class));
+
+        TestObject testObj = new TestObject("World", 50);
+
+        byte[] arr = marsh.marshal(testObj);
+
+        assertTrue(marsh.hasField("o2", arr, 0, arr.length));
+        assertTrue(marsh.hasField("str", arr, 0, arr.length));
+
+        assertFalse(marsh.hasField("m", arr, 0, arr.length));
+    }
+
+    /**
+     * @throws Exception In case of error.
+     */
+    public void testReadField() throws Exception {
+        OptimizedMarshallerExt marsh = (OptimizedMarshallerExt)OptimizedMarshallerExtSelfTest.marsh;
+
+        assertTrue(marsh.putMetaForClass(TestObject.class));
+
+        TestObject testObj = new TestObject("World", 50);
+
+        byte[] arr = marsh.marshal(testObj);
+
+        // Simple field extraction.
+
+        String text = marsh.readField("str", arr, 0, arr.length, null);
+
+        assertEquals(testObj.str, text);
+
+        // Serializable extraction (doesn't have meta, thus doesn't have footer)
+        TestObject2 o2 = marsh.readField("o2", arr, 0, arr.length, null);
+
+        assertEquals(testObj.o2, o2);
+
+        // Add metadata for the enclosed object.
+        assertTrue(marsh.putMetaForClass(TestObject2.class));
+
+        arr = marsh.marshal(testObj);
+
+        // Must be returned in a wrapped form, since metadata was added enabling the footer.
+        CacheObjectImpl cacheObject = marsh.readField("o2", arr, 0, arr.length, null);
+
+        arr = cacheObject.valueBytes(null);
+
+        // Check enclosed objects fields
+        assertTrue(marsh.hasField("i", arr, 0, arr.length));
+        assertEquals(testObj.o2.i, (int)marsh.readField("i", arr, 0, arr.length, null));
+    }
+
+    /** */
+    private static class TestObject2 {
+        /** */
+        private final int i;
+
+        /**
+         * Constructor for TestObject2 instances.
+         *
+         * @param i Integer value to hold.
+         */
+        private TestObject2(int i) {
+            this.i = i;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            return i == ((TestObject2)o).i;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return i;
+        }
+    }
+
+    /**
+     * Static nested class.
+     */
+    private static class TestObject {
+        /** */
+        private final TestObject2 o2;
+
+        /** The only meaningful field in the class, used for {@link #equals(Object o)} and {@link #hashCode()}. */
+        private final String str;
+
+        /**
+         * @param str String to hold.
+         * @param i Integer.
+         */
+        TestObject(String str, int i) {
+            this.str = str;
+
+            o2 = new TestObject2(i);
+        }
+
+        /**
+         * Method for accessing value of the hold string after the object is created.
+         *
+         * @return Wrapped string.
+         */
+        public String string() {
+            return str;
+        }
+
+        /**
+         * @return Object held in this wrapped.
+         */
+        public TestObject2 obj() {
+            return o2;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return 31 * o2.hashCode() + str.hashCode();
+        }
+
+        /** {@inheritDoc} */
+        @SuppressWarnings("RedundantIfStatement")
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestObject obj = (TestObject)o;
+
+            if (o2 != null ? !o2.equals(obj.o2) : obj.o2 != null)
+                return false;
+
+            if (str != null ? !str.equals(obj.str) : obj.str != null)
+                return false;
+
+            return true;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/package-info.java b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
new file mode 100644
index 0000000..eabdbce
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/ext/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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 description. -->
+ * Extended optimized marshaller test package.
+ */
+package org.apache.ignite.marshaller.optimized.ext;
\ No newline at end of file


[3/3] incubator-ignite git commit: ignite-950: moved all footer related logic to dedicated class, unmarshalling enclosed objects, wrapping serializable fields with CacheObjectImpl

Posted by sb...@apache.org.
ignite-950: moved all footer related logic to dedicated class, unmarshalling enclosed objects, wrapping serializable fields with CacheObjectImpl


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/9e2943db
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/9e2943db
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/9e2943db

Branch: refs/heads/ignite-950
Commit: 9e2943db84989606ce2b6e19cf0949b77f48be84
Parents: 549a1a7
Author: Denis Magda <dm...@gridgain.com>
Authored: Tue Jun 16 16:44:55 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Tue Jun 16 16:44:55 2015 +0300

----------------------------------------------------------------------
 .../processors/cache/CacheObjectImpl.java       |  21 ++
 .../cacheobject/IgniteCacheObjectProcessor.java |   4 +-
 .../IgniteCacheObjectProcessorImpl.java         |  15 +-
 .../ignite/internal/util/io/GridDataInput.java  |   7 +
 .../internal/util/io/GridUnsafeDataInput.java   |   5 +
 .../optimized/OptimizedClassDescriptor.java     | 121 ++++----
 .../optimized/OptimizedFieldType.java           |   2 +-
 .../optimized/OptimizedMarshaller.java          |  77 +----
 .../optimized/OptimizedMarshallerUtils.java     |  34 +--
 .../optimized/OptimizedObjectInputStream.java   | 217 +++-----------
 .../optimized/OptimizedObjectMetadata.java      | 149 ----------
 .../OptimizedObjectMetadataHandler.java         |  40 ---
 .../optimized/OptimizedObjectMetadataKey.java   |  70 -----
 .../optimized/OptimizedObjectOutputStream.java  | 160 +++++-----
 .../optimized/ext/OptimizedMarshallerExt.java   | 291 +++++++++++++++++++
 .../ext/OptimizedMarshallerExtMetaHandler.java  |  40 +++
 .../ext/OptimizedObjectInputStreamExt.java      | 212 ++++++++++++++
 .../optimized/ext/OptimizedObjectMetadata.java  | 149 ++++++++++
 .../ext/OptimizedObjectMetadataKey.java         |  70 +++++
 .../ext/OptimizedObjectOutputStreamExt.java     | 135 +++++++++
 .../ext/OptimizedObjectStreamExtRegistry.java   | 226 ++++++++++++++
 .../marshaller/optimized/ext/package-info.java  |  21 ++
 .../optimized/OptimizedMarshallerSelfTest.java  |  13 -
 .../OptimizedObjectStreamSelfTest.java          |  16 +-
 .../ext/OptimizedMarshallerExtSelfTest.java     | 179 ++++++++++++
 .../marshaller/optimized/ext/package-info.java  |  22 ++
 26 files changed, 1579 insertions(+), 717 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectImpl.java
index ad033a7..49c0262 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectImpl.java
@@ -104,6 +104,27 @@ public class CacheObjectImpl extends CacheObjectAdapter {
             val = ctx.processor().unmarshal(ctx, valBytes, ldr);
     }
 
+    /**
+     * Gets field value.
+     *
+     * @param fieldName Field name.
+     * @return Field value.
+     * @throws IgniteCheckedException In case of any other error.
+     */
+    @Nullable public <F> F field(String fieldName) throws IgniteCheckedException {
+        return null;
+    }
+
+    /**
+     * Checks whether field is set.
+     *
+     * @param fieldName Field name.
+     * @return {@code true} if field is set.
+     */
+    public boolean hasField(String fieldName) {
+        return false;
+    }
+
     /** {@inheritDoc} */
     @Override public byte directType() {
         return 89;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
index c339037..6a5f947 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
@@ -101,11 +101,11 @@ public interface IgniteCacheObjectProcessor extends GridProcessor {
      * Footer contains information on fields location in the serialized form, thus enabling fast queries without a need
      * to deserialize the object.
      *
-     * @param obj Object.
+     * @param cls Class.
      * @return {@code true} if the footer is supported.
      * @throws IgniteCheckedException If failed.
      */
-    public boolean footerSupported(Object obj) throws IgniteCheckedException;
+    public boolean footerSupported(Class<?> cls) throws IgniteCheckedException;
 
     /**
      * @param ctx Cache object context.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index a002d36..7a89598 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -29,6 +29,7 @@ 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.optimized.ext.*;
 import org.jetbrains.annotations.*;
 
 import java.math.*;
@@ -60,7 +61,7 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     private final CountDownLatch startLatch = new CountDownLatch(1);
 
     /** */
-    private OptimizedMarshaller optMarsh;
+    private OptimizedMarshallerExt optMarshExt;
 
     /**
      *
@@ -86,10 +87,10 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
 
         Marshaller marsh = ctx.config().getMarshaller();
 
-        if (marsh instanceof OptimizedMarshaller) {
-            optMarsh = (OptimizedMarshaller)marsh;
+        if (marsh instanceof OptimizedMarshallerExt) {
+            optMarshExt = (OptimizedMarshallerExt)marsh;
 
-            OptimizedObjectMetadataHandler metaHandler = new OptimizedObjectMetadataHandler() {
+            OptimizedMarshallerExtMetaHandler metaHandler = new OptimizedMarshallerExtMetaHandler() {
                 @Override public void addMeta(int typeId, OptimizedObjectMetadata meta) {
                     if (metaBuf.contains(typeId))
                         return;
@@ -118,7 +119,7 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
                 }
             };
 
-            optMarsh.setMetadataHandler(metaHandler);
+            optMarshExt.setMetadataHandler(metaHandler);
         }
     }
 
@@ -319,8 +320,8 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     }
 
     /** {@inheritDoc} */
-    @Override public boolean footerSupported(Object obj) throws IgniteCheckedException {
-        return optMarsh != null && optMarsh.footerSupported(obj);
+    @Override public boolean footerSupported(Class<?> cls) throws IgniteCheckedException {
+        return optMarshExt != null && optMarshExt.metaSupported(cls);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
index 2583673..ad139a2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
@@ -67,6 +67,13 @@ public interface GridDataInput extends DataInput {
     public int size() throws IOException;
 
     /**
+     * Returns byte array that is backed by this GridDataInput.
+     *
+     * @return Byte array.
+     */
+    public byte[] array();
+
+    /**
      * Resets data output.
      *
      * @throws IOException In case of error.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
index d2ae22d..6be90c5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
@@ -189,6 +189,11 @@ public class GridUnsafeDataInput extends InputStream implements GridDataInput {
         return off;
     }
 
+    /** {@inheritDoc} */
+    @Override public byte[] array() {
+        return buf;
+    }
+
     /**
      * @param more Bytes to move forward.
      * @return Old offset value.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
index 0ef39b1..ae3ae77 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
@@ -34,7 +34,7 @@ import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.EN
 /**
  * Class descriptor.
  */
-class OptimizedClassDescriptor {
+public class OptimizedClassDescriptor {
     /** Unsafe. */
     private static final Unsafe UNSAFE = GridUnsafe.unsafe();
 
@@ -107,9 +107,6 @@ class OptimizedClassDescriptor {
     /** Access order field offset. */
     private long accessOrderFieldOff;
 
-    /** Metadata handler */
-    private OptimizedObjectMetadataHandler metaHandler;
-
     /**
      * Creates descriptor for class.
      *
@@ -118,9 +115,6 @@ class OptimizedClassDescriptor {
      * @param cls Class.
      * @param ctx Context.
      * @param mapper ID mapper.
-     * @param metaHandler Metadata handler.
-     * @param tryEnableMeta Try to enable meta during {@code OptimizedClassDescriptor} registration. Meta is supported,
-     *                      only for objects that support footer injection is their serialized form.
      * @throws IOException In case of error.
      */
     @SuppressWarnings("ForLoopReplaceableByForEach")
@@ -128,16 +122,13 @@ class OptimizedClassDescriptor {
         int typeId,
         ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
         MarshallerContext ctx,
-        OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler,
-        boolean tryEnableMeta)
+        OptimizedMarshallerIdMapper mapper)
         throws IOException {
         this.cls = cls;
         this.typeId = typeId;
         this.clsMap = clsMap;
         this.ctx = ctx;
         this.mapper = mapper;
-        this.metaHandler = metaHandler;
 
         name = cls.getName();
 
@@ -349,7 +340,7 @@ class OptimizedClassDescriptor {
                     List<ClassFields> fields = new ArrayList<>();
                     Set<String> fieldsSet = new HashSet<>();
 
-                    boolean fieldsIndexingEnabled = true;
+                    boolean fieldsIndexingSupported = true;
 
                     for (c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
                         Method mtd;
@@ -361,7 +352,7 @@ class OptimizedClassDescriptor {
 
                             if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE) {
                                 mtd.setAccessible(true);
-                                fieldsIndexingEnabled = false;
+                                fieldsIndexingSupported = false;
                             }
                             else
                                 // Set method back to null if it has incorrect signature.
@@ -380,7 +371,7 @@ class OptimizedClassDescriptor {
 
                             if (!isStatic(mod) && isPrivate(mod) && mtd.getReturnType() == Void.TYPE) {
                                 mtd.setAccessible(true);
-                                fieldsIndexingEnabled = false;
+                                fieldsIndexingSupported = false;
                             }
                             else
                                 // Set method back to null if it has incorrect signature.
@@ -401,7 +392,7 @@ class OptimizedClassDescriptor {
 
                             // Check for fields duplicate names in classes hierarchy
                             if (!fieldsSet.add(f.getName()))
-                                fieldsIndexingEnabled = false;
+                                fieldsIndexingSupported = false;
                         }
 
                         List<FieldInfo> clsFields = new ArrayList<>(clsFields0.length);
@@ -417,7 +408,7 @@ class OptimizedClassDescriptor {
                                 isPrivate(mod) && isStatic(mod) && isFinal(mod)) {
                                 hasSerialPersistentFields = true;
 
-                                fieldsIndexingEnabled = false;
+                                fieldsIndexingSupported = false;
 
                                 serFieldsDesc.setAccessible(true);
 
@@ -488,21 +479,7 @@ class OptimizedClassDescriptor {
                     Collections.reverse(readObjMtds);
                     Collections.reverse(fields);
 
-                    this.fields = new Fields(fields, fieldsIndexingEnabled);
-
-                    if (tryEnableMeta && fieldsIndexingEnabled && metaHandler.metadata(typeId) == null) {
-                        OptimizedObjectMetadata meta = new OptimizedObjectMetadata();
-
-                        for (ClassFields clsFields : this.fields.fields)
-                            for (FieldInfo info : clsFields.fields)
-                                meta.addMeta(info.id(), info.type());
-
-                        U.debug("putting to cache: " + typeId);
-
-                        metaHandler.addMeta(typeId, meta);
-
-                        U.debug("put to cache: " + typeId);
-                    }
+                    this.fields = new Fields(fields, fieldsIndexingSupported);
                 }
             }
         }
@@ -659,8 +636,9 @@ class OptimizedClassDescriptor {
 
             case OBJ_ARR:
                 OptimizedClassDescriptor compDesc = classDescriptor(clsMap,
-                    obj.getClass().getComponentType(), ctx,
-                    mapper, metaHandler, false);
+                    obj.getClass().getComponentType(),
+                    ctx,
+                    mapper);
 
                 compDesc.writeTypeData(out);
 
@@ -719,8 +697,7 @@ class OptimizedClassDescriptor {
                 break;
 
             case CLS:
-                OptimizedClassDescriptor clsDesc = classDescriptor(clsMap, (Class<?>)obj, ctx, mapper, metaHandler,
-                                                       false);
+                OptimizedClassDescriptor clsDesc = classDescriptor(clsMap, (Class<?>)obj, ctx, mapper);
 
                 clsDesc.writeTypeData(out);
 
@@ -827,6 +804,15 @@ class OptimizedClassDescriptor {
     }
 
     /**
+     * Returns class fields.
+     *
+     * @return Fields.
+     */
+    public Fields fields() {
+        return fields;
+    }
+
+    /**
      * @param cls Class.
      * @return Type.
      */
@@ -860,7 +846,7 @@ class OptimizedClassDescriptor {
      * Information about one field.
      */
     @SuppressWarnings("PackageVisibleInnerClass")
-    static class FieldInfo {
+    public static class FieldInfo {
         /** Field. */
         private final Field field;
 
@@ -891,6 +877,20 @@ class OptimizedClassDescriptor {
         }
 
         /**
+         * @return Field ID.
+         */
+        public int id() {
+            return fieldId;
+        }
+
+        /**
+         * @return Type.
+         */
+        public OptimizedFieldType type() {
+            return fieldType;
+        }
+
+        /**
          * @return Returns field.
          */
         Field field() {
@@ -905,31 +905,17 @@ class OptimizedClassDescriptor {
         }
 
         /**
-         * @return Type.
-         */
-        OptimizedFieldType type() {
-            return fieldType;
-        }
-
-        /**
          * @return Name.
          */
         String name() {
             return fieldName;
         }
-
-        /**
-         * @return Field ID.
-         */
-        int id() {
-            return fieldId;
-        }
     }
 
     /**
      * Information about one class.
      */
-    static class ClassFields {
+    public static class ClassFields {
         /** Fields. */
         private final List<FieldInfo> fields;
 
@@ -979,13 +965,22 @@ class OptimizedClassDescriptor {
 
             return nameToIndex.get(name);
         }
+
+        /**
+         * Returns field info list.
+         *
+         * @return Fields info list.
+         */
+        public List<FieldInfo> fieldInfoList() {
+            return fields;
+        }
     }
 
     /**
      * Encapsulates data about class fields.
      */
     @SuppressWarnings("PackageVisibleInnerClass")
-    static class Fields {
+    public static class Fields {
         /** Fields. */
         private final List<ClassFields> fields;
 
@@ -993,16 +988,16 @@ class OptimizedClassDescriptor {
         private final List<Field> ownFields;
 
         /** Fields indexing flag. */
-        private final boolean fieldsIndexingEnabled;
+        private final boolean fieldsIndexingSupported;
 
         /**
          * Creates new instance.
          *
          * @param fields Fields.
          */
-        Fields(List<ClassFields> fields, boolean fieldsIndexingEnabled) {
+        Fields(List<ClassFields> fields, boolean fieldsIndexingSupported) {
             this.fields = fields;
-            this.fieldsIndexingEnabled = fieldsIndexingEnabled;
+            this.fieldsIndexingSupported = fieldsIndexingSupported;
 
             if (fields.isEmpty())
                 ownFields = null;
@@ -1036,21 +1031,21 @@ class OptimizedClassDescriptor {
         }
 
         /**
-         * Whether fields indexing is enabled for a given object or not.
+         * Whether fields indexing is supported for a given object or not.
          *
-         * @return {@code true} if enabled, {@code false} otherwise.
+         * @return {@code true} if supported, {@code false} otherwise.
          */
-        boolean fieldsIndexingEnabled() {
-            return fieldsIndexingEnabled;
+        public boolean fieldsIndexingSupported() {
+            return fieldsIndexingSupported;
         }
 
         /**
-         * Returns a total number of hierarchy levels.
+         * Returns fields list.
          *
-         * @return number of hierarchy levels.
+         * @return Fields list.
          */
-        int hierarchyLevels() {
-            return fields.size();
+        public List<ClassFields> fieldsList() {
+            return fields;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedFieldType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedFieldType.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedFieldType.java
index 3840dfd..0fbf53c 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedFieldType.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedFieldType.java
@@ -20,7 +20,7 @@ package org.apache.ignite.marshaller.optimized;
 /**
  * Field type used to calculate {@code Unsafe} offsets into objects.
  */
-enum OptimizedFieldType {
+public enum OptimizedFieldType {
     /** */
     BYTE,
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
index 4e5b1a7..a5b0cc4 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java
@@ -77,19 +77,16 @@ import java.util.concurrent.*;
  */
 public class OptimizedMarshaller extends AbstractMarshaller {
     /** Default class loader. */
-    private final ClassLoader dfltClsLdr = getClass().getClassLoader();
+    protected final ClassLoader dfltClsLdr = getClass().getClassLoader();
 
     /** Whether or not to require an object to be serializable in order to be marshalled. */
-    private boolean requireSer = true;
+    protected boolean requireSer = true;
 
     /** ID mapper. */
-    private OptimizedMarshallerIdMapper mapper;
-
-    /** Metadata handler. */
-    private OptimizedObjectMetadataHandler metaHandler;
+    protected OptimizedMarshallerIdMapper mapper;
 
     /** Class descriptors by class. */
-    private final ConcurrentMap<Class, OptimizedClassDescriptor> clsMap = new ConcurrentHashMap8<>();
+    protected final ConcurrentMap<Class, OptimizedClassDescriptor> clsMap = new ConcurrentHashMap8<>();
 
     /**
      * Creates new marshaller will all defaults.
@@ -131,20 +128,10 @@ public class OptimizedMarshaller extends AbstractMarshaller {
     }
 
     /**
-     * Sets metadata handler.
-     *
-     * @param metaHandler Metadata handler.
-     */
-    public void setMetadataHandler(OptimizedObjectMetadataHandler metaHandler) {
-        this.metaHandler = metaHandler;
-    }
-
-    /**
      * Specifies size of cached object streams used by marshaller. Object streams are cached for
      * performance reason to avoid costly recreation for every serialization routine. If {@code 0} (default),
      * pool is not used and each thread has its own cached object stream which it keeps reusing.
      * <p>
-     *     
      * Since each stream has an internal buffer, creating a stream for each thread can lead to
      * high memory consumption if many large messages are marshalled or unmarshalled concurrently.
      * Consider using pool in this case. This will limit number of streams that can be created and,
@@ -168,7 +155,7 @@ public class OptimizedMarshaller extends AbstractMarshaller {
         try {
             objOut = OptimizedObjectStreamRegistry.out();
 
-            objOut.context(clsMap, ctx, mapper, metaHandler, requireSer);
+            objOut.context(clsMap, ctx, mapper, requireSer);
 
             objOut.out().outputStream(out);
 
@@ -189,7 +176,7 @@ public class OptimizedMarshaller extends AbstractMarshaller {
         try {
             objOut = OptimizedObjectStreamRegistry.out();
 
-            objOut.context(clsMap, ctx, mapper, metaHandler, requireSer);
+            objOut.context(clsMap, ctx, mapper, requireSer);
 
             objOut.writeObject(obj);
 
@@ -213,7 +200,7 @@ public class OptimizedMarshaller extends AbstractMarshaller {
         try {
             objIn = OptimizedObjectStreamRegistry.in();
 
-            objIn.context(clsMap, ctx, mapper, metaHandler, clsLdr != null ? clsLdr : dfltClsLdr);
+            objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr);
 
             objIn.in().inputStream(in);
 
@@ -242,7 +229,7 @@ public class OptimizedMarshaller extends AbstractMarshaller {
         try {
             objIn = OptimizedObjectStreamRegistry.in();
 
-            objIn.context(clsMap, ctx, mapper, metaHandler, clsLdr != null ? clsLdr : dfltClsLdr);
+            objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr);
 
             objIn.in().bytes(arr, arr.length);
 
@@ -261,54 +248,6 @@ public class OptimizedMarshaller extends AbstractMarshaller {
         }
     }
 
-    //TODO:
-    public <T> T unmarshal(String fieldName, byte[] arr, @Nullable ClassLoader clsLdr) throws IgniteCheckedException {
-        assert arr != null && fieldName != null;
-
-        OptimizedObjectInputStream objIn = null;
-
-        try {
-            objIn = OptimizedObjectStreamRegistry.in();
-
-            objIn.context(clsMap, ctx, mapper, metaHandler, clsLdr != null ? clsLdr : dfltClsLdr);
-
-            objIn.in().bytes(arr, arr.length);
-
-            return (T)objIn.readField(fieldName);
-        }
-        catch (IOException e) {
-            throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e);
-        }
-        catch (ClassNotFoundException e) {
-            throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " +
-                                                 "(make sure same version of all classes are available on all nodes or enable peer-class-loading): " +
-                                                 clsLdr, e);
-        }
-        finally {
-            OptimizedObjectStreamRegistry.closeIn(objIn);
-        }
-    }
-
-    /**
-     * Checks whether a footer injection into a serialized form of the object is supported.
-     * Footer contains information on fields location in the serialized form, thus enabling fast queries without a need
-     * to deserialize the object.
-     *
-     * @param obj Object.
-     * @return {@code true} if the footer is supported.
-     */
-    public boolean footerSupported(Object obj) throws IgniteCheckedException {
-        try {
-            OptimizedClassDescriptor desc = OptimizedMarshallerUtils.classDescriptor(clsMap, obj.getClass(), ctx,
-                mapper, metaHandler, true);
-
-            return  metaHandler.metadata(desc.typeId()) != null;
-        }
-        catch (IOException e) {
-            throw new IgniteCheckedException("Failed to get class descriptor.", e);
-        }
-    }
-
     /**
      * Checks whether {@code GridOptimizedMarshaller} is able to work on the current JVM.
      * <p>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
index 138e732..61cbcee 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerUtils.java
@@ -34,7 +34,7 @@ import java.util.concurrent.*;
 /**
  * Miscellaneous utility methods to facilitate {@link OptimizedMarshaller}.
  */
-class OptimizedMarshallerUtils {
+public class OptimizedMarshallerUtils {
     /** */
     private static final Unsafe UNSAFE = GridUnsafe.unsafe();
 
@@ -141,16 +141,7 @@ class OptimizedMarshallerUtils {
     static final byte EXTERNALIZABLE = 101;
 
     /** */
-    static final byte SERIALIZABLE = 102;
-
-    /** */
-    static final byte EMPTY_FOOTER = -1;
-
-    /** */
-    static final byte FOOTER_LEN_OFF = 4;
-
-    /** */
-    static final byte VARIABLE_LEN = -1;
+    public static final byte SERIALIZABLE = 102;
 
     /** UTF-8 character name. */
     static final Charset UTF_8 = Charset.forName("UTF-8");
@@ -180,19 +171,14 @@ class OptimizedMarshallerUtils {
      * @param cls Class.
      * @param ctx Context.
      * @param mapper ID mapper.
-     * @param metaHandler Metadata handler.
-     * @param tryEnableMeta Try to enable meta during {@code OptimizedClassDescriptor} registration. Meta is supported,
-     *                      only for objects that support footer injection is their serialized form.
      * @return Descriptor.
      * @throws IOException In case of error.
      */
-    static OptimizedClassDescriptor classDescriptor(
+    public static OptimizedClassDescriptor classDescriptor(
         ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
         Class cls,
         MarshallerContext ctx,
-        OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler,
-        boolean tryEnableMeta)
+        OptimizedMarshallerIdMapper mapper)
         throws IOException
     {
         OptimizedClassDescriptor desc = clsMap.get(cls);
@@ -209,8 +195,7 @@ class OptimizedMarshallerUtils {
                 throw new IOException("Failed to register class: " + cls.getName(), e);
             }
 
-            desc = new OptimizedClassDescriptor(cls, registered ? typeId : 0, clsMap, ctx, mapper, metaHandler,
-                       tryEnableMeta);
+            desc = new OptimizedClassDescriptor(cls, registered ? typeId : 0, clsMap, ctx, mapper);
 
             if (registered) {
                 OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc);
@@ -247,7 +232,7 @@ class OptimizedMarshallerUtils {
      * @param fieldName Field name.
      * @return Field ID.
      */
-    static int resolveFieldId(String fieldName) {
+    public static int resolveFieldId(String fieldName) {
         return fieldName.hashCode();
     }
 
@@ -259,7 +244,6 @@ class OptimizedMarshallerUtils {
      * @param ldr Class loader.
      * @param ctx Context.
      * @param mapper ID mapper.
-     * @param metaHandler Metadata handler.
      * @return Descriptor.
      * @throws IOException In case of error.
      * @throws ClassNotFoundException If class was not found.
@@ -269,8 +253,7 @@ class OptimizedMarshallerUtils {
         int id,
         ClassLoader ldr,
         MarshallerContext ctx,
-        OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler) throws IOException, ClassNotFoundException {
+        OptimizedMarshallerIdMapper mapper) throws IOException, ClassNotFoundException {
         Class cls;
 
         try {
@@ -284,8 +267,7 @@ class OptimizedMarshallerUtils {
 
         if (desc == null) {
             OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc =
-                new OptimizedClassDescriptor(cls, resolveTypeId(cls.getName(), mapper), clsMap, ctx, mapper,
-                    metaHandler, false));
+                new OptimizedClassDescriptor(cls, resolveTypeId(cls.getName(), mapper), clsMap, ctx, mapper));
 
             if (old != null)
                 desc = old;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
index 70a2f3f..7d1cad0 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
@@ -34,7 +34,7 @@ import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*;
 /**
  * Optimized object input stream.
  */
-class OptimizedObjectInputStream extends ObjectInputStream {
+public class OptimizedObjectInputStream extends ObjectInputStream {
     /** Unsafe. */
     private static final Unsafe UNSAFE = GridUnsafe.unsafe();
 
@@ -42,19 +42,19 @@ class OptimizedObjectInputStream extends ObjectInputStream {
     private static final Object DUMMY = new Object();
 
     /** */
-    private final HandleTable handles = new HandleTable(10);
+    protected MarshallerContext ctx;
 
     /** */
-    private MarshallerContext ctx;
+    protected OptimizedMarshallerIdMapper mapper;
 
     /** */
-    private OptimizedMarshallerIdMapper mapper;
+    protected ClassLoader clsLdr;
 
     /** */
-    private ClassLoader clsLdr;
+    protected GridDataInput in;
 
     /** */
-    private GridDataInput in;
+    protected ConcurrentMap<Class, OptimizedClassDescriptor> clsMap;
 
     /** */
     private Object curObj;
@@ -66,16 +66,13 @@ class OptimizedObjectInputStream extends ObjectInputStream {
     private Class<?> curCls;
 
     /** */
-    private ConcurrentMap<Class, OptimizedClassDescriptor> clsMap;
-
-    /** */
-    private OptimizedObjectMetadataHandler metaHandler;
+    private final HandleTable handles = new HandleTable(10);
 
     /**
      * @param in Input.
      * @throws IOException In case of error.
      */
-    OptimizedObjectInputStream(GridDataInput in) throws IOException {
+    protected OptimizedObjectInputStream(GridDataInput in) throws IOException {
         this.in = in;
     }
 
@@ -83,21 +80,18 @@ class OptimizedObjectInputStream extends ObjectInputStream {
      * @param clsMap Class descriptors by class map.
      * @param ctx Context.
      * @param mapper ID mapper.
-     * @param metaHandler Metadata handler.
      * @param clsLdr Class loader.
      */
-    void context(
+    protected void context(
         ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
         MarshallerContext ctx,
         OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler,
         ClassLoader clsLdr)
     {
         this.clsMap = clsMap;
         this.ctx = ctx;
         this.mapper = mapper;
         this.clsLdr = clsLdr;
-        this.metaHandler = metaHandler;
     }
 
     /**
@@ -250,8 +244,8 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                 int typeId = readInt();
 
                 OptimizedClassDescriptor desc = typeId == 0 ?
-                    classDescriptor(clsMap, U.forName(readUTF(), clsLdr), ctx, mapper, metaHandler, false):
-                    classDescriptor(clsMap, typeId, clsLdr, ctx, mapper, metaHandler);
+                    classDescriptor(clsMap, U.forName(readUTF(), clsLdr), ctx, mapper):
+                    classDescriptor(clsMap, typeId, clsLdr, ctx, mapper);
 
                 curCls = desc.describedClass();
 
@@ -281,7 +275,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
         int compTypeId = readInt();
 
         return compTypeId == 0 ? U.forName(readUTF(), clsLdr) :
-            classDescriptor(clsMap, compTypeId, clsLdr, ctx, mapper, metaHandler).describedClass();
+            classDescriptor(clsMap, compTypeId, clsLdr, ctx, mapper).describedClass();
     }
 
     /**
@@ -358,7 +352,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
 
             switch ((t.type())) {
                 case BYTE:
-                    readByte(); //type
+                    readFieldType();
 
                     byte resByte = readByte();
 
@@ -368,7 +362,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case SHORT:
-                    readByte(); //type
+                    readFieldType();
 
                     short resShort = readShort();
 
@@ -378,7 +372,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case INT:
-                    readByte(); //type
+                    readFieldType();
 
                     int resInt = readInt();
 
@@ -388,7 +382,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case LONG:
-                    readByte(); //type
+                    readFieldType();
 
                     long resLong = readLong();
 
@@ -398,7 +392,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case FLOAT:
-                    readByte(); //type
+                    readFieldType();
 
                     float resFloat = readFloat();
 
@@ -408,7 +402,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case DOUBLE:
-                    readByte(); //type
+                    readFieldType();
 
                     double resDouble = readDouble();
 
@@ -418,7 +412,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case CHAR:
-                    readByte(); //type
+                    readFieldType();
 
                     char resChar = readChar();
 
@@ -428,7 +422,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
                     break;
 
                 case BOOLEAN:
-                    readByte(); //type
+                    readFieldType();
 
                     boolean resBoolean = readBoolean();
 
@@ -540,12 +534,7 @@ class OptimizedObjectInputStream extends ObjectInputStream {
             }
         }
 
-        if (metaHandler.metadata(resolveTypeId(cls.getName(), mapper)) != null) {
-            int footerLen = in.readInt();
-
-            if (footerLen != EMPTY_FOOTER)
-                in.skipBytes(footerLen - 4);
-        }
+        readFooter(cls);
 
         return obj;
     }
@@ -946,126 +935,34 @@ class OptimizedObjectInputStream extends ObjectInputStream {
         return new GetFieldImpl(this);
     }
 
-    /** {@inheritDoc} */
-    @Override public void registerValidation(ObjectInputValidation obj, int pri) {
-        // No-op.
-    }
-
-    /** {@inheritDoc} */
-    @Override public int available() throws IOException {
-        return -1;
-    }
-
     /**
-     * Checks whether the object has a field with name {@code fieldName}.
+     * Reads object footer from the underlying stream.
      *
-     * @param fieldName Field name.
-     * @return {@code true} if field exists, {@code false} otherwise.
-     * @throws IOException in case of error.
-     */
-    public boolean hasField(String fieldName) throws IOException {
-        int pos = in.position();
-
-        // TODO: IGNITE-950, do we need move to start position?
-        if (in.readByte() != SERIALIZABLE) {
-            in.position(pos);
-            return false;
-        }
-
-        FieldRange range = fieldRange(fieldName);
-
-        in.position(pos);
-
-        return range != null && range.start > 0;
-    }
-
-    /**
-     * TODO: IGNITE-950
-     * @param fieldName
-     * @return
-     * @throws IOException
-     * @throws ClassNotFoundException
+     * @param cls Class.
+     * @throws IOException In case of error.
      */
-    Object readField(String fieldName) throws IOException, ClassNotFoundException {
-        int pos = in.position();
-
-        // TODO: IGNITE-950, do we need move to start position?
-        if (in.readByte() != SERIALIZABLE) {
-            in.position(pos);
-            return false;
-        }
-
-        FieldRange range = fieldRange(fieldName);
-
-        Object obj = null;
-
-        if (range != null && range.start > 0) {
-            in.position(range.start);
-            obj = readObject();
-        }
-
-        in.position(pos);
-
-        return obj;
+    protected void readFooter(Class<?> cls) throws IOException {
+        // No-op
     }
 
     /**
-     * Returns field offset in the byte stream.
+     * Reads field's type during its deserialization.
      *
-     * @param fieldName Field name.
-     * @return positive range or {@code null} if the object doesn't have such a field.
-     * @throws IOException in case of error.
+     * @return Field type.
+     * @throws IOException In case of error.
      */
-    private FieldRange fieldRange(String fieldName) throws IOException {
-        int fieldId = resolveFieldId(fieldName);
-
-        int typeId = readInt();
-
-        int clsNameLen = 0;
-
-        if (typeId == 0) {
-            int pos = in.position();
-
-            typeId = OptimizedMarshallerUtils.resolveTypeId(readUTF(), mapper);
-
-            clsNameLen = in.position() - pos;
-        }
-
-        OptimizedObjectMetadata meta = metaHandler.metadata(typeId);
-
-        if (meta == null)
-            // TODO: IGNITE-950 add warning!
-            return null;
-
-        int end = in.size();
-
-        in.position(end - FOOTER_LEN_OFF);
-
-        int footerLen = in.readInt();
-
-        if (footerLen == EMPTY_FOOTER)
-            return null;
-
-        // 4 - skipping length at the beginning
-        int footerOff = (end - footerLen) + 4;
-        in.position(footerOff);
-
-        int fieldOff = 0;
-
-        for (OptimizedObjectMetadata.FieldInfo info : meta.getMeta()) {
-            if (info.id == fieldId) {
-                //object header len: 1 - for type, 4 - for type ID, 2 - for checksum.
-                fieldOff += 1 + 4 + clsNameLen + 2;
-
-                FieldRange range = new FieldRange(fieldOff, info.len == VARIABLE_LEN ? in.readShort() : info.len);
+    protected int readFieldType() throws IOException {
+        return 0;
+    }
 
-                return range;
-            }
-            else
-                fieldOff += info.len == VARIABLE_LEN ? in.readShort() : info.len;
-        }
+    /** {@inheritDoc} */
+    @Override public void registerValidation(ObjectInputValidation obj, int pri) {
+        // No-op.
+    }
 
-        return null;
+    /** {@inheritDoc} */
+    @Override public int available() throws IOException {
+        return -1;
     }
 
     /**
@@ -1185,56 +1082,56 @@ class OptimizedObjectInputStream extends ObjectInputStream {
 
                 switch (t.type()) {
                     case BYTE:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readByte();
 
                         break;
 
                     case SHORT:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readShort();
 
                         break;
 
                     case INT:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readInt();
 
                         break;
 
                     case LONG:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readLong();
 
                         break;
 
                     case FLOAT:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readFloat();
 
                         break;
 
                     case DOUBLE:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readDouble();
 
                         break;
 
                     case CHAR:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readChar();
 
                         break;
 
                     case BOOLEAN:
-                        in.readByte(); //type
+                        in.readFieldType();
 
                         obj = in.readBoolean();
 
@@ -1313,24 +1210,4 @@ class OptimizedObjectInputStream extends ObjectInputStream {
             return objs[fieldInfo.getIndex(name)] != null ? (T)objs[fieldInfo.getIndex(name)] : dflt;
         }
     }
-
-    /**
-     *
-     */
-    private static class FieldRange {
-        /** */
-        private int start;
-
-        /** */
-        private int len;
-
-        /**
-         * @param start Start.
-         * @param len   Length.
-         */
-        public FieldRange(int start, int len) {
-            this.start = start;
-            this.len = len;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
deleted file mode 100644
index 5b476da..0000000
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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.marshaller.optimized;
-
-import org.apache.ignite.*;
-
-import java.io.*;
-import java.util.*;
-
-import static org.apache.ignite.marshaller.optimized.OptimizedFieldType.*;
-/**
- * Metadata that keeps fields information. Used in conjunction with the footer that is added to some objects during
- * marshalling.
- */
-public class OptimizedObjectMetadata implements Externalizable {
-    /** */
-    private List<FieldInfo> fieldsInfo;
-
-    /** Constructor. */
-    public OptimizedObjectMetadata() {
-        // No-op
-    }
-
-    /**
-     * Adds meta for a new field.
-     *
-     * @param fieldId Field ID.
-     * @param fieldType Field type.
-     */
-    public void addMeta(int fieldId, OptimizedFieldType fieldType) {
-        if (fieldsInfo == null)
-            fieldsInfo = new ArrayList<>();
-
-        int len = 1;
-
-        switch (fieldType) {
-            case BYTE:
-            case BOOLEAN:
-                len += 1;
-                break;
-
-            case SHORT:
-            case CHAR:
-                len += 2;
-                break;
-
-            case INT:
-            case FLOAT:
-                len += 4;
-                break;
-
-            case LONG:
-            case DOUBLE:
-                len += 8;
-                break;
-
-            case OTHER:
-                len = OptimizedMarshallerUtils.VARIABLE_LEN;
-                break;
-
-            default:
-                throw new IgniteException("Unknown field type: " + fieldType);
-        }
-
-        assert len != 1;
-
-        fieldsInfo.add(new FieldInfo(fieldId, len));
-    }
-
-    /**
-     * Gets {@link org.apache.ignite.marshaller.optimized.OptimizedObjectMetadata.FieldInfo} at the {@code index}.
-     *
-     * @param index Position.
-     * @return Field meta info.
-     */
-    public FieldInfo getMeta(int index) {
-        return fieldsInfo.get(index);
-    }
-    /**
-     * Returns all the metadata stored for the object.
-     *
-     * @return Metadata collection.
-     */
-    public List<FieldInfo> getMeta() {
-        return Collections.unmodifiableList(fieldsInfo);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void writeExternal(ObjectOutput out) throws IOException {
-        if (fieldsInfo == null) {
-            out.writeInt(0);
-            return;
-        }
-
-        out.writeInt(fieldsInfo.size());
-
-        for (FieldInfo fieldInfo : fieldsInfo) {
-            out.writeInt(fieldInfo.id);
-            out.writeInt(fieldInfo.len);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-        int size = in.readInt();
-
-        fieldsInfo = new ArrayList<>(size);
-
-        for (int i = 0; i < size; i++)
-            fieldsInfo.add(new FieldInfo(in.readInt(), in.readInt()));
-    }
-
-    /**
-     * Field info.
-     */
-    public static class FieldInfo {
-        /** Field ID. */
-        int id;
-
-        /** Field type. */
-        int len;
-
-        /**
-         * Constructor.
-         *
-         * @param id Field ID.
-         * @param len Field len.
-         */
-        public FieldInfo(int id, int len) {
-            this.id = id;
-            this.len = len;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataHandler.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataHandler.java
deleted file mode 100644
index a5d54dd..0000000
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataHandler.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.marshaller.optimized;
-
-/**
- * Metadata handler for optimized objects.
- */
-public interface OptimizedObjectMetadataHandler {
-    /**
-     * Adds meta data.
-     *
-     * @param typeId Type ID.
-     * @param meta Meta data.
-     */
-    void addMeta(int typeId, OptimizedObjectMetadata meta);
-
-
-    /**
-     * Gets meta data for provided type ID.
-     *
-     * @param typeId Type ID.
-     * @return Meta data.
-     */
-    OptimizedObjectMetadata metadata(int typeId);
-}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataKey.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataKey.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataKey.java
deleted file mode 100644
index ee85754..0000000
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadataKey.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.marshaller.optimized;
-
-import org.apache.ignite.internal.processors.cache.*;
-
-import java.io.*;
-
-/**
- * Optimized object metadata key.
- */
-public class OptimizedObjectMetadataKey extends GridCacheUtilityKey<OptimizedObjectMetadataKey>
-    implements Externalizable {
-    /** */
-    private static final long serialVersionUID = 0L;
-
-    /** */
-    private int typeId;
-
-    /**
-     * For {@link Externalizable}.
-     */
-    public OptimizedObjectMetadataKey() {
-        // No-op
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param typeId Type id.
-     */
-    public OptimizedObjectMetadataKey(int typeId) {
-        this.typeId = typeId;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void writeExternal(ObjectOutput out) throws IOException {
-        out.writeInt(typeId);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-        typeId = in.readInt();
-    }
-
-    /** {@inheritDoc} */
-    @Override protected boolean equalsx(OptimizedObjectMetadataKey key) {
-        return typeId == key.typeId;
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hashCode() {
-        return typeId;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
index 05d3e3d..a88da95 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
@@ -34,7 +34,7 @@ import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*;
 /**
  * Optimized object output stream.
  */
-class OptimizedObjectOutputStream extends ObjectOutputStream {
+public class OptimizedObjectOutputStream extends ObjectOutputStream {
     /** */
     private static final Collection<String> CONVERTED_ERR = F.asList(
         "weblogic/management/ManagementException",
@@ -43,19 +43,22 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     );
 
     /** */
-    private final GridHandleTable handles = new GridHandleTable(10, 3.00f);
+    protected final GridDataOutput out;
 
     /** */
-    private final GridDataOutput out;
+    protected MarshallerContext ctx;
 
     /** */
-    private MarshallerContext ctx;
+    protected OptimizedMarshallerIdMapper mapper;
 
     /** */
-    private OptimizedMarshallerIdMapper mapper;
+    protected ConcurrentMap<Class, OptimizedClassDescriptor> clsMap;
 
     /** */
-    private boolean requireSer;
+    protected boolean requireSer;
+
+    /** */
+    private final GridHandleTable handles = new GridHandleTable(10, 3.00f);
 
     /** */
     private Object curObj;
@@ -69,17 +72,11 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     /** */
     private PutFieldImpl curPut;
 
-    /** */
-    private ConcurrentMap<Class, OptimizedClassDescriptor> clsMap;
-
-    /** */
-    private OptimizedObjectMetadataHandler metaHandler;
-
     /**
      * @param out Output.
      * @throws IOException In case of error.
      */
-    OptimizedObjectOutputStream(GridDataOutput out) throws IOException {
+    protected OptimizedObjectOutputStream(GridDataOutput out) throws IOException {
         this.out = out;
     }
 
@@ -87,18 +84,15 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
      * @param clsMap Class descriptors by class map.
      * @param ctx Context.
      * @param mapper ID mapper.
-     * @param metaHandler Metadata handler.
      * @param requireSer Require {@link Serializable} flag.
      */
-    void context(ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
+    protected void context(ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
         MarshallerContext ctx,
         OptimizedMarshallerIdMapper mapper,
-        OptimizedObjectMetadataHandler metaHandler,
         boolean requireSer) {
         this.clsMap = clsMap;
         this.ctx = ctx;
         this.mapper = mapper;
-        this.metaHandler = metaHandler;
         this.requireSer = requireSer;
     }
 
@@ -193,9 +187,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                     clsMap,
                     obj instanceof Object[] ? Object[].class : obj.getClass(),
                     ctx,
-                    mapper,
-                    metaHandler,
-                    false);
+                    mapper);
 
                 if (desc.excluded()) {
                     writeByte(NULL);
@@ -220,9 +212,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                     desc = classDescriptor(clsMap,
                         obj instanceof Object[] ? Object[].class : obj.getClass(),
                         ctx,
-                        mapper,
-                        metaHandler,
-                        false);
+                        mapper);
                 }
 
                 if (handle >= 0) {
@@ -319,11 +309,10 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     @SuppressWarnings("ForLoopReplaceableByForEach")
     void writeSerializable(Object obj, List<Method> mtds, OptimizedClassDescriptor.Fields fields, int headerPos)
         throws IOException {
-        Footer footer = null;
-
-        if (metaHandler.metadata(resolveTypeId(obj.getClass().getName(), mapper)) != null) {
-            footer = new Footer(fields);
+        Footer footer = createFooter(obj.getClass());
 
+        if (footer != null) {
+            footer.fields(fields);
             footer.headerPos(headerPos);
         }
 
@@ -494,7 +483,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
             switch (t.type()) {
                 case BYTE:
                     if (t.field() != null) {
-                        writeByte(BYTE);
+                        writeFieldType(BYTE);
                         writeByte(getByte(obj, t.offset()));
                     }
 
@@ -502,7 +491,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case SHORT:
                     if (t.field() != null) {
-                        writeByte(SHORT);
+                        writeFieldType(SHORT);
                         writeShort(getShort(obj, t.offset()));
                     }
 
@@ -510,7 +499,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case INT:
                     if (t.field() != null) {
-                        writeByte(INT);
+                        writeFieldType(INT);
                         writeInt(getInt(obj, t.offset()));
                     }
 
@@ -518,7 +507,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case LONG:
                     if (t.field() != null) {
-                        writeByte(LONG);
+                        writeFieldType(LONG);
                         writeLong(getLong(obj, t.offset()));
                     }
 
@@ -526,7 +515,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case FLOAT:
                     if (t.field() != null) {
-                        writeByte(FLOAT);
+                        writeFieldType(FLOAT);
                         writeFloat(getFloat(obj, t.offset()));
                     }
 
@@ -534,7 +523,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case DOUBLE:
                     if (t.field() != null) {
-                        writeByte(DOUBLE);
+                        writeFieldType(DOUBLE);
                         writeDouble(getDouble(obj, t.offset()));
                     }
 
@@ -542,7 +531,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case CHAR:
                     if (t.field() != null) {
-                        writeByte(CHAR);
+                        writeFieldType(CHAR);
                         writeChar(getChar(obj, t.offset()));
                     }
 
@@ -550,7 +539,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
                 case BOOLEAN:
                     if (t.field() != null) {
-                        writeByte(BOOLEAN);
+                        writeFieldType(BOOLEAN);
                         writeBoolean(getBoolean(obj, t.offset()));
                     }
 
@@ -561,7 +550,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                         int handle = writeObject0(getObject(obj, t.offset()));
 
                         if (footer != null && handle >= 0)
-                            footer.disable();
+                            footer.putHandle(t.id(), handle);
                     }
             }
 
@@ -770,49 +759,49 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
 
             switch (t.get1().type()) {
                 case BYTE:
-                    writeByte(BYTE);
+                    writeFieldType(BYTE);
                     writeByte((Byte)t.get2());
 
                     break;
 
                 case SHORT:
-                    writeByte(SHORT);
+                    writeFieldType(SHORT);
                     writeShort((Short)t.get2());
 
                     break;
 
                 case INT:
-                    writeByte(INT);
+                    writeFieldType(INT);
                     writeInt((Integer)t.get2());
 
                     break;
 
                 case LONG:
-                    writeByte(LONG);
+                    writeFieldType(LONG);
                     writeLong((Long)t.get2());
 
                     break;
 
                 case FLOAT:
-                    writeByte(FLOAT);
+                    writeFieldType(FLOAT);
                     writeFloat((Float)t.get2());
 
                     break;
 
                 case DOUBLE:
-                    writeByte(DOUBLE);
+                    writeFieldType(DOUBLE);
                     writeDouble((Double)t.get2());
 
                     break;
 
                 case CHAR:
-                    writeByte(CHAR);
+                    writeFieldType(CHAR);
                     writeChar((Character)t.get2());
 
                     break;
 
                 case BOOLEAN:
-                    writeByte(BOOLEAN);
+                    writeFieldType(BOOLEAN);
                     writeBoolean((Boolean)t.get2());
 
                     break;
@@ -821,7 +810,7 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
                     int handle = writeObject0(t.get2());
 
                     if (footer != null && handle >= 0)
-                        footer.disable();
+                        footer.putHandle(t.get1().id(), handle);
             }
 
             if (footer != null) {
@@ -853,6 +842,26 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     }
 
     /**
+     * Writes field's type to an OutputStream during field serialization.
+     *
+     * @param type Field type.
+     * @throws IOException If error.
+     */
+    protected void writeFieldType(byte type) throws IOException {
+        // No-op
+    }
+
+    /**
+     * Creates new instance of {@code Footer}.
+     *
+     * @param cls Class.
+     * @return {@code Footer} instance.
+     */
+    protected Footer createFooter(Class<?> cls) {
+        return null;
+    }
+
+    /**
      * Returns objects that were added to handles table.
      * Used ONLY for test purposes.
      *
@@ -960,79 +969,44 @@ class OptimizedObjectOutputStream extends ObjectOutputStream {
     /**
      *
      */
-    private class Footer {
-        /** */
-        private ArrayList<Short> data;
-
-        /** */
-        private int headerPos;
-
+    protected interface Footer {
         /**
-         * Constructor.
+         * Sets fields.
          *
          * @param fields Fields.
          */
-        private Footer(OptimizedClassDescriptor.Fields fields) {
-            if (fields.fieldsIndexingEnabled())
-                data = new ArrayList<>();
-            else
-                data = null;
-        }
+        void fields(OptimizedClassDescriptor.Fields fields);
 
         /**
          * Sets field's header absolute position.
          *
          * @param pos Absolute position.
          */
-        private void headerPos(int pos) {
-            headerPos = pos;
-        }
+        void headerPos(int pos);
 
         /**
          * Puts type ID and its value len to the footer.
          *
-         * @param fieldId Field ID.
+         * @param fieldId   Field ID.
          * @param fieldType Field type.
-         * @param len Total number of bytes occupied by type's value.
+         * @param len       Total number of bytes occupied by type's value.
          */
-        private void put(int fieldId, OptimizedFieldType fieldType, int len) {
-            if (data == null)
-                return;
+        void put(int fieldId, OptimizedFieldType fieldType, int len);
 
-            // Considering that field's length will be no longer 2^15 (32 MB)
-            if (fieldType == OptimizedFieldType.OTHER)
-                data.add((short)len);
-        }
 
         /**
-         * Disable footer and indexing for the given Object.
+         * Puts handle ID for the given field ID.
+         *
+         * @param fieldId Field ID.
+         * @param handleId Handle ID.
          */
-        private void disable() {
-            data = null;
-        }
+        void putHandle(int fieldId, int handleId);
 
         /**
          * Writes footer content to the OutputStream.
          *
          * @throws IOException In case of error.
          */
-        private void write() throws IOException {
-            if (data == null)
-                writeInt(EMPTY_FOOTER);
-            else {
-                //12 - 4 bytes for len at the beginning, 4 bytes for len at the end, 4 bytes for object len.
-                int footerLen = data.size() * 2 + 12;
-
-                writeInt(footerLen);
-
-                for (short fieldLen : data)
-                    writeShort(fieldLen);
-
-                // object total len
-                writeInt((out.size() - headerPos) + 8);
-
-                writeInt(footerLen);
-            }
-        }
+        void write() throws IOException;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExt.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExt.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExt.java
new file mode 100644
index 0000000..8636566
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExt.java
@@ -0,0 +1,291 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.*;
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.marshaller.optimized.*;
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+
+import static org.apache.ignite.marshaller.optimized.OptimizedClassDescriptor.*;
+import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*;
+
+/**
+ * TODO
+ */
+public class OptimizedMarshallerExt extends OptimizedMarshaller {
+    /** */
+    static final byte EMPTY_FOOTER = -1;
+
+    /** */
+    static final byte FOOTER_LEN_OFF = 4;
+
+    /** */
+    static final byte VARIABLE_LEN = -1;
+
+    /** */
+    private OptimizedMarshallerExtMetaHandler metaHandler;
+
+    /**
+     * Creates new marshaller providing whether it should
+     * require {@link Serializable} interface or not.
+     *
+     * @param requireSer Whether to require {@link Serializable}.
+     */
+    public OptimizedMarshallerExt(boolean requireSer) {
+        super(requireSer);
+    }
+
+    /**
+     * Sets metadata handler.
+     *
+     * @param metaHandler Metadata handler.
+     */
+    public void setMetadataHandler(OptimizedMarshallerExtMetaHandler metaHandler) {
+        this.metaHandler = metaHandler;
+    }
+
+    /**
+     * Stores metadata information for the given {@link Class}. If the metadata is stored then a footer will be added
+     * during marshalling of an object of the given {@link Class} to the end of its serialized form.
+     *
+     * @param cls Class.
+     * @return {@code true} if the metadata has been added successfully and the footer will be written to the end of
+     * Object's serialized form.
+     * @throws IgniteCheckedException In case of error.
+     */
+    public boolean putMetaForClass(Class<?> cls) throws IgniteCheckedException {
+        assert metaHandler != null;
+
+        try {
+            OptimizedClassDescriptor desc = OptimizedMarshallerUtils.classDescriptor(clsMap, cls, ctx, mapper);
+
+            if (desc.fields().fieldsIndexingSupported()) {
+                if (metaHandler.metadata(desc.typeId()) != null)
+                    return true;
+
+                OptimizedObjectMetadata meta = new OptimizedObjectMetadata();
+
+                for (ClassFields clsFields : desc.fields().fieldsList())
+                    for (FieldInfo info : clsFields.fieldInfoList())
+                        meta.addMeta(info.id(), info.type());
+
+                metaHandler.addMeta(desc.typeId(), meta);
+
+                return true;
+            }
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to put meta for class: " + cls.getName(), e);
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether a metadata is stored for the given {@link Class}. If it's stored then a footer is injected into
+     * a serialized form of the object of this {@link Class}.
+     * Footer contains information on fields location in the serialized form, thus enabling fast queries without a need
+     * to deserialize the object.
+     *
+     * @param cls Class.
+     * @return {@code true} if the metadata exists and the footer will be written to the end of Object's serialized
+     * form.
+     */
+    public boolean metaSupported(Class<?> cls) throws IgniteCheckedException {
+        return  metaHandler.metadata(resolveTypeId(cls.getName(), mapper)) != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void marshal(@Nullable Object obj, OutputStream out) throws IgniteCheckedException {
+        assert out != null;
+
+        OptimizedObjectOutputStreamExt objOut = null;
+
+        try {
+            objOut = OptimizedObjectStreamExtRegistry.out();
+
+            objOut.context(clsMap, ctx, mapper, requireSer, metaHandler);
+
+            objOut.out().outputStream(out);
+
+            objOut.writeObject(obj);
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to serialize object: " + obj, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeOut(objOut);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] marshal(@Nullable Object obj) throws IgniteCheckedException {
+        OptimizedObjectOutputStreamExt objOut = null;
+
+        try {
+            objOut = OptimizedObjectStreamExtRegistry.out();
+
+            objOut.context(clsMap, ctx, mapper, requireSer, metaHandler);
+
+            objOut.writeObject(obj);
+
+            return objOut.out().array();
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to serialize object: " + obj, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeOut(objOut);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T> T unmarshal(InputStream in, @Nullable ClassLoader clsLdr) throws IgniteCheckedException {
+        assert in != null;
+
+        OptimizedObjectInputStreamExt objIn = null;
+
+        try {
+            objIn = OptimizedObjectStreamExtRegistry.in();
+
+            objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr, metaHandler);
+
+            objIn.in().inputStream(in);
+
+            return (T)objIn.readObject();
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e);
+        }
+        catch (ClassNotFoundException e) {
+            throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " +
+                                             "(make sure same versions of all classes are available on all nodes or " +
+                                             "enable peer-class-loading): " + clsLdr, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeIn(objIn);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T> T unmarshal(byte[] arr, @Nullable ClassLoader clsLdr) throws IgniteCheckedException {
+        assert arr != null;
+
+        OptimizedObjectInputStreamExt objIn = null;
+
+        try {
+            objIn = OptimizedObjectStreamExtRegistry.in();
+
+            objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr, metaHandler);
+
+            objIn.in().bytes(arr, arr.length);
+
+            return (T)objIn.readObject();
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e);
+        }
+        catch (ClassNotFoundException e) {
+            throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " +
+                                             "(make sure same version of all classes are available on all nodes or" +
+                                             " enable peer-class-loading): " + clsLdr, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeIn(objIn);
+        }
+    }
+
+    /**
+     * Checks whether object, serialized to byte array {@code arr}, has a field with name {@code fieldName}.
+     *
+     * @param fieldName Field name.
+     * @param arr Object's serialized form.
+     * @param off Object's start off.
+     * @param len Object's len.
+     * @return {@code true} if field exists.
+     */
+    public boolean hasField(String fieldName, byte[] arr, int off, int len) throws IgniteCheckedException {
+        assert arr != null && fieldName != null;
+
+        OptimizedObjectInputStreamExt objIn = null;
+
+        try {
+            objIn = OptimizedObjectStreamExtRegistry.in();
+
+            objIn.context(clsMap, ctx, mapper, dfltClsLdr, metaHandler);
+
+            objIn.in().bytes(arr, off, len);
+
+            return objIn.hasField(fieldName);
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to find field with name: " + fieldName, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeIn(objIn);
+        }
+    }
+
+    /**
+     * Looks up field with the given name and returns it in one of the following representations. If the field is
+     * serializable and has a footer then it's not deserialized but rather returned wrapped by {@link CacheObjectImpl}
+     * for future processing. In all other cases the field is fully deserialized.
+     *
+     * @param fieldName Field name.
+     * @param arr Object's serialized form.
+     * @param off Object's start offset.
+     * @param len Object's len.
+     * @param clsLdr Class loader.
+     * @param <T> Expected field class.
+     * @return Field.
+     * @throws IgniteCheckedException In case of error.
+     */
+    public <T> T readField(String fieldName, byte[] arr, int off, int len, @Nullable ClassLoader clsLdr)
+        throws IgniteCheckedException {
+
+        assert arr != null && fieldName != null;
+
+        OptimizedObjectInputStreamExt objIn = null;
+
+        try {
+            objIn = OptimizedObjectStreamExtRegistry.in();
+
+            objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr, metaHandler);
+
+            objIn.in().bytes(arr, off, len);
+
+            return objIn.readField(fieldName);
+        }
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to find field with name: " + fieldName, e);
+        }
+        catch (ClassNotFoundException e) {
+            throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " +
+                                             "(make sure same version of all classes are available on all nodes or" +
+                                             " enable peer-class-loading): " + clsLdr, e);
+        }
+        finally {
+            OptimizedObjectStreamExtRegistry.closeIn(objIn);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtMetaHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtMetaHandler.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtMetaHandler.java
new file mode 100644
index 0000000..ea3b70f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedMarshallerExtMetaHandler.java
@@ -0,0 +1,40 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+/**
+ * Metadata handler for optimized objects.
+ */
+public interface OptimizedMarshallerExtMetaHandler {
+    /**
+     * Adds meta data.
+     *
+     * @param typeId Type ID.
+     * @param meta Meta data.
+     */
+    void addMeta(int typeId, OptimizedObjectMetadata meta);
+
+
+    /**
+     * Gets meta data for provided type ID.
+     *
+     * @param typeId Type ID.
+     * @return Meta data.
+     */
+    OptimizedObjectMetadata metadata(int typeId);
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/9e2943db/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectInputStreamExt.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectInputStreamExt.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectInputStreamExt.java
new file mode 100644
index 0000000..e3af580
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/ext/OptimizedObjectInputStreamExt.java
@@ -0,0 +1,212 @@
+/*
+ * 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.marshaller.optimized.ext;
+
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.internal.util.io.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.marshaller.optimized.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import static org.apache.ignite.marshaller.optimized.ext.OptimizedMarshallerExt.*;
+import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*;
+
+
+/**
+ * TODO: IGNITE-950
+ */
+public class OptimizedObjectInputStreamExt extends OptimizedObjectInputStream {
+    /** */
+    private OptimizedMarshallerExtMetaHandler metaHandler;
+
+    /** {@inheritDoc} */
+    public OptimizedObjectInputStreamExt(GridDataInput in) throws IOException {
+        super(in);
+    }
+
+    /**
+     * @param clsMap Class descriptors by class map.
+     * @param ctx Context.
+     * @param mapper ID mapper.
+     * @param clsLdr Class loader.
+     */
+    protected void context(ConcurrentMap<Class, OptimizedClassDescriptor> clsMap, MarshallerContext ctx,
+        OptimizedMarshallerIdMapper mapper, ClassLoader clsLdr, OptimizedMarshallerExtMetaHandler metaHandler) {
+        context(clsMap, ctx, mapper, clsLdr);
+
+        this.metaHandler = metaHandler;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readFooter(Class<?> cls) throws IOException {
+        if (metaHandler.metadata(resolveTypeId(cls.getName(), mapper)) != null) {
+            int footerLen = in.readInt();
+
+            if (footerLen != EMPTY_FOOTER)
+                in.skipBytes(footerLen - 4);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected int readFieldType() throws IOException {
+        return in.readByte();
+    }
+
+    /**
+     * Checks whether the object has a field with name {@code fieldName}.
+     *
+     * @param fieldName Field name.
+     * @return {@code true} if field exists, {@code false} otherwise.
+     * @throws IOException in case of error.
+     */
+    boolean hasField(String fieldName) throws IOException {
+        int pos = in.position();
+
+        if (in.readByte() != SERIALIZABLE) {
+            in.position(pos);
+            return false;
+        }
+
+        FieldRange range = fieldRange(fieldName);
+
+        in.position(pos);
+
+        return range != null && range.start > 0;
+    }
+
+    /**
+     * Looks up field with the given name and returns it in one of the following representations. If the field is
+     * serializable and has a footer then it's not deserialized but rather returned wrapped by {@link CacheObjectImpl}
+     * for future processing. In all other cases the field is fully deserialized.
+     *
+     * @param fieldName Field name.
+     * @return Field.
+     * @throws IOException In case of error.
+     * @throws ClassNotFoundException In case of error.
+     */
+    <F> F readField(String fieldName) throws IOException, ClassNotFoundException {
+        int pos = in.position();
+
+        if (in.readByte() != SERIALIZABLE) {
+            in.position(pos);
+            return null;
+        }
+
+        FieldRange range = fieldRange(fieldName);
+
+        F field = null;
+
+        if (range != null && range.start > 0) {
+            in.position(range.start);
+
+            if (in.readByte() == SERIALIZABLE && metaHandler.metadata(in.readInt()) != null) {
+                //TODO: IGNITE-950. Optimization - don't create a copy of array, pass the old one with range.
+                field = (F)new CacheObjectImpl(null, Arrays.copyOfRange(in.array(), range.start, range.len));
+            }
+            else {
+                in.position(range.start);
+                field = (F)readObject();
+            }
+        }
+
+        in.position(pos);
+
+        return field;
+    }
+
+    /**
+     * Returns field offset in the byte stream.
+     *
+     * @param fieldName Field name.
+     * @return positive range or {@code null} if the object doesn't have such a field.
+     * @throws IOException in case of error.
+     */
+    private FieldRange fieldRange(String fieldName) throws IOException {
+        int fieldId = resolveFieldId(fieldName);
+
+        int typeId = readInt();
+
+        int clsNameLen = 0;
+
+        if (typeId == 0) {
+            int pos = in.position();
+
+            typeId = OptimizedMarshallerUtils.resolveTypeId(readUTF(), mapper);
+
+            clsNameLen = in.position() - pos;
+        }
+
+        OptimizedObjectMetadata meta = metaHandler.metadata(typeId);
+
+        if (meta == null)
+            // TODO: IGNITE-950 add warning!
+            return null;
+
+        int end = in.size();
+
+        in.position(end - FOOTER_LEN_OFF);
+
+        int footerLen = in.readInt();
+
+        if (footerLen == EMPTY_FOOTER)
+            return null;
+
+        // 4 - skipping length at the beginning
+        int footerOff = (end - footerLen) + 4;
+        in.position(footerOff);
+
+        int fieldOff = 0;
+
+        for (OptimizedObjectMetadata.FieldInfo info : meta.getMeta()) {
+            if (info.id == fieldId) {
+                //object header len: 1 - for type, 4 - for type ID, 2 - for checksum.
+                fieldOff += 1 + 4 + clsNameLen + 2;
+
+                FieldRange range = new FieldRange(fieldOff, info.len == VARIABLE_LEN ? in.readShort() : info.len);
+
+                return range;
+            }
+            else
+                fieldOff += info.len == VARIABLE_LEN ? in.readShort() : info.len;
+        }
+
+        return null;
+    }
+
+    /**
+     *
+     */
+    private static class FieldRange {
+        /** */
+        private int start;
+
+        /** */
+        private int len;
+
+        /**
+         * @param start Start.
+         * @param len   Length.
+         */
+        public FieldRange(int start, int len) {
+            this.start = start;
+            this.len = len;
+        }
+    }
+}