You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2015/11/05 14:16:15 UTC

[23/26] ignite git commit: Merged IGNITE-950-new into IGNITE-1282

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/binary/Binarylizable.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/binary/Binarylizable.java b/modules/core/src/main/java/org/apache/ignite/binary/Binarylizable.java
new file mode 100644
index 0000000..d09ffc8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/binary/Binarylizable.java
@@ -0,0 +1,48 @@
+/*
+ * 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.binary;
+
+/**
+ * Interface that allows to implement custom serialization
+ * logic for binary objects. IgniteObject is not required
+ * to implement this interface, in which case Ignite will automatically
+ * serialize binary objects using reflection.
+ * <p>
+ * This interface, in a way, is analogous to {@link java.io.Externalizable}
+ * interface, which allows users to override default serialization logic,
+ * usually for performance reasons. The only difference here is that binary
+ * serialization is already very fast and implementing custom serialization
+ * logic for binary does not provide significant performance gains.
+ */
+public interface Binarylizable {
+    /**
+     * Writes fields to provided writer.
+     *
+     * @param writer Binary object writer.
+     * @throws BinaryObjectException In case of error.
+     */
+    public void writeBinary(BinaryWriter writer) throws BinaryObjectException;
+
+    /**
+     * Reads fields from provided reader.
+     *
+     * @param reader Binary object reader.
+     * @throws BinaryObjectException In case of error.
+     */
+    public void readBinary(BinaryReader reader) throws BinaryObjectException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/binary/package-info.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/binary/package-info.java b/modules/core/src/main/java/org/apache/ignite/binary/package-info.java
new file mode 100644
index 0000000..5ebf9d8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/binary/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. -->
+ * Contains Ignite Binary Objects API classes.
+ */
+package org.apache.ignite.binary;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java b/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
new file mode 100644
index 0000000..767b44c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
@@ -0,0 +1,92 @@
+/*
+ * 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.cache;
+
+import java.io.Serializable;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Configuration defining various aspects of cache keys without explicit usage of annotations on user classes.
+ */
+public class CacheKeyConfiguration implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Type name. */
+    private String typeName;
+
+    /** Affinity key field name. */
+    private String affKeyFieldName;
+
+    /**
+     * Creates an empty cache key configuration that should be populated via setters.
+     */
+    public CacheKeyConfiguration() {
+        // Convenience no-op constructor.
+    }
+
+    /**
+     * Creates cache key configuration with given type name and affinity field name.
+     *
+     * @param typeName Type name.
+     * @param affKeyFieldName Affinity field name.
+     */
+    public CacheKeyConfiguration(String typeName, String affKeyFieldName) {
+        this.typeName = typeName;
+        this.affKeyFieldName = affKeyFieldName;
+    }
+
+    /**
+     * Sets type name for which affinity field name is being defined.
+     *
+     * @return Type name.
+     */
+    public String getTypeName() {
+        return typeName;
+    }
+
+    /**
+     * @param typeName Type name for which affinity field name is being defined.
+     */
+    public void setTypeName(String typeName) {
+        this.typeName = typeName;
+    }
+
+    /**
+     * Gets affinity key field name.
+     *
+     * @return Affinity key field name.
+     */
+    public String getAffinityKeyFieldName() {
+        return affKeyFieldName;
+    }
+
+    /**
+     * Sets affinity key field name.
+     *
+     * @param affKeyFieldName Affinity key field name.
+     */
+    public void setAffinityKeyFieldName(String affKeyFieldName) {
+        this.affKeyFieldName = affKeyFieldName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(CacheKeyConfiguration.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/CacheTypeMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/CacheTypeMetadata.java b/modules/core/src/main/java/org/apache/ignite/cache/CacheTypeMetadata.java
index 95a138d..2b7205b 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/CacheTypeMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/CacheTypeMetadata.java
@@ -23,9 +23,12 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import javax.cache.CacheException;
 import org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStore;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiTuple;
 
 /**
@@ -47,6 +50,9 @@ public class CacheTypeMetadata implements Serializable {
     /** Value class used to store value in cache. */
     private String valType;
 
+    /** Simple value type name that will be used as SQL table name.*/
+    private String simpleValType;
+
     /** Optional persistent key fields (needed only if {@link CacheJdbcPojoStore} is used). */
     @GridToStringInclude
     private Collection<CacheTypeFieldMetadata> keyFields;
@@ -75,22 +81,20 @@ public class CacheTypeMetadata implements Serializable {
     @GridToStringInclude
     private Map<String, LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>>> grps;
 
+    /** */
+    @GridToStringInclude
+    private Map<String,String> aliases;
+
     /**
      * Default constructor.
      */
     public CacheTypeMetadata() {
         keyFields = new ArrayList<>();
-
         valFields = new ArrayList<>();
-
         qryFlds = new LinkedHashMap<>();
-
         ascFlds = new LinkedHashMap<>();
-
         descFlds = new LinkedHashMap<>();
-
         txtFlds = new LinkedHashSet<>();
-
         grps = new LinkedHashMap<>();
     }
 
@@ -99,25 +103,16 @@ public class CacheTypeMetadata implements Serializable {
      */
     public CacheTypeMetadata(CacheTypeMetadata src) {
         dbSchema = src.getDatabaseSchema();
-
         dbTbl = src.getDatabaseTable();
-
         keyType = src.getKeyType();
-
         valType = src.getValueType();
-
+        simpleValType = src.simpleValType;
         keyFields = new ArrayList<>(src.getKeyFields());
-
         valFields = new ArrayList<>(src.getValueFields());
-
         qryFlds = new LinkedHashMap<>(src.getQueryFields());
-
         ascFlds = new LinkedHashMap<>(src.getAscendingFields());
-
         descFlds = new LinkedHashMap<>(src.getDescendingFields());
-
         txtFlds = new LinkedHashSet<>(src.getTextFields());
-
         grps = new LinkedHashMap<>(src.getGroups());
     }
 
@@ -194,12 +189,28 @@ public class CacheTypeMetadata implements Serializable {
     }
 
     /**
+     * Gets simple value type.
+     *
+     * @return Simple value type.
+     */
+    public String getSimpleValueType() {
+        return simpleValType;
+    }
+
+    /**
      * Sets value type.
      *
      * @param valType Value type.
      */
     public void setValueType(String valType) {
+        if (this.valType != null)
+            throw new CacheException("Value type can be set only once.");
+
         this.valType = valType;
+
+        Class<?> cls = U.classForName(valType, null);
+
+        simpleValType = cls == null ? valType : GridQueryProcessor.typeName(cls);
     }
 
     /**
@@ -280,7 +291,10 @@ public class CacheTypeMetadata implements Serializable {
      * @param ascFlds Name-to-type map for ascending-indexed fields.
      */
     public void setAscendingFields(Map<String, Class<?>> ascFlds) {
-        this.ascFlds = ascFlds;
+        if (ascFlds == null)
+            this.ascFlds = ascFlds;
+        else
+            this.ascFlds.putAll(ascFlds);
     }
 
     /**
@@ -337,6 +351,25 @@ public class CacheTypeMetadata implements Serializable {
         this.grps = grps;
     }
 
+    /**
+     * Sets mapping from full property name in dot notation to an alias that will be used as SQL column name.
+     * Example: {"parent.name" -> "parentName"}.
+     *
+     * @param aliases Aliases.
+     */
+    public void setAliases(Map<String,String> aliases) {
+        this.aliases = aliases;
+    }
+
+    /**
+     * Gets aliases mapping.
+     *
+     * @return Aliases.
+     */
+    public Map<String,String> getAliases() {
+        return aliases;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(CacheTypeMetadata.class, this);

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/IgniteObject.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/IgniteObject.java b/modules/core/src/main/java/org/apache/ignite/cache/IgniteObject.java
deleted file mode 100644
index ab926ce..0000000
--- a/modules/core/src/main/java/org/apache/ignite/cache/IgniteObject.java
+++ /dev/null
@@ -1,60 +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.cache;
-
-import java.io.Serializable;
-import org.apache.ignite.portable.PortableException;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Ignite object. Abstracted representation of a binary object form.
- */
-public interface IgniteObject extends Serializable {
-    /**
-     * Gets object type ID.
-     *
-     * @return Type ID.
-     */
-    public int typeId();
-
-    /**
-     * Gets field value.
-     *
-     * @param fieldName Field name.
-     * @return Field value.
-     * @throws PortableException In case of any other error.
-     */
-    @Nullable public <F> F field(String fieldName) throws PortableException;
-
-    /**
-     * Checks whether field is set.
-     *
-     * @param fieldName Field name.
-     * @return {@code true} if field is set.
-     */
-    public boolean hasField(String fieldName);
-
-    /**
-     * Gets fully deserialized instance of portable object.
-     *
-     * @return Fully deserialized instance of portable object.
-     * @throws org.apache.ignite.portable.PortableInvalidClassException If class doesn't exist.
-     * @throws PortableException In case of any other error.
-     */
-    @Nullable public <T> T deserialize() throws PortableException;
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
new file mode 100644
index 0000000..cb84c47
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
@@ -0,0 +1,217 @@
+/*
+ * 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.cache;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.A;
+
+/**
+ * Query entity is a description of {@link org.apache.ignite.IgniteCache cache} entry (composed of key and value)
+ * in a way of how it must be indexed and can be queried.
+ */
+public class QueryEntity implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Key type. */
+    private String keyType;
+
+    /** Value type. */
+    private String valType;
+
+    /** Fields available for query. A map from field name to type name. */
+    private LinkedHashMap<String, String> fields = new LinkedHashMap<>();
+
+    /** Aliases. */
+    private Map<String, String> aliases = new HashMap<>();
+
+    /** Collection of query indexes. */
+    private Map<String, QueryIndex> idxs = new HashMap<>();
+
+    /**
+     * Gets key type for this query pair.
+     *
+     * @return Key type.
+     */
+    public String getKeyType() {
+        return keyType;
+    }
+
+    /**
+     * Sets key type for this query pair.
+     *
+     * @param keyType Key type.
+     */
+    public void setKeyType(String keyType) {
+        this.keyType = keyType;
+    }
+
+    /**
+     * Gets value type for this query pair.
+     *
+     * @return Value type.
+     */
+    public String getValueType() {
+        return valType;
+    }
+
+    /**
+     * Sets value type for this query pair.
+     *
+     * @param valType Value type.
+     */
+    public void setValueType(String valType) {
+        this.valType = valType;
+    }
+
+    /**
+     * Gets query fields for this query pair. The order of fields is important as it defines the order
+     * of columns returned by the 'select *' queries.
+     *
+     * @return Field-to-type map.
+     */
+    public LinkedHashMap<String, String> getFields() {
+        return fields;
+    }
+
+    /**
+     * Sets query fields for this query pair. The order if fields is important as it defines the
+     * order of columns returned by the 'select *' queries.
+     *
+     * @param fields Field-to-type map.
+     */
+    public void setFields(LinkedHashMap<String, String> fields) {
+        this.fields = fields;
+    }
+
+    /**
+     * Gets a collection of index entities.
+     *
+     * @return Collection of index entities.
+     */
+    public Collection<QueryIndex> getIndexes() {
+        return idxs.values();
+    }
+
+    /**
+     * Gets aliases map.
+     *
+     * @return Aliases.
+     */
+    public Map<String, String> getAliases() {
+        return aliases;
+    }
+
+    /**
+     * Sets mapping from full property name in dot notation to an alias that will be used as SQL column name.
+     * Example: {"parent.name" -> "parentName"}.
+
+     * @param aliases Aliases map.
+     */
+    public void setAliases(Map<String, String> aliases) {
+        this.aliases = aliases;
+    }
+
+    /**
+     * Sets a collection of index entities.
+     *
+     * @param idxs Collection of index entities.
+     */
+    public void setIndexes(Collection<QueryIndex> idxs) {
+        for (QueryIndex idx : idxs) {
+            if (!F.isEmpty(idx.getFields())) {
+                if (idx.getName() == null)
+                    idx.setName(defaultIndexName(idx));
+
+                if (!this.idxs.containsKey(idx.getName()))
+                    this.idxs.put(idx.getName(), idx);
+                else
+                    throw new IllegalArgumentException("Duplicate index name: " + idx.getName());
+            }
+        }
+    }
+
+    /**
+     * Utility method for building query entities programmatically.
+     */
+    public void addQueryField(String fullName, String type, String alias) {
+        A.notNull(fullName, "fullName");
+        A.notNull(type, "type");
+
+        fields.put(fullName, type);
+
+        if (alias != null)
+            aliases.put(fullName, alias);
+    }
+
+    /**
+     * Ensures that index with the given name exists.
+     *
+     * @param idxName Index name.
+     * @param idxType Index type.
+     */
+    public void ensureIndex(String idxName, QueryIndexType idxType) {
+        QueryIndex idx = idxs.get(idxName);
+
+        if (idx == null) {
+            idx = new QueryIndex();
+
+            idx.setName(idxName);
+            idx.setIndexType(idxType);
+
+            idxs.put(idxName, idx);
+        }
+        else
+            throw new IllegalArgumentException("An index with the same name and of a different type already exists " +
+                "[idxName=" + idxName + ", existingIdxType=" + idx.getIndexType() + ", newIdxType=" + idxType + ']');
+    }
+
+    /**
+     * Generates default index name by concatenating all index field names.
+     *
+     * @param idx Index to build name for.
+     * @return Index name.
+     */
+    public static String defaultIndexName(QueryIndex idx) {
+        StringBuilder idxName = new StringBuilder();
+
+        for (Map.Entry<String, Boolean> field : idx.getFields().entrySet()) {
+            idxName.append(field.getKey());
+
+            idxName.append('_');
+            idxName.append(field.getValue() ? "asc_" : "desc_");
+        }
+
+        for (int i = 0; i < idxName.length(); i++) {
+            char ch = idxName.charAt(i);
+
+            if (Character.isWhitespace(ch))
+                idxName.setCharAt(i, '_');
+            else
+                idxName.setCharAt(i, Character.toLowerCase(ch));
+        }
+
+        idxName.append("idx");
+
+        return idxName.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
new file mode 100644
index 0000000..f12044d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java
@@ -0,0 +1,192 @@
+/*
+ * 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.cache;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+
+/**
+ * Contains list of fields to be indexed. It is possible to provide field name
+ * suffixed with index specific extension, for example for {@link QueryIndexType#SORTED sorted} index
+ * the list can be provided as following {@code (id, name asc, age desc)}.
+ */
+@SuppressWarnings("TypeMayBeWeakened")
+public class QueryIndex implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Index name. */
+    private String name;
+
+    /** */
+    private LinkedHashMap<String, Boolean> fields;
+
+    /** */
+    private QueryIndexType type;
+
+    /**
+     * Creates an empty index. Should be populated via setters.
+     */
+    public QueryIndex() {
+        // Empty constructor.
+    }
+
+    /**
+     * Creates single-field sorted ascending index.
+     *
+     * @param name Field name.
+     */
+    public QueryIndex(String name) {
+        this(name, QueryIndexType.SORTED, true);
+    }
+
+    /**
+     * Creates single-field sorted index.
+     *
+     * @param name Field name.
+     * @param asc Ascending flag.
+     */
+    public QueryIndex(String name, boolean asc) {
+        this(name, QueryIndexType.SORTED, asc);
+    }
+
+    /**
+     * Creates index for one field.
+     * If index is sorted, then ascending sorting is used by default.
+     * To specify sort order, use the next method.
+     * This constructor should also have a corresponding setter method.
+     */
+    public QueryIndex(String field, QueryIndexType type) {
+        this(Arrays.asList(field), type);
+    }
+
+    /**
+     * Creates index for one field. The last boolean parameter
+     * is ignored for non-sorted indexes.
+     */
+    public QueryIndex(String field, QueryIndexType type, boolean asc) {
+        fields = new LinkedHashMap<>();
+        fields.put(field, asc);
+
+        this.type = type;
+    }
+
+    /**
+     * Creates index for a collection of fields. If index is sorted, fields will be sorted in
+     * ascending order.
+     *
+     * @param fields Collection of fields to create an index.
+     * @param type Index type.
+     */
+    public QueryIndex(Collection<String> fields, QueryIndexType type) {
+        this.fields = new LinkedHashMap<>();
+
+        for (String field : fields)
+            this.fields.put(field, true);
+
+        this.type = type;
+    }
+
+    /**
+     * Creates index for a collection of fields. The order of fields in the created index will be the same
+     * as iteration order in the passed map. Map value defines whether the index will be ascending.
+     *
+     * @param fields Field name to field sort direction for sorted indexes.
+     * @param type Index type.
+     */
+    public QueryIndex(LinkedHashMap<String, Boolean> fields, QueryIndexType type) {
+        this.fields = fields;
+        this.type = type;
+    }
+
+    /**
+     * Gets index name. Will be automatically set if not provided by a user.
+     *
+     * @return Index name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets index name.
+     *
+     * @param name Index name.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Gets fields included in the index.
+     *
+     * @return Collection of index fields.
+     */
+    public LinkedHashMap<String, Boolean> getFields() {
+        return fields;
+    }
+
+    /**
+     * Sets fields included in the index.
+     *
+     * @param fields Collection of index fields.
+     */
+    public void setFields(LinkedHashMap<String, Boolean> fields) {
+        this.fields = fields;
+    }
+
+    /**
+     * @return Gets a collection of field names.
+     */
+    public Collection<String> getFieldNames() {
+        return fields.keySet();
+    }
+
+    /**
+     * Sets a collection of field names altogether with the field sorting direction. Sorting direction will be
+     * ignored for non-sorted indexes.
+     *
+     * @param fields Collection of fields.
+     * @param asc Ascending flag.
+     */
+    public void setFieldNames(Collection<String> fields, boolean asc) {
+        this.fields = new LinkedHashMap<>();
+
+        for (String field : fields)
+            this.fields.put(field, asc);
+    }
+
+    /**
+     * Gets index type.
+     *
+     * @return Index type.
+     */
+    public QueryIndexType getIndexType() {
+        return type;
+    }
+
+    /**
+     * Sets index type.
+     *
+     * @param type Index type.
+     */
+    public void setIndexType(QueryIndexType type) {
+        this.type = type;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/cache/QueryIndexType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndexType.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndexType.java
new file mode 100644
index 0000000..f9b544d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndexType.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cache;
+
+/**
+ * Index type.
+ */
+public enum QueryIndexType {
+    /**
+     * Sorted index.
+     */
+    SORTED,
+
+    /**
+     * Full-text index.
+     */
+    FULLTEXT,
+
+    /**
+     * Geo-spatial index.
+     */
+    GEOSPATIAL
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index ae3f21f..af0122f 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -18,8 +18,22 @@
 package org.apache.ignite.configuration;
 
 import java.io.Serializable;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeSet;
 import javax.cache.Cache;
+import javax.cache.CacheException;
 import javax.cache.configuration.CacheEntryListenerConfiguration;
 import javax.cache.configuration.CompleteConfiguration;
 import javax.cache.configuration.Factory;
@@ -36,14 +50,26 @@ import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.cache.CacheRebalanceMode;
 import org.apache.ignite.cache.CacheTypeMetadata;
 import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.QueryIndexType;
 import org.apache.ignite.cache.affinity.AffinityFunction;
 import org.apache.ignite.cache.affinity.AffinityKeyMapper;
 import org.apache.ignite.cache.eviction.EvictionFilter;
 import org.apache.ignite.cache.eviction.EvictionPolicy;
+import org.apache.ignite.cache.query.annotations.QueryGroupIndex;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
 import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
+import org.apache.ignite.cache.query.annotations.QueryTextField;
 import org.apache.ignite.cache.store.CacheStore;
 import org.apache.ignite.cache.store.CacheStoreSessionListener;
 import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
+import org.apache.ignite.internal.processors.query.GridQueryIndexType;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -51,14 +77,12 @@ import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.plugin.CachePluginConfiguration;
 import org.jetbrains.annotations.Nullable;
 
-import javax.cache.Cache;
-import javax.cache.configuration.CompleteConfiguration;
-import javax.cache.configuration.Factory;
-import javax.cache.configuration.MutableConfiguration;
-import javax.cache.expiry.ExpiryPolicy;
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.HashSet;
+import static org.apache.ignite.internal.processors.query.GridQueryIndexType.FULLTEXT;
+import static org.apache.ignite.internal.processors.query.GridQueryIndexType.GEO_SPATIAL;
+import static org.apache.ignite.internal.processors.query.GridQueryIndexType.SORTED;
+import static org.apache.ignite.internal.processors.query.GridQueryProcessor._VAL;
+import static org.apache.ignite.internal.processors.query.GridQueryProcessor.isGeometryClass;
+import static org.apache.ignite.internal.processors.query.GridQueryProcessor.isSqlType;
 
 /**
  * This class defines grid cache configuration. This configuration is passed to
@@ -330,7 +354,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     private boolean sqlEscapeAll;
 
     /** */
-    private Class<?>[] indexedTypes;
+    private transient Class<?>[] indexedTypes;
 
     /** */
     private int sqlOnheapRowCacheSize = DFLT_SQL_ONHEAP_ROW_CACHE_SIZE;
@@ -347,6 +371,9 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     /** Cache store session listeners. */
     private Factory<? extends CacheStoreSessionListener>[] storeSesLsnrs;
 
+    /** Query entities. */
+    private Collection<QueryEntity> qryEntities;
+
     /** Empty constructor (all values are initialized to their defaults). */
     public CacheConfiguration() {
         /* No-op. */
@@ -411,6 +438,8 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         name = cc.getName();
         nearCfg = cc.getNearConfiguration();
         nodeFilter = cc.getNodeFilter();
+        pluginCfgs = cc.getPluginConfigurations();
+        qryEntities = cc.getQueryEntities();
         rebalanceMode = cc.getRebalanceMode();
         rebalanceBatchSize = cc.getRebalanceBatchSize();
         rebalanceDelay = cc.getRebalanceDelay();
@@ -435,7 +464,6 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         writeBehindFlushSize = cc.getWriteBehindFlushSize();
         writeBehindFlushThreadCnt = cc.getWriteBehindFlushThreadCount();
         writeSync = cc.getWriteSynchronizationMode();
-        pluginCfgs = cc.getPluginConfigurations();
     }
 
     /**
@@ -855,19 +883,19 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
      * store implementation in some cases, but it can cause performance
      * degradation due to additional serializations and deserializations
      * of portable objects. You will also need to have key and value
-     * classes on all nodes since portables will be deserialized when
+     * classes on all nodes since binary will be deserialized when
      * store is called.
      *
-     * @return Keep portables in store flag.
+     * @return Keep binary in store flag.
      */
     public Boolean isKeepPortableInStore() {
         return keepPortableInStore;
     }
 
     /**
-     * Sets keep portables in store flag.
+     * Sets keep binary in store flag.
      *
-     * @param keepPortableInStore Keep portables in store flag.
+     * @param keepPortableInStore Keep binary in store flag.
      */
     public void setKeepPortableInStore(boolean keepPortableInStore) {
         this.keepPortableInStore = keepPortableInStore;
@@ -1577,9 +1605,10 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
      *
      * @param typeMeta Collection of type metadata.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link #setQueryEntities(java.util.Collection)} instead.
      */
     public CacheConfiguration<K, V> setTypeMetadata(Collection<CacheTypeMetadata> typeMeta) {
-        this.typeMeta = typeMeta;
+        this.typeMeta = new ArrayList<>(typeMeta);
 
         return this;
     }
@@ -1734,21 +1763,39 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
      * @return {@code this} for chaining.
      */
     public CacheConfiguration<K, V> setIndexedTypes(Class<?>... indexedTypes) {
-        A.ensure(indexedTypes == null || (indexedTypes.length & 1) == 0,
+        A.notNull(indexedTypes, "indexedTypes");
+
+        int len = indexedTypes.length;
+
+        if (len == 0)
+            return this;
+
+        A.ensure((len & 1) == 0,
             "Number of indexed types is expected to be even. Refer to method javadoc for details.");
 
-        if (indexedTypes != null) {
-            int len = indexedTypes.length;
+        if (this.indexedTypes != null)
+            throw new CacheException("Indexed types can be set only once.");
 
-            Class<?>[] newIndexedTypes = new Class<?>[len];
+        Class<?>[] newIndexedTypes = new Class<?>[len];
 
-            for (int i = 0; i < len; i++)
-                newIndexedTypes[i] = U.box(indexedTypes[i]);
+        for (int i = 0; i < len; i++) {
+            if (indexedTypes[i] == null)
+                throw new NullPointerException("Indexed types array contains null at index: " + i);
 
-            this.indexedTypes = newIndexedTypes;
+            newIndexedTypes[i] = U.box(indexedTypes[i]);
+        }
+
+        if (qryEntities == null)
+            qryEntities = new ArrayList<>();
+
+        for (int i = 0; i < len; i += 2) {
+            Class<?> keyCls = newIndexedTypes[i];
+            Class<?> valCls = newIndexedTypes[i + 1];
+
+            TypeDescriptor desc = processKeyAndValueClasses(keyCls, valCls);
+
+            qryEntities.add(convert(desc));
         }
-        else
-            this.indexedTypes = null;
 
         return this;
     }
@@ -1800,6 +1847,32 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     }
 
     /**
+     * Gets a collection of configured  query entities.
+     *
+     * @return Query entities configurations.
+     */
+    public Collection<QueryEntity> getQueryEntities() {
+        return qryEntities != null ? qryEntities : Collections.<QueryEntity>emptyList();
+    }
+
+    /**
+     * Sets query entities configuration.
+     *
+     * @param qryEntities Query entities.
+     * @return {@code this} for chaining.
+     */
+    public CacheConfiguration<K, V> setQueryEntities(Collection<QueryEntity> qryEntities) {
+        if (this.qryEntities == null)
+            this.qryEntities = new ArrayList<>(qryEntities);
+        else if (indexedTypes != null)
+            this.qryEntities.addAll(qryEntities);
+        else
+            throw new CacheException("Query entities can be set only once.");
+
+        return this;
+    }
+
+    /**
      * Gets topology validator.
      * <p>
      * See {@link TopologyValidator} for details.
@@ -1889,6 +1962,233 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         return cfg;
     }
 
+    /**
+     * @param desc Type descriptor.
+     * @return Type metadata.
+     */
+    static QueryEntity convert(TypeDescriptor desc) {
+        QueryEntity entity = new QueryEntity();
+
+        // Key and val types.
+        entity.setKeyType(desc.keyClass().getName());
+        entity.setValueType(desc.valueClass().getName());
+
+        for (ClassProperty prop : desc.props.values())
+            entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias());
+
+        QueryIndex txtIdx = null;
+
+        Collection<QueryIndex> idxs = new ArrayList<>();
+
+        for (Map.Entry<String, GridQueryIndexDescriptor> idxEntry : desc.indexes().entrySet()) {
+            GridQueryIndexDescriptor idx = idxEntry.getValue();
+
+            if (idx.type() == FULLTEXT) {
+                assert txtIdx == null;
+
+                txtIdx = new QueryIndex();
+
+                txtIdx.setIndexType(QueryIndexType.FULLTEXT);
+
+                txtIdx.setFieldNames(idx.fields(), true);
+                txtIdx.setName(idxEntry.getKey());
+            }
+            else {
+                Collection<String> grp = new ArrayList<>();
+
+                for (String fieldName : idx.fields())
+                    grp.add(idx.descending(fieldName) ? fieldName + " desc" : fieldName);
+
+                QueryIndex sortedIdx = new QueryIndex();
+
+                sortedIdx.setIndexType(idx.type() == SORTED ? QueryIndexType.SORTED : QueryIndexType.GEOSPATIAL);
+
+                LinkedHashMap<String, Boolean> fields = new LinkedHashMap<>();
+
+                for (String f : idx.fields())
+                    fields.put(f, !idx.descending(f));
+
+                sortedIdx.setFields(fields);
+
+                sortedIdx.setName(idxEntry.getKey());
+
+                idxs.add(sortedIdx);
+            }
+        }
+
+        if (desc.valueTextIndex()) {
+            if (txtIdx == null) {
+                txtIdx = new QueryIndex();
+
+                txtIdx.setIndexType(QueryIndexType.FULLTEXT);
+
+                txtIdx.setFieldNames(Arrays.asList(_VAL), true);
+            }
+            else
+                txtIdx.getFields().put(_VAL, true);
+        }
+
+        if (txtIdx != null)
+            idxs.add(txtIdx);
+
+        if (!F.isEmpty(idxs))
+            entity.setIndexes(idxs);
+
+        return entity;
+    }
+
+    /**
+     * @param cls Class.
+     * @return Masked class.
+     */
+    private static Class<?> mask(Class<?> cls) {
+        assert cls != null;
+
+        return isSqlType(cls) ? cls : Object.class;
+    }
+
+    /**
+     * @param keyCls Key class.
+     * @param valCls Value class.
+     * @return Type descriptor.
+     */
+    static TypeDescriptor processKeyAndValueClasses(
+        Class<?> keyCls,
+        Class<?> valCls
+    ) {
+        TypeDescriptor d = new TypeDescriptor();
+
+        d.keyClass(keyCls);
+        d.valueClass(valCls);
+
+        processAnnotationsInClass(true, d.keyCls, d, null);
+        processAnnotationsInClass(false, d.valCls, d, null);
+
+        return d;
+    }
+
+    /**
+     * Process annotations for class.
+     *
+     * @param key If given class relates to key.
+     * @param cls Class.
+     * @param type Type descriptor.
+     * @param parent Parent in case of embeddable.
+     */
+    private static void processAnnotationsInClass(boolean key, Class<?> cls, TypeDescriptor type,
+        @Nullable ClassProperty parent) {
+        if (U.isJdk(cls) || isGeometryClass(cls)) {
+            if (parent == null && !key && isSqlType(cls)) { // We have to index primitive _val.
+                String idxName = _VAL + "_idx";
+
+                type.addIndex(idxName, isGeometryClass(cls) ? GEO_SPATIAL : SORTED);
+
+                type.addFieldToIndex(idxName, _VAL, 0, false);
+            }
+
+            return;
+        }
+
+        if (parent != null && parent.knowsClass(cls))
+            throw new CacheException("Recursive reference found in type: " + cls.getName());
+
+        if (parent == null) { // Check class annotation at top level only.
+            QueryTextField txtAnnCls = cls.getAnnotation(QueryTextField.class);
+
+            if (txtAnnCls != null)
+                type.valueTextIndex(true);
+
+            QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class);
+
+            if (grpIdx != null)
+                type.addIndex(grpIdx.name(), SORTED);
+
+            QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class);
+
+            if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) {
+                for (QueryGroupIndex idx : grpIdxList.value())
+                    type.addIndex(idx.name(), SORTED);
+            }
+        }
+
+        for (Class<?> c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) {
+            for (Field field : c.getDeclaredFields()) {
+                QuerySqlField sqlAnn = field.getAnnotation(QuerySqlField.class);
+                QueryTextField txtAnn = field.getAnnotation(QueryTextField.class);
+
+                if (sqlAnn != null || txtAnn != null) {
+                    ClassProperty prop = new ClassProperty(field);
+
+                    prop.parent(parent);
+
+                    processAnnotation(key, sqlAnn, txtAnn, field.getType(), prop, type);
+
+                    type.addProperty(prop, true);
+                }
+            }
+
+            for (Method mtd : c.getDeclaredMethods()) {
+                QuerySqlField sqlAnn = mtd.getAnnotation(QuerySqlField.class);
+                QueryTextField txtAnn = mtd.getAnnotation(QueryTextField.class);
+
+                if (sqlAnn != null || txtAnn != null) {
+                    if (mtd.getParameterTypes().length != 0)
+                        throw new CacheException("Getter with QuerySqlField " +
+                            "annotation cannot have parameters: " + mtd);
+
+                    ClassProperty prop = new ClassProperty(mtd);
+
+                    prop.parent(parent);
+
+                    processAnnotation(key, sqlAnn, txtAnn, mtd.getReturnType(), prop, type);
+
+                    type.addProperty(prop, true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Processes annotation at field or method.
+     *
+     * @param key If given class relates to key.
+     * @param sqlAnn SQL annotation, can be {@code null}.
+     * @param txtAnn H2 text annotation, can be {@code null}.
+     * @param cls Class of field or return type for method.
+     * @param prop Current property.
+     * @param desc Class description.
+     */
+    private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTextField txtAnn,
+        Class<?> cls, ClassProperty prop, TypeDescriptor desc) {
+        if (sqlAnn != null) {
+            processAnnotationsInClass(key, cls, desc, prop);
+
+            if (!sqlAnn.name().isEmpty())
+                prop.alias(sqlAnn.name());
+
+            if (sqlAnn.index()) {
+                String idxName = prop.alias() + "_idx";
+
+                desc.addIndex(idxName, isGeometryClass(prop.type()) ? GEO_SPATIAL : SORTED);
+
+                desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending());
+            }
+
+            if (!F.isEmpty(sqlAnn.groups())) {
+                for (String group : sqlAnn.groups())
+                    desc.addFieldToIndex(group, prop.fullName(), 0, false);
+            }
+
+            if (!F.isEmpty(sqlAnn.orderedGroups())) {
+                for (QuerySqlField.Group idx : sqlAnn.orderedGroups())
+                    desc.addFieldToIndex(idx.name(), prop.fullName(), idx.order(), idx.descending());
+            }
+        }
+
+        if (txtAnn != null)
+            desc.addFieldToTextIndex(prop.fullName());
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(CacheConfiguration.class, this);
@@ -1908,10 +2208,318 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
 
         /** {@inheritDoc} */
         @Override public boolean equals(Object obj) {
-            if (obj == null)
-                return false;
+            return obj != null && obj.getClass().equals(this.getClass());
+        }
+    }
+
+    /**
+     * Descriptor of type.
+     */
+    private static class TypeDescriptor {
+        /** Value field names and types with preserved order. */
+        @GridToStringInclude
+        private final Map<String, Class<?>> fields = new LinkedHashMap<>();
+
+        /** */
+        @GridToStringExclude
+        private final Map<String, ClassProperty> props = new LinkedHashMap<>();
+
+        /** */
+        @GridToStringInclude
+        private final Map<String, IndexDescriptor> indexes = new HashMap<>();
+
+        /** */
+        private IndexDescriptor fullTextIdx;
+
+        /** */
+        private Class<?> keyCls;
+
+        /** */
+        private Class<?> valCls;
+
+        /** */
+        private boolean valTextIdx;
+
+        /**
+         * @return Indexes.
+         */
+        public Map<String, GridQueryIndexDescriptor> indexes() {
+            return Collections.<String, GridQueryIndexDescriptor>unmodifiableMap(indexes);
+        }
+
+        /**
+         * Adds index.
+         *
+         * @param idxName Index name.
+         * @param type Index type.
+         * @return Index descriptor.
+         */
+        public IndexDescriptor addIndex(String idxName, GridQueryIndexType type) {
+            IndexDescriptor idx = new IndexDescriptor(type);
+
+            if (indexes.put(idxName, idx) != null)
+                throw new CacheException("Index with name '" + idxName + "' already exists.");
+
+            return idx;
+        }
+
+        /**
+         * Adds field to index.
+         *
+         * @param idxName Index name.
+         * @param field Field name.
+         * @param orderNum Fields order number in index.
+         * @param descending Sorting order.
+         */
+        public void addFieldToIndex(String idxName, String field, int orderNum,
+            boolean descending) {
+            IndexDescriptor desc = indexes.get(idxName);
+
+            if (desc == null)
+                desc = addIndex(idxName, SORTED);
+
+            desc.addField(field, orderNum, descending);
+        }
+
+        /**
+         * Adds field to text index.
+         *
+         * @param field Field name.
+         */
+        public void addFieldToTextIndex(String field) {
+            if (fullTextIdx == null) {
+                fullTextIdx = new IndexDescriptor(FULLTEXT);
+
+                indexes.put(null, fullTextIdx);
+            }
+
+            fullTextIdx.addField(field, 0, false);
+        }
+
+        /**
+         * @return Value class.
+         */
+        public Class<?> valueClass() {
+            return valCls;
+        }
+
+        /**
+         * Sets value class.
+         *
+         * @param valCls Value class.
+         */
+        void valueClass(Class<?> valCls) {
+            this.valCls = valCls;
+        }
+
+        /**
+         * @return Key class.
+         */
+        public Class<?> keyClass() {
+            return keyCls;
+        }
+
+        /**
+         * Set key class.
+         *
+         * @param keyCls Key class.
+         */
+        void keyClass(Class<?> keyCls) {
+            this.keyCls = keyCls;
+        }
+
+        /**
+         * Adds property to the type descriptor.
+         *
+         * @param prop Property.
+         * @param failOnDuplicate Fail on duplicate flag.
+         */
+        public void addProperty(ClassProperty prop, boolean failOnDuplicate) {
+            String name = prop.fullName();
+
+            if (props.put(name, prop) != null && failOnDuplicate)
+                throw new CacheException("Property with name '" + name + "' already exists.");
+
+            fields.put(name, prop.type());
+        }
+
+        /**
+         * @return {@code true} If we need to have a fulltext index on value.
+         */
+        public boolean valueTextIndex() {
+            return valTextIdx;
+        }
+
+        /**
+         * Sets if this value should be text indexed.
+         *
+         * @param valTextIdx Flag value.
+         */
+        public void valueTextIndex(boolean valTextIdx) {
+            this.valTextIdx = valTextIdx;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(TypeDescriptor.class, this);
+        }
+    }
+
+    /**
+     * Index descriptor.
+     */
+    private static class IndexDescriptor implements GridQueryIndexDescriptor {
+        /** Fields sorted by order number. */
+        private final Collection<T2<String, Integer>> fields = new TreeSet<>(
+            new Comparator<T2<String, Integer>>() {
+                @Override public int compare(T2<String, Integer> o1, T2<String, Integer> o2) {
+                    if (o1.get2().equals(o2.get2())) // Order is equal, compare field names to avoid replace in Set.
+                        return o1.get1().compareTo(o2.get1());
+
+                    return o1.get2() < o2.get2() ? -1 : 1;
+                }
+            });
+
+        /** Fields which should be indexed in descending order. */
+        private Collection<String> descendings;
+
+        /** */
+        private final GridQueryIndexType type;
+
+        /**
+         * @param type Type.
+         */
+        private IndexDescriptor(GridQueryIndexType type) {
+            assert type != null;
+
+            this.type = type;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Collection<String> fields() {
+            Collection<String> res = new ArrayList<>(fields.size());
+
+            for (T2<String, Integer> t : fields)
+                res.add(t.get1());
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean descending(String field) {
+            return descendings != null && descendings.contains(field);
+        }
+
+        /**
+         * Adds field to this index.
+         *
+         * @param field Field name.
+         * @param orderNum Field order number in this index.
+         * @param descending Sort order.
+         */
+        public void addField(String field, int orderNum, boolean descending) {
+            fields.add(new T2<>(field, orderNum));
+
+            if (descending) {
+                if (descendings == null)
+                    descendings  = new HashSet<>();
+
+                descendings.add(field);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public GridQueryIndexType type() {
+            return type;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(IndexDescriptor.class, this);
+        }
+    }
+
+    /**
+     * Description of type property.
+     */
+    private static class ClassProperty {
+        /** */
+        private final Member member;
+
+        /** */
+        private ClassProperty parent;
+
+        /** */
+        private String name;
+
+        /** */
+        private String alias;
+
+        /**
+         * Constructor.
+         *
+         * @param member Element.
+         */
+        ClassProperty(Member member) {
+            this.member = member;
+
+            name = member instanceof Method && member.getName().startsWith("get") && member.getName().length() > 3 ?
+                member.getName().substring(3) : member.getName();
+
+            ((AccessibleObject) member).setAccessible(true);
+        }
+
+        /**
+         * @param alias Alias.
+         */
+        public void alias(String alias) {
+            this.alias = alias;
+        }
+
+        /**
+         * @return Alias.
+         */
+        String alias() {
+            return F.isEmpty(alias) ? name : alias;
+        }
+
+        /**
+         * @return Type.
+         */
+        public Class<?> type() {
+            return member instanceof Field ? ((Field)member).getType() : ((Method)member).getReturnType();
+        }
+
+        /**
+         * @param parent Parent property if this is embeddable element.
+         */
+        public void parent(ClassProperty parent) {
+            this.parent = parent;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(ClassProperty.class, this);
+        }
+
+        /**
+         * @param cls Class.
+         * @return {@code true} If this property or some parent relates to member of the given class.
+         */
+        public boolean knowsClass(Class<?> cls) {
+            return member.getDeclaringClass() == cls || (parent != null && parent.knowsClass(cls));
+        }
+
+        /**
+         * @return Full name with all parents in dot notation.
+         */
+        public String fullName() {
+            assert name != null;
+
+            if (parent == null)
+                return name;
 
-            return obj.getClass().equals(this.getClass());
+            return parent.fullName() + '.' + name;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
index 3d38ed9..a91aa7e 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
@@ -31,6 +31,7 @@ import javax.net.ssl.SSLContext;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.CacheKeyConfiguration;
 import org.apache.ignite.cache.store.CacheStoreSessionListener;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
@@ -427,6 +428,9 @@ public class IgniteConfiguration {
     /** Platform configuration. */
     private PlatformConfiguration platformCfg;
 
+    /** Cache key configuration. */
+    private CacheKeyConfiguration[] cacheKeyCfg;
+
     /**
      * Creates valid grid configuration with all default values.
      */
@@ -463,6 +467,7 @@ public class IgniteConfiguration {
         atomicCfg = cfg.getAtomicConfiguration();
         daemon = cfg.isDaemon();
         cacheCfg = cfg.getCacheConfiguration();
+        cacheKeyCfg = cfg.getCacheKeyConfiguration();
         cacheSanityCheckEnabled = cfg.isCacheSanityCheckEnabled();
         connectorCfg = cfg.getConnectorConfiguration();
         classLdr = cfg.getClassLoader();
@@ -1950,6 +1955,25 @@ public class IgniteConfiguration {
     }
 
     /**
+     * Gets cache key configuration.
+     *
+     * @return Cache key configuration.
+     */
+    public CacheKeyConfiguration[] getCacheKeyConfiguration() {
+        return cacheKeyCfg;
+    }
+
+    /**
+     * Sets cache key configuration.
+     * Cache key configuration defines
+     *
+     * @param cacheKeyCfg Cache key configuration.
+     */
+    public void setCacheKeyCfg(CacheKeyConfiguration... cacheKeyCfg) {
+        this.cacheKeyCfg = cacheKeyCfg;
+    }
+
+    /**
      * Gets flag indicating whether cache sanity check is enabled. If enabled, then Ignite
      * will perform the following checks and throw an exception if check fails:
      * <ul>

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index ebf83bd..4990dc7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -49,7 +49,7 @@ import org.apache.ignite.internal.managers.swapspace.GridSwapSpaceManager;
 import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
 import org.apache.ignite.internal.processors.cache.CacheConflictResolutionManager;
 import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
-import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessorImpl;
+import org.apache.ignite.internal.processors.cache.portable.CacheObjectBinaryProcessorImpl;
 import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
 import org.apache.ignite.internal.processors.clock.GridClockSource;
 import org.apache.ignite.internal.processors.clock.GridClockSyncProcessor;
@@ -830,7 +830,7 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
             return res;
 
         if (cls.equals(IgniteCacheObjectProcessor.class))
-            return (T)new CacheObjectPortableProcessorImpl(this);
+            return (T)new CacheObjectBinaryProcessorImpl(this);
 
         if (cls.equals(CacheConflictResolutionManager.class))
             return null;

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index a7e62b0..88e697c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -64,7 +64,7 @@ import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteFileSystem;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteMessaging;
-import org.apache.ignite.IgnitePortables;
+import org.apache.ignite.IgniteBinary;
 import org.apache.ignite.IgniteQueue;
 import org.apache.ignite.IgniteScheduler;
 import org.apache.ignite.IgniteServices;
@@ -102,8 +102,8 @@ import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
 import org.apache.ignite.internal.processors.cache.GridCacheUtilityKey;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
-import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessor;
-import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessorImpl;
+import org.apache.ignite.internal.processors.cache.portable.CacheObjectBinaryProcessor;
+import org.apache.ignite.internal.processors.cache.portable.CacheObjectBinaryProcessorImpl;
 import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
 import org.apache.ignite.internal.processors.clock.GridClockSyncProcessor;
 import org.apache.ignite.internal.processors.closure.GridClosureProcessor;
@@ -157,7 +157,6 @@ import org.apache.ignite.lifecycle.LifecycleBean;
 import org.apache.ignite.lifecycle.LifecycleEventType;
 import org.apache.ignite.marshaller.MarshallerExclusions;
 import org.apache.ignite.marshaller.optimized.OptimizedMarshaller;
-import org.apache.ignite.marshaller.portable.PortableMarshaller;
 import org.apache.ignite.mxbean.ClusterLocalNodeMetricsMXBean;
 import org.apache.ignite.mxbean.IgniteMXBean;
 import org.apache.ignite.mxbean.ThreadPoolMXBean;
@@ -1490,6 +1489,8 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
                 mgr.start();
         }
         catch (IgniteCheckedException e) {
+            U.error(log, "Failed to start manager: " + mgr , e);
+
             throw new IgniteCheckedException("Failed to start manager: " + mgr, e);
         }
     }
@@ -2789,8 +2790,10 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
     }
 
     /** {@inheritDoc} */
-    @Override public IgnitePortables portables() {
-        return ((CacheObjectPortableProcessor)ctx.cacheObjects()).portables();
+    @Override public IgniteBinary binary() {
+        IgniteCacheObjectProcessor objProc = ctx.cacheObjects();
+
+        return objProc.binary();
     }
 
     /** {@inheritDoc} */
@@ -3088,7 +3091,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
             return comp;
 
         if (cls.equals(IgniteCacheObjectProcessor.class))
-            return (T)new CacheObjectPortableProcessorImpl(ctx);
+            return (T)new CacheObjectBinaryProcessorImpl(ctx);
 
         if (cls.equals(DiscoveryNodeValidationProcessor.class))
             return (T)new OsDiscoveryNodeValidationProcessor(ctx);

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/client/GridClientCompute.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/GridClientCompute.java b/modules/core/src/main/java/org/apache/ignite/internal/client/GridClientCompute.java
index c907779..e7255f9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/GridClientCompute.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/GridClientCompute.java
@@ -414,7 +414,7 @@ public interface GridClientCompute {
     public GridClientFuture<List<GridClientNode>> refreshTopologyAsync(boolean includeAttrs, boolean includeMetrics);
 
     /**
-     * Sets keep portables flag for the next task execution in the current thread.
+     * Sets keep binary flag for the next task execution in the current thread.
      */
     public GridClientCompute withKeepPortables();
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnection.java
index fedd15a..8bdb1d0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnection.java
@@ -313,7 +313,7 @@ public abstract class GridClientConnection {
      * @param taskName Task name.
      * @param arg Task argument.
      * @param destNodeId Destination node ID.
-     * @param keepPortables Keep portables flag.
+     * @param keepPortables Keep binary flag.
      * @return Task execution result.
      * @throws GridClientConnectionResetException In case of error.
      * @throws GridClientClosedException If client was manually closed before request was sent over network.

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnectionManagerAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnectionManagerAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnectionManagerAdapter.java
index f1a9af1..dedee10 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnectionManagerAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientConnectionManagerAdapter.java
@@ -483,7 +483,7 @@ public abstract class GridClientConnectionManagerAdapter implements GridClientCo
     }
 
     /**
-     * @return Get thread local used to enable keep portables mode.
+     * @return Get thread local used to enable keep binary mode.
      */
     protected ThreadLocal<Boolean> keepPortablesThreadLocal() {
         return null;

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java
index eee2858..24c9c70 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java
@@ -385,7 +385,7 @@ public class GridClientNioTcpConnection extends GridClientConnection {
      *
      * @param msg Message to request,
      * @param destId Destination node identifier.
-     * @param keepPortables Keep portables flag.
+     * @param keepPortables Keep binary flag.
      * @return Response object.
      * @throws GridClientConnectionResetException If request failed.
      * @throws GridClientClosedException If client was closed.
@@ -1037,7 +1037,7 @@ public class GridClientNioTcpConnection extends GridClientConnection {
         /** Flag indicating if connected message is a forwarded. */
         private final boolean forward;
 
-        /** Keep portables flag. */
+        /** Keep binary flag. */
         private final boolean keepPortables;
 
         /** Pending message for this future. */
@@ -1101,7 +1101,7 @@ public class GridClientNioTcpConnection extends GridClientConnection {
         }
 
         /**
-         * @return Keep portables flag.
+         * @return Keep binary flag.
          */
         public boolean keepPortables() {
             return keepPortables;

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
index 079015c..623da66 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
@@ -31,7 +31,7 @@ import org.apache.ignite.internal.managers.deployment.GridDeploymentInfoBean;
 import org.apache.ignite.internal.managers.deployment.GridDeploymentRequest;
 import org.apache.ignite.internal.managers.deployment.GridDeploymentResponse;
 import org.apache.ignite.internal.managers.eventstorage.GridEventStorageMessage;
-import org.apache.ignite.internal.portable.PortableObjectImpl;
+import org.apache.ignite.internal.portable.BinaryObjectImpl;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
 import org.apache.ignite.internal.processors.cache.CacheEntryPredicateContainsValue;
@@ -680,7 +680,7 @@ public class GridIoMessageFactory implements MessageFactory {
                 break;
 
             case 113:
-                msg = new PortableObjectImpl();
+                msg = new BinaryObjectImpl();
 
                 break;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/managers/swapspace/GridSwapSpaceManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/swapspace/GridSwapSpaceManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/swapspace/GridSwapSpaceManager.java
index f90cf4f..437603a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/swapspace/GridSwapSpaceManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/swapspace/GridSwapSpaceManager.java
@@ -221,22 +221,6 @@ public class GridSwapSpaceManager extends GridManagerAdapter<SwapSpaceSpi> {
     }
 
     /**
-     * Writes value to swap.
-     *
-     * @param spaceName Space name.
-     * @param key Key.
-     * @param val Value.
-     * @param ldr Class loader (optional).
-     * @throws IgniteCheckedException If failed.
-     */
-    public void write(@Nullable String spaceName, Object key, @Nullable Object val, @Nullable ClassLoader ldr)
-        throws IgniteCheckedException {
-        assert key != null;
-
-        write(spaceName, new SwapKey(key), marshal(val), ldr);
-    }
-
-    /**
      * Removes value from swap.
      *
      * @param spaceName Space name.
@@ -284,24 +268,6 @@ public class GridSwapSpaceManager extends GridManagerAdapter<SwapSpaceSpi> {
     }
 
     /**
-     * Removes value from swap.
-     *
-     * @param spaceName Space name.
-     * @param key Key.
-     * @param c Optional closure that takes removed value and executes after actual
-     *      removing. If there was no value in storage the closure is executed given
-     *      {@code null} value as parameter.
-     * @param ldr Class loader (optional).
-     * @throws IgniteCheckedException If failed.
-     */
-    public void remove(@Nullable String spaceName, Object key, @Nullable IgniteInClosure<byte[]> c,
-        @Nullable ClassLoader ldr) throws IgniteCheckedException {
-        assert key != null;
-
-        remove(spaceName, new SwapKey(key), c, ldr);
-    }
-
-    /**
      * Gets size in bytes for swap space.
      *
      * @param spaceName Space name.

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryFieldImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryFieldImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryFieldImpl.java
new file mode 100644
index 0000000..b8a25c1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryFieldImpl.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryField;
+
+/**
+ * Implementation of portable field descriptor.
+ */
+public class BinaryFieldImpl implements BinaryField {
+    /** Well-known object schemas. */
+    @GridToStringExclude
+    private final PortableSchemaRegistry schemas;
+
+    /** Field name. */
+    private final String fieldName;
+
+    /** Pre-calculated field ID. */
+    private final int fieldId;
+
+    /**
+     * Constructor.
+     *
+     * @param schemas Schemas.
+     * @param fieldName Field name.
+     * @param fieldId Field ID.
+     */
+    public BinaryFieldImpl(PortableSchemaRegistry schemas, String fieldName, int fieldId) {
+        assert schemas != null;
+        assert fieldName != null;
+        assert fieldId != 0;
+
+        this.schemas = schemas;
+        this.fieldName = fieldName;
+        this.fieldId = fieldId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return fieldName;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean exists(BinaryObject obj) {
+        BinaryObjectEx obj0 = (BinaryObjectEx)obj;
+
+        return fieldOrder(obj0) != PortableSchema.ORDER_NOT_FOUND;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T> T value(BinaryObject obj) {
+        BinaryObjectEx obj0 = (BinaryObjectEx)obj;
+
+        int order = fieldOrder(obj0);
+
+        return order != PortableSchema.ORDER_NOT_FOUND ? (T)obj0.fieldByOrder(order) : null;
+    }
+
+    /**
+     * Get relative field offset.
+     *
+     * @param obj Object.
+     * @return Field offset.
+     */
+    private int fieldOrder(BinaryObjectEx obj) {
+        int schemaId = obj.schemaId();
+
+        PortableSchema schema = schemas.schema(schemaId);
+
+        if (schema == null) {
+            schema = obj.createSchema();
+
+            schemas.addSchema(schemaId, schema);
+        }
+
+        assert schema != null;
+
+        return schema.order(fieldId);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(BinaryFieldImpl.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b783d2b7/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryMetaDataCollector.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryMetaDataCollector.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryMetaDataCollector.java
new file mode 100644
index 0000000..b053a55
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryMetaDataCollector.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.BinaryRawWriter;
+import org.apache.ignite.binary.BinaryWriter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Writer for meta data collection.
+ */
+class BinaryMetaDataCollector implements BinaryWriter {
+    /** */
+    private final Map<String, String> meta = new HashMap<>();
+
+    /** */
+    private final String typeName;
+
+    /**
+     * @param typeName Type name.
+     */
+    BinaryMetaDataCollector(String typeName) {
+        this.typeName = typeName;
+    }
+
+    /**
+     * @return Field meta data.
+     */
+    Map<String, String> meta() {
+        return meta;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeByte(String fieldName, byte val) throws BinaryObjectException {
+        add(fieldName, byte.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeShort(String fieldName, short val) throws BinaryObjectException {
+        add(fieldName, short.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeInt(String fieldName, int val) throws BinaryObjectException {
+        add(fieldName, int.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeLong(String fieldName, long val) throws BinaryObjectException {
+        add(fieldName, long.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeFloat(String fieldName, float val) throws BinaryObjectException {
+        add(fieldName, float.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDouble(String fieldName, double val) throws BinaryObjectException {
+        add(fieldName, double.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeChar(String fieldName, char val) throws BinaryObjectException {
+        add(fieldName, char.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBoolean(String fieldName, boolean val) throws BinaryObjectException {
+        add(fieldName, boolean.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDecimal(String fieldName, @Nullable BigDecimal val) throws BinaryObjectException {
+        add(fieldName, PortableClassDescriptor.Mode.DECIMAL.typeName());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeString(String fieldName, @Nullable String val) throws BinaryObjectException {
+        add(fieldName, String.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeUuid(String fieldName, @Nullable UUID val) throws BinaryObjectException {
+        add(fieldName, UUID.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDate(String fieldName, @Nullable Date val) throws BinaryObjectException {
+        add(fieldName, Date.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTimestamp(String fieldName, @Nullable Timestamp val) throws BinaryObjectException {
+        add(fieldName, Timestamp.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T extends Enum<?>> void writeEnum(String fieldName, T val) throws BinaryObjectException {
+        add(fieldName, Enum.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T extends Enum<?>> void writeEnumArray(String fieldName, T[] val) throws BinaryObjectException {
+        add(fieldName, Enum[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeObject(String fieldName, @Nullable Object obj) throws BinaryObjectException {
+        add(fieldName, Object.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeByteArray(String fieldName, @Nullable byte[] val) throws BinaryObjectException {
+        add(fieldName, byte[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeShortArray(String fieldName, @Nullable short[] val) throws BinaryObjectException {
+        add(fieldName, short[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeIntArray(String fieldName, @Nullable int[] val) throws BinaryObjectException {
+        add(fieldName, int[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeLongArray(String fieldName, @Nullable long[] val) throws BinaryObjectException {
+        add(fieldName, long[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeFloatArray(String fieldName, @Nullable float[] val) throws BinaryObjectException {
+        add(fieldName, float[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDoubleArray(String fieldName, @Nullable double[] val) throws BinaryObjectException {
+        add(fieldName, double[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeCharArray(String fieldName, @Nullable char[] val) throws BinaryObjectException {
+        add(fieldName, char[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeBooleanArray(String fieldName, @Nullable boolean[] val) throws BinaryObjectException {
+        add(fieldName, boolean[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDecimalArray(String fieldName, @Nullable BigDecimal[] val) throws BinaryObjectException {
+        add(fieldName, PortableClassDescriptor.Mode.DECIMAL_ARR.typeName());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeStringArray(String fieldName, @Nullable String[] val) throws BinaryObjectException {
+        add(fieldName, String[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeUuidArray(String fieldName, @Nullable UUID[] val) throws BinaryObjectException {
+        add(fieldName, UUID[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeDateArray(String fieldName, @Nullable Date[] val) throws BinaryObjectException {
+        add(fieldName, Date[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeTimestampArray(String fieldName, @Nullable Timestamp[] val) throws BinaryObjectException {
+        add(fieldName, Timestamp[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeObjectArray(String fieldName, @Nullable Object[] val) throws BinaryObjectException {
+        add(fieldName, Object[].class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> void writeCollection(String fieldName, @Nullable Collection<T> col)
+        throws BinaryObjectException {
+        add(fieldName, Collection.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public <K, V> void writeMap(String fieldName, @Nullable Map<K, V> map) throws BinaryObjectException {
+        add(fieldName, Map.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public BinaryRawWriter rawWriter() {
+        return (BinaryRawWriter)Proxy.newProxyInstance(getClass().getClassLoader(),
+            new Class<?>[] { BinaryRawWriterEx.class },
+            new InvocationHandler() {
+                @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable {
+                    return null;
+                }
+            });
+    }
+
+    /**
+     * @param name Field name.
+     * @param fieldType Field type.
+     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
+     */
+    private void add(String name, Class<?> fieldType) throws BinaryObjectException {
+        assert fieldType != null;
+
+        add(name, fieldType.getSimpleName());
+    }
+
+    /**
+     * @param name Field name.
+     * @param fieldTypeName Field type name.
+     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
+     */
+    private void add(String name, String fieldTypeName) throws BinaryObjectException {
+        assert name != null;
+
+        String oldFieldTypeName = meta.put(name, fieldTypeName);
+
+        if (oldFieldTypeName != null && !oldFieldTypeName.equals(fieldTypeName)) {
+            throw new BinaryObjectException(
+                "Field is written twice with different types [" +
+                "typeName=" + typeName +
+                ", fieldName=" + name +
+                ", fieldTypeName1=" + oldFieldTypeName +
+                ", fieldTypeName2=" + fieldTypeName +
+                ']'
+            );
+        }
+    }
+}
\ No newline at end of file