You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vk...@apache.org on 2015/08/27 02:11:39 UTC
[14/59] [abbrv] ignite git commit: ignite-1258: portable objects API
support in Ignite
ignite-1258: portable objects API support in Ignite
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/878dcd92
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/878dcd92
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/878dcd92
Branch: refs/heads/ignite-884
Commit: 878dcd924be66b3bdf92de3ec07afe3cafc4dc82
Parents: 2ce0209
Author: Denis Magda <dm...@gridgain.com>
Authored: Tue Aug 25 10:05:15 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Tue Aug 25 10:05:16 2015 +0300
----------------------------------------------------------------------
modules/core/pom.xml | 21 +
.../src/main/java/org/apache/ignite/Ignite.java | 7 +
.../java/org/apache/ignite/IgniteCache.java | 41 +
.../java/org/apache/ignite/IgnitePortables.java | 362 ++
.../configuration/CacheConfiguration.java | 40 +
.../ignite/internal/GridKernalContextImpl.java | 3 +-
.../apache/ignite/internal/IgniteKernal.java | 8 +-
.../communication/GridIoMessageFactory.java | 6 +
.../portable/GridPortableMarshaller.java | 304 ++
.../portable/PortableAbstractLazyValue.java | 57 +
.../internal/portable/PortableBuilderEnum.java | 114 +
.../internal/portable/PortableBuilderImpl.java | 519 +++
.../portable/PortableBuilderReader.java | 775 ++++
.../PortableBuilderSerializationAware.java | 29 +
.../portable/PortableBuilderSerializer.java | 210 +
.../portable/PortableClassDescriptor.java | 1344 +++++++
.../internal/portable/PortableContext.java | 1089 ++++++
.../portable/PortableEnumArrayLazyValue.java | 111 +
.../portable/PortableLazyArrayList.java | 156 +
.../portable/PortableLazyLinkedList.java | 210 +
.../internal/portable/PortableLazyMap.java | 214 +
.../internal/portable/PortableLazyMapEntry.java | 66 +
.../internal/portable/PortableLazySet.java | 89 +
.../internal/portable/PortableLazyValue.java | 28 +
.../portable/PortableMetaDataCollector.java | 253 ++
.../portable/PortableMetaDataHandler.java | 43 +
.../internal/portable/PortableMetaDataImpl.java | 140 +
.../portable/PortableObjectArrayLazyValue.java | 89 +
.../internal/portable/PortableObjectEx.java | 213 +
.../internal/portable/PortableObjectImpl.java | 383 ++
.../portable/PortableObjectOffheapImpl.java | 238 ++
.../portable/PortablePlainLazyValue.java | 47 +
.../portable/PortablePlainPortableObject.java | 50 +
.../internal/portable/PortablePrimitives.java | 773 ++++
.../internal/portable/PortableRawReaderEx.java | 33 +
.../internal/portable/PortableRawWriterEx.java | 44 +
.../portable/PortableReaderContext.java | 83 +
.../internal/portable/PortableReaderExImpl.java | 2949 ++++++++++++++
.../PortableThreadLocalMemoryAllocator.java | 163 +
.../ignite/internal/portable/PortableUtils.java | 419 ++
.../portable/PortableValueWithType.java | 74 +
.../internal/portable/PortableWriterExImpl.java | 1769 +++++++++
.../ignite/internal/portable/package-info.java | 22 +
.../streams/PortableAbstractInputStream.java | 343 ++
.../streams/PortableAbstractOutputStream.java | 323 ++
.../streams/PortableAbstractStream.java | 82 +
.../streams/PortableHeapInputStream.java | 134 +
.../streams/PortableHeapOutputStream.java | 155 +
.../portable/streams/PortableInputStream.java | 168 +
.../streams/PortableMemoryAllocator.java | 76 +
.../streams/PortableOffheapInputStream.java | 129 +
.../streams/PortableOffheapOutputStream.java | 169 +
.../portable/streams/PortableOutputStream.java | 165 +
.../streams/PortableSimpleMemoryAllocator.java | 67 +
.../portable/streams/PortableStream.java | 53 +
.../internal/portable/streams/package-info.java | 22 +
.../processors/cache/GridCacheProcessor.java | 7 +
.../processors/cache/IgniteCacheProxy.java | 5 +
.../CacheDefaultPortableAffinityKeyMapper.java | 51 +
.../portable/CacheObjectPortableContext.java | 187 +
.../portable/CacheObjectPortableProcessor.java | 101 +
.../CacheObjectPortableProcessorImpl.java | 956 +++++
.../cache/portable/IgnitePortablesImpl.java | 176 +
.../cache/portable/PortableMetaDataKey.java | 80 +
.../processors/cache/portable/package-info.java | 22 +
.../cache/store/CacheOsStoreManager.java | 3 +-
.../portable/GridPortableInputStream.java | 168 -
.../portable/GridPortableOutputStream.java | 165 -
.../processors/portable/GridPortableStream.java | 53 -
.../processors/portable/package-info.java | 22 -
.../marshaller/portable/PortableMarshaller.java | 347 ++
.../marshaller/portable/package-info.java | 22 +
.../apache/ignite/portable/PortableBuilder.java | 138 +
.../ignite/portable/PortableException.java | 58 +
.../ignite/portable/PortableIdMapper.java | 56 +
.../portable/PortableInvalidClassException.java | 58 +
.../ignite/portable/PortableMarshalAware.java | 48 +
.../ignite/portable/PortableMetadata.java | 63 +
.../apache/ignite/portable/PortableObject.java | 153 +
.../portable/PortableProtocolVersion.java | 41 +
.../ignite/portable/PortableRawReader.java | 233 ++
.../ignite/portable/PortableRawWriter.java | 218 ++
.../apache/ignite/portable/PortableReader.java | 283 ++
.../ignite/portable/PortableSerializer.java | 49 +
.../portable/PortableTypeConfiguration.java | 197 +
.../apache/ignite/portable/PortableWriter.java | 265 ++
.../apache/ignite/portable/package-info.java | 22 +
.../resources/META-INF/classnames.properties | 285 +-
.../GridPortableAffinityKeySelfTest.java | 215 +
.../GridPortableBuilderAdditionalSelfTest.java | 1001 +++++
.../portable/GridPortableBuilderSelfTest.java | 1007 +++++
...eBuilderStringAsCharsAdditionalSelfTest.java | 28 +
...ridPortableBuilderStringAsCharsSelfTest.java | 28 +
...idPortableMarshallerCtxDisabledSelfTest.java | 128 +
.../GridPortableMarshallerSelfTest.java | 3691 ++++++++++++++++++
.../GridPortableMetaDataDisabledSelfTest.java | 218 ++
.../portable/GridPortableMetaDataSelfTest.java | 343 ++
.../portable/GridPortableWildcardsSelfTest.java | 480 +++
.../GridPortableMarshalerAwareTestClass.java | 62 +
.../mutabletest/GridPortableTestClasses.java | 425 ++
.../portable/mutabletest/package-info.java | 22 +
.../ignite/internal/portable/package-info.java | 22 +
.../portable/test/GridPortableTestClass1.java | 28 +
.../portable/test/GridPortableTestClass2.java | 24 +
.../internal/portable/test/package-info.java | 22 +
.../test/subpackage/GridPortableTestClass3.java | 24 +
.../portable/test/subpackage/package-info.java | 22 +
...ClientNodePortableMetadataMultinodeTest.java | 277 ++
...GridCacheClientNodePortableMetadataTest.java | 280 ++
...ableObjectsAbstractDataStreamerSelfTest.java | 183 +
...bleObjectsAbstractMultiThreadedSelfTest.java | 222 ++
...ridCachePortableObjectsAbstractSelfTest.java | 958 +++++
.../GridCachePortableStoreAbstractSelfTest.java | 294 ++
.../GridCachePortableStoreObjectsSelfTest.java | 55 +
...GridCachePortableStorePortablesSelfTest.java | 67 +
...ridPortableCacheEntryMemorySizeSelfTest.java | 52 +
...leDuplicateIndexObjectsAbstractSelfTest.java | 153 +
.../DataStreamProcessorPortableSelfTest.java | 67 +
.../GridDataStreamerImplSelfTest.java | 338 ++
...ridCacheAffinityRoutingPortableSelfTest.java | 48 +
...lyPortableDataStreamerMultiNodeSelfTest.java | 29 +
...rtableDataStreamerMultithreadedSelfTest.java | 46 +
...artitionedOnlyPortableMultiNodeSelfTest.java | 28 +
...tionedOnlyPortableMultithreadedSelfTest.java | 46 +
.../GridCacheMemoryModePortableSelfTest.java | 36 +
...acheOffHeapTieredAtomicPortableSelfTest.java | 48 +
...eapTieredEvictionAtomicPortableSelfTest.java | 96 +
...heOffHeapTieredEvictionPortableSelfTest.java | 96 +
.../GridCacheOffHeapTieredPortableSelfTest.java | 48 +
...ateIndexObjectPartitionedAtomicSelfTest.java | 37 +
...xObjectPartitionedTransactionalSelfTest.java | 40 +
...AtomicNearDisabledOffheapTieredSelfTest.java | 29 +
...rtableObjectsAtomicNearDisabledSelfTest.java | 50 +
...tableObjectsAtomicOffheapTieredSelfTest.java | 29 +
.../GridCachePortableObjectsAtomicSelfTest.java | 50 +
...tionedNearDisabledOffheapTieredSelfTest.java | 30 +
...eObjectsPartitionedNearDisabledSelfTest.java | 50 +
...ObjectsPartitionedOffheapTieredSelfTest.java | 30 +
...CachePortableObjectsPartitionedSelfTest.java | 50 +
...sNearPartitionedByteArrayValuesSelfTest.java | 41 +
...sPartitionedOnlyByteArrayValuesSelfTest.java | 42 +
...dCachePortableObjectsReplicatedSelfTest.java | 50 +
...CachePortableObjectsAtomicLocalSelfTest.java | 32 +
...rtableObjectsLocalOffheapTieredSelfTest.java | 29 +
.../GridCachePortableObjectsLocalSelfTest.java | 50 +
.../ignite/testframework/junits/IgniteMock.java | 5 +
.../multijvm/IgniteCacheProcessProxy.java | 5 +
.../junits/multijvm/IgniteProcessProxy.java | 5 +
.../IgnitePortableCacheFullApiTestSuite.java | 38 +
.../IgnitePortableCacheTestSuite.java | 86 +
.../IgnitePortableObjectsTestSuite.java | 74 +
.../ignite/portable/test1/1.1/test1-1.1.jar | Bin 0 -> 2548 bytes
.../ignite/portable/test1/1.1/test1-1.1.pom | 9 +
.../portable/test1/maven-metadata-local.xml | 12 +
.../ignite/portable/test2/1.1/test2-1.1.jar | Bin 0 -> 1361 bytes
.../ignite/portable/test2/1.1/test2-1.1.pom | 9 +
.../portable/test2/maven-metadata-local.xml | 12 +
.../IgnitePortableCacheQueryTestSuite.java | 95 +
.../platform/memory/PlatformInputStream.java | 4 +-
.../platform/memory/PlatformOutputStream.java | 4 +-
.../org/apache/ignite/IgniteSpringBean.java | 7 +
parent/pom.xml | 8 +
162 files changed, 31734 insertions(+), 541 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/pom.xml
----------------------------------------------------------------------
diff --git a/modules/core/pom.xml b/modules/core/pom.xml
index c8abe15..6f1cbf8 100644
--- a/modules/core/pom.xml
+++ b/modules/core/pom.xml
@@ -33,6 +33,13 @@
<artifactId>ignite-core</artifactId>
<version>1.4.1-SNAPSHOT</version>
+ <repositories>
+ <repository>
+ <id>ignite-portables-test-repo</id>
+ <url>file://${basedir}/src/test/portables/repo</url>
+ </repository>
+ </repositories>
+
<dependencies>
<dependency>
<groupId>javax.cache</groupId>
@@ -164,6 +171,20 @@
<version>2.4</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite.portable</groupId>
+ <artifactId>test1</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite.portable</groupId>
+ <artifactId>test2</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/Ignite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/Ignite.java b/modules/core/src/main/java/org/apache/ignite/Ignite.java
index 209946b..d70a3a1 100644
--- a/modules/core/src/main/java/org/apache/ignite/Ignite.java
+++ b/modules/core/src/main/java/org/apache/ignite/Ignite.java
@@ -455,6 +455,13 @@ public interface Ignite extends AutoCloseable {
public <T extends IgnitePlugin> T plugin(String name) throws PluginNotFoundException;
/**
+ * Gets an instance of {@link IgnitePortables} interface.
+ *
+ * @return Instance of {@link IgnitePortables} interface.
+ */
+ public IgnitePortables portables();
+
+ /**
* Closes {@code this} instance of grid. This method is identical to calling
* {@link G#stop(String, boolean) G.stop(gridName, true)}.
* <p>
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteCache.java b/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
index fd0112c..c9ff955 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
@@ -25,6 +25,7 @@ import org.apache.ignite.cache.store.*;
import org.apache.ignite.cluster.*;
import org.apache.ignite.configuration.*;
import org.apache.ignite.lang.*;
+import org.apache.ignite.marshaller.portable.*;
import org.apache.ignite.mxbean.*;
import org.jetbrains.annotations.*;
@@ -35,7 +36,9 @@ import javax.cache.expiry.*;
import javax.cache.integration.*;
import javax.cache.processor.*;
import java.io.*;
+import java.sql.*;
import java.util.*;
+import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
@@ -112,6 +115,44 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
public IgniteCache<K, V> withNoRetries();
/**
+ * Returns cache that will operate with portable objects.
+ * <p>
+ * Cache returned by this method will not be forced to deserialize portable objects,
+ * so keys and values will be returned from cache API methods without changes. Therefore,
+ * signature of the cache can contain only following types:
+ * <ul>
+ * <li><code>org.apache.ignite.portable.PortableObject</code> for portable classes</li>
+ * <li>All primitives (byte, int, ...) and there boxed versions (Byte, Integer, ...)</li>
+ * <li>Arrays of primitives (byte[], int[], ...)</li>
+ * <li>{@link String} and array of {@link String}s</li>
+ * <li>{@link UUID} and array of {@link UUID}s</li>
+ * <li>{@link Date} and array of {@link Date}s</li>
+ * <li>{@link Timestamp} and array of {@link Timestamp}s</li>
+ * <li>Enums and array of enums</li>
+ * <li>
+ * Maps, collections and array of objects (but objects inside
+ * them will still be converted if they are portable)
+ * </li>
+ * </ul>
+ * <p>
+ * For example, if you use {@link Integer} as a key and {@code Value} class as a value
+ * (which will be stored in portable format), you should acquire following projection
+ * to avoid deserialization:
+ * <pre>
+ * IgniteCache<Integer, PortableObject> prj = cache.withKeepPortable();
+ *
+ * // Value is not deserialized and returned in portable format.
+ * PortableObject po = prj.get(1);
+ * </pre>
+ * <p>
+ * Note that this method makes sense only if cache is working in portable mode ({@link PortableMarshaller} is used).
+ * If not, this method is no-op and will return current cache.
+ *
+ * @return New cache instance for portable objects.
+ */
+ public <K1, V1> IgniteCache<K1, V1> withKeepPortable();
+
+ /**
* Executes {@link #localLoadCache(IgniteBiPredicate, Object...)} on all cache nodes.
*
* @param p Optional predicate (may be {@code null}). If provided, will be used to
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/IgnitePortables.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgnitePortables.java b/modules/core/src/main/java/org/apache/ignite/IgnitePortables.java
new file mode 100644
index 0000000..6800edc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/IgnitePortables.java
@@ -0,0 +1,362 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.portable.*;
+import org.apache.ignite.marshaller.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+
+/**
+ * Defines portable objects functionality. With portable objects you are able to:
+ * <ul>
+ * <li>Seamlessly interoperate between Java, .NET, and C++.</li>
+ * <li>Make any object portable with zero code change to your existing code.</li>
+ * <li>Nest portable objects within each other.</li>
+ * <li>Automatically handle {@code circular} or {@code null} references.</li>
+ * <li>Automatically convert collections and maps between Java, .NET, and C++.</li>
+ * <li>
+ * Optionally avoid deserialization of objects on the server side
+ * (objects are stored in {@link PortableObject} format).
+ * </li>
+ * <li>Avoid need to have concrete class definitions on the server side.</li>
+ * <li>Dynamically change structure of the classes without having to restart the cluster.</li>
+ * <li>Index into portable objects for querying purposes.</li>
+ * </ul>
+ * <h1 class="header">Working With Portables Directly</h1>
+ * Once an object is defined as portable,
+ * Ignite will always store it in memory in the portable (i.e. binary) format.
+ * User can choose to work either with the portable format or with the deserialized form
+ * (assuming that class definitions are present in the classpath).
+ * <p>
+ * To work with the portable format directly, user should create a special cache projection
+ * using {@link IgniteCache#withKeepPortable()} method and then retrieve individual fields as needed:
+ * <pre name=code class=java>
+ * IgniteCache<PortableObject, PortableObject> prj = cache.withKeepPortable();
+ *
+ * // Convert instance of MyKey to portable format.
+ * // We could also use PortableBuilder to create the key in portable format directly.
+ * PortableObject key = grid.portables().toPortable(new MyKey());
+ *
+ * PortableObject val = prj.get(key);
+ *
+ * String field = val.field("myFieldName");
+ * </pre>
+ * Alternatively, if we have class definitions in the classpath, we may choose to work with deserialized
+ * typed objects at all times. In this case we do incur the deserialization cost. However, if
+ * {@link PortableMarshaller#isKeepDeserialized()} is {@code true} then Ignite will only deserialize on the first access
+ * and will cache the deserialized object, so it does not have to be deserialized again:
+ * <pre name=code class=java>
+ * IgniteCache<MyKey.class, MyValue.class> cache = grid.cache(null);
+ *
+ * MyValue val = cache.get(new MyKey());
+ *
+ * // Normal java getter.
+ * String fieldVal = val.getMyFieldName();
+ * </pre>
+ * If we used, for example, one of the automatically handled portable types for a key, like integer,
+ * and still wanted to work with binary portable format for values, then we would declare cache projection
+ * as follows:
+ * <pre name=code class=java>
+ * IgniteCache<Integer.class, PortableObject> prj = cache.withKeepPortable();
+ * </pre>
+ * <h1 class="header">Automatic Portable Types</h1>
+ * Note that only portable classes are converted to {@link PortableObject} format. Following
+ * classes are never converted (e.g., {@link #toPortable(Object)} method will return original
+ * object, and instances of these classes will be stored in cache without changes):
+ * <ul>
+ * <li>All primitives (byte, int, ...) and there boxed versions (Byte, Integer, ...)</li>
+ * <li>Arrays of primitives (byte[], int[], ...)</li>
+ * <li>{@link String} and array of {@link String}s</li>
+ * <li>{@link UUID} and array of {@link UUID}s</li>
+ * <li>{@link Date} and array of {@link Date}s</li>
+ * <li>{@link Timestamp} and array of {@link Timestamp}s</li>
+ * <li>Enums and array of enums</li>
+ * <li>
+ * Maps, collections and array of objects (but objects inside
+ * them will still be converted if they are portable)
+ * </li>
+ * </ul>
+ * <h1 class="header">Working With Maps and Collections</h1>
+ * All maps and collections in the portable objects are serialized automatically. When working
+ * with different platforms, e.g. C++ or .NET, Ignite will automatically pick the most
+ * adequate collection or map in either language. For example, {@link ArrayList} in Java will become
+ * {@code List} in C#, {@link LinkedList} in Java is {@link LinkedList} in C#, {@link HashMap}
+ * in Java is {@code Dictionary} in C#, and {@link TreeMap} in Java becomes {@code SortedDictionary}
+ * in C#, etc.
+ * <h1 class="header">Building Portable Objects</h1>
+ * Ignite comes with {@link PortableBuilder} which allows to build portable objects dynamically:
+ * <pre name=code class=java>
+ * PortableBuilder builder = Ignition.ignite().portables().builder();
+ *
+ * builder.typeId("MyObject");
+ *
+ * builder.stringField("fieldA", "A");
+ * build.intField("fieldB", "B");
+ *
+ * PortableObject portableObj = builder.build();
+ * </pre>
+ * For the cases when class definition is present
+ * in the class path, it is also possible to populate a standard POJO and then
+ * convert it to portable format, like so:
+ * <pre name=code class=java>
+ * MyObject obj = new MyObject();
+ *
+ * obj.setFieldA("A");
+ * obj.setFieldB(123);
+ *
+ * PortableObject portableObj = Ignition.ignite().portables().toPortable(obj);
+ * </pre>
+ * NOTE: you don't need to convert typed objects to portable format before storing
+ * them in cache, Ignite will do that automatically.
+ * <h1 class="header">Portable Metadata</h1>
+ * Even though Ignite portable protocol only works with hash codes for type and field names
+ * to achieve better performance, Ignite provides metadata for all portable types which
+ * can be queried ar runtime via any of the {@link IgnitePortables#metadata(Class)}
+ * methods. Having metadata also allows for proper formatting of {@code PortableObject#toString()} method,
+ * even when portable objects are kept in binary format only, which may be necessary for audit reasons.
+ * <h1 class="header">Dynamic Structure Changes</h1>
+ * Since objects are always cached in the portable binary format, server does not need to
+ * be aware of the class definitions. Moreover, if class definitions are not present or not
+ * used on the server, then clients can continuously change the structure of the portable
+ * objects without having to restart the cluster. For example, if one client stores a
+ * certain class with fields A and B, and another client stores the same class with
+ * fields B and C, then the server-side portable object will have the fields A, B, and C.
+ * As the structure of a portable object changes, the new fields become available for SQL queries
+ * automatically.
+ * <h1 class="header">Configuration</h1>
+ * By default all your objects are considered as portables and no specific configuration is needed.
+ * However, in some cases, like when an object is used by both Java and .Net, you may need to specify portable objects
+ * explicitly by calling {@link PortableMarshaller#setClassNames(Collection)}.
+ * The only requirement Ignite imposes is that your object has an empty
+ * constructor. Note, that since server side does not have to know the class definition,
+ * you only need to list portable objects in configuration on the client side. However, if you
+ * list them on the server side as well, then you get the ability to deserialize portable objects
+ * into concrete types on the server as well as on the client.
+ * <p>
+ * Here is an example of portable configuration (note that star (*) notation is supported):
+ * <pre name=code class=xml>
+ * ...
+ * <!-- Explicit portable objects configuration. -->
+ * <property name="marshaller">
+ * <bean class="org.apache.ignite.marshaller.portable.PortableMarshaller">
+ * <property name="classNames">
+ * <list>
+ * <value>my.package.for.portable.objects.*</value>
+ * <value>org.apache.ignite.examples.client.portable.Employee</value>
+ * </list>
+ * </property>
+ * </bean>
+ * </property>
+ * ...
+ * </pre>
+ * or from code:
+ * <pre name=code class=java>
+ * IgniteConfiguration cfg = new IgniteConfiguration();
+ *
+ * PortableMarshaller marsh = new PortableMarshaller();
+ *
+ * marsh.setClassNames(Arrays.asList(
+ * Employee.class.getName(),
+ * Address.class.getName())
+ * );
+ *
+ * cfg.setMarshaller(marsh);
+ * </pre>
+ * You can also specify class name for a portable object via {@link PortableTypeConfiguration}.
+ * Do it in case if you need to override other configuration properties on per-type level, like
+ * ID-mapper, or serializer.
+ * <h1 class="header">Custom Affinity Keys</h1>
+ * Often you need to specify an alternate key (not the cache key) for affinity routing whenever
+ * storing objects in cache. For example, if you are caching {@code Employee} object with
+ * {@code Organization}, and want to colocate employees with organization they work for,
+ * so you can process them together, you need to specify an alternate affinity key.
+ * With portable objects you would have to do it as following:
+ * <pre name=code class=xml>
+ * <property name="marshaller">
+ * <bean class="org.gridgain.grid.marshaller.portable.PortableMarshaller">
+ * ...
+ * <property name="typeConfigurations">
+ * <list>
+ * <bean class="org.apache.ignite.portable.PortableTypeConfiguration">
+ * <property name="className" value="org.apache.ignite.examples.client.portable.EmployeeKey"/>
+ * <property name="affinityKeyFieldName" value="organizationId"/>
+ * </bean>
+ * </list>
+ * </property>
+ * ...
+ * </bean>
+ * </property>
+ * </pre>
+ * <h1 class="header">Serialization</h1>
+ * Serialization and deserialization works out-of-the-box in Ignite. However, you can provide your own custom
+ * serialization logic by optionally implementing {@link PortableMarshalAware} interface, like so:
+ * <pre name=code class=java>
+ * public class Address implements PortableMarshalAware {
+ * private String street;
+ * private int zip;
+ *
+ * // Empty constructor required for portable deserialization.
+ * public Address() {}
+ *
+ * @Override public void writePortable(PortableWriter writer) throws PortableException {
+ * writer.writeString("street", street);
+ * writer.writeInt("zip", zip);
+ * }
+ *
+ * @Override public void readPortable(PortableReader reader) throws PortableException {
+ * street = reader.readString("street");
+ * zip = reader.readInt("zip");
+ * }
+ * }
+ * </pre>
+ * Alternatively, if you cannot change class definitions, you can provide custom serialization
+ * logic in {@link PortableSerializer} either globally in {@link PortableMarshaller} or
+ * for a specific type via {@link PortableTypeConfiguration} instance.
+ * <p>
+ * Similar to java serialization you can use {@code writeReplace()} and {@code readResolve()} methods.
+ * <ul>
+ * <li>
+ * {@code readResolve} is defined as follows: {@code ANY-ACCESS-MODIFIER Object readResolve()}.
+ * It may be used to replace the de-serialized object by another one of your choice.
+ * </li>
+ * <li>
+ * {@code writeReplace} is defined as follows: {@code ANY-ACCESS-MODIFIER Object writeReplace()}. This method
+ * allows the developer to provide a replacement object that will be serialized instead of the original one.
+ * </li>
+ * </ul>
+ *
+ * <h1 class="header">Custom ID Mappers</h1>
+ * Ignite implementation uses name hash codes to generate IDs for class names or field names
+ * internally. However, in cases when you want to provide your own ID mapping schema,
+ * you can provide your own {@link PortableIdMapper} implementation.
+ * <p>
+ * ID-mapper may be provided either globally in {@link PortableMarshaller},
+ * or for a specific type via {@link PortableTypeConfiguration} instance.
+ * <h1 class="header">Query Indexing</h1>
+ * Portable objects can be indexed for querying by specifying index fields in
+ * {@link org.apache.ignite.cache.CacheTypeMetadata} inside of specific
+ * {@link org.apache.ignite.configuration.CacheConfiguration} instance,
+ * like so:
+ * <pre name=code class=xml>
+ * ...
+ * <bean class="org.apache.ignite.cache.CacheConfiguration">
+ * ...
+ * <property name="typeMetadata">
+ * <list>
+ * <bean class="CacheTypeMetadata">
+ * <property name="type" value="Employee"/>
+ *
+ * <!-- Fields to index in ascending order. -->
+ * <property name="ascendingFields">
+ * <map>
+ * <entry key="name" value="java.lang.String"/>
+ *
+ * <!-- Nested portable objects can also be indexed. -->
+ * <entry key="address.zip" value="java.lang.Integer"/>
+ * </map>
+ * </property>
+ * </bean>
+ * </list>
+ * </property>
+ * </bean>
+ * </pre>
+ */
+public interface IgnitePortables {
+ /**
+ * Gets type ID for given type name.
+ *
+ * @param typeName Type name.
+ * @return Type ID.
+ */
+ public int typeId(String typeName);
+
+ /**
+ * Converts provided object to instance of {@link PortableObject}.
+ *
+ * @param obj Object to convert.
+ * @return Converted object.
+ * @throws PortableException In case of error.
+ */
+ public <T> T toPortable(@Nullable Object obj) throws PortableException;
+
+ /**
+ * Creates new portable builder.
+ *
+ * @param typeId ID of the type.
+ * @return Newly portable builder.
+ */
+ public PortableBuilder builder(int typeId);
+
+ /**
+ * Creates new portable builder.
+ *
+ * @param typeName Type name.
+ * @return Newly portable builder.
+ */
+ public PortableBuilder builder(String typeName);
+
+ /**
+ * Creates portable builder initialized by existing portable object.
+ *
+ * @param portableObj Portable object to initialize builder.
+ * @return Portable builder.
+ */
+ public PortableBuilder builder(PortableObject portableObj);
+
+ /**
+ * Gets metadata for provided class.
+ *
+ * @param cls Class.
+ * @return Metadata.
+ * @throws PortableException In case of error.
+ */
+ @Nullable public PortableMetadata metadata(Class<?> cls) throws PortableException;
+
+ /**
+ * Gets metadata for provided class name.
+ *
+ * @param typeName Type name.
+ * @return Metadata.
+ * @throws PortableException In case of error.
+ */
+ @Nullable public PortableMetadata metadata(String typeName) throws PortableException;
+
+ /**
+ * Gets metadata for provided type ID.
+ *
+ * @param typeId Type ID.
+ * @return Metadata.
+ * @throws PortableException In case of error.
+ */
+ @Nullable public PortableMetadata metadata(int typeId) throws PortableException;
+
+ /**
+ * Gets metadata for all known types.
+ *
+ * @return Metadata.
+ * @throws PortableException In case of error.
+ */
+ public Collection<PortableMetadata> metadata() throws PortableException;
+}
+
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/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 3ad0f01..af2bbe8 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
@@ -153,6 +153,10 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
/** Default size for onheap SQL row cache size. */
public static final int DFLT_SQL_ONHEAP_ROW_CACHE_SIZE = 10 * 1024;
+ /** Default value for keep portable in store behavior .*/
+ @SuppressWarnings({"UnnecessaryBoxing", "BooleanConstructorCall"})
+ public static final Boolean DFLT_KEEP_PORTABLE_IN_STORE = new Boolean(true);
+
/** Cache name. */
private String name;
@@ -205,6 +209,9 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
private Factory storeFactory;
/** */
+ private Boolean keepPortableInStore = DFLT_KEEP_PORTABLE_IN_STORE;
+
+ /** */
private boolean loadPrevVal = DFLT_LOAD_PREV_VAL;
/** Node group resolver. */
@@ -366,6 +373,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
invalidate = cc.isInvalidate();
isReadThrough = cc.isReadThrough();
isWriteThrough = cc.isWriteThrough();
+ keepPortableInStore = cc.isKeepPortableInStore();
listenerConfigurations = cc.listenerConfigurations;
loadPrevVal = cc.isLoadPreviousValue();
longQryWarnTimeout = cc.getLongQueryWarningTimeout();
@@ -806,6 +814,38 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
}
/**
+ * Flag indicating that {@link CacheStore} implementation
+ * is working with portable objects instead of Java objects.
+ * Default value of this flag is {@link #DFLT_KEEP_PORTABLE_IN_STORE},
+ * because this is recommended behavior from performance standpoint.
+ * <p>
+ * If set to {@code false}, Ignite will deserialize keys and
+ * values stored in portable format before they are passed
+ * to cache store.
+ * <p>
+ * Note that setting this flag to {@code false} can simplify
+ * 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
+ * store is called.
+ *
+ * @return Keep portables in store flag.
+ */
+ public Boolean isKeepPortableInStore() {
+ return keepPortableInStore;
+ }
+
+ /**
+ * Sets keep portables in store flag.
+ *
+ * @param keepPortableInStore Keep portables in store flag.
+ */
+ public void setKeepPortableInStore(boolean keepPortableInStore) {
+ this.keepPortableInStore = keepPortableInStore;
+ }
+
+ /**
* Gets key topology resolver to provide mapping from keys to nodes.
*
* @return Key topology resolver to provide mapping from keys to nodes.
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/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 fd8b50c..01dadfd 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
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.managers.loadbalancer.*;
import org.apache.ignite.internal.managers.swapspace.*;
import org.apache.ignite.internal.processors.affinity.*;
import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.internal.processors.cache.portable.*;
import org.apache.ignite.internal.processors.cacheobject.*;
import org.apache.ignite.internal.processors.clock.*;
import org.apache.ignite.internal.processors.closure.*;
@@ -803,7 +804,7 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
return res;
if (cls.equals(IgniteCacheObjectProcessor.class))
- return (T)new IgniteCacheObjectProcessorImpl(this);
+ return (T)new CacheObjectPortableProcessorImpl(this);
if (cls.equals(CacheConflictResolutionManager.class))
return null;
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/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 bf47f63..391d3dc 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
@@ -36,6 +36,7 @@ import org.apache.ignite.internal.managers.swapspace.*;
import org.apache.ignite.internal.processors.*;
import org.apache.ignite.internal.processors.affinity.*;
import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.internal.processors.cache.portable.*;
import org.apache.ignite.internal.processors.cacheobject.*;
import org.apache.ignite.internal.processors.clock.*;
import org.apache.ignite.internal.processors.closure.*;
@@ -2666,6 +2667,11 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
}
/** {@inheritDoc} */
+ @Override public IgnitePortables portables() {
+ return ((CacheObjectPortableProcessor)ctx.cacheObjects()).portables();
+ }
+
+ /** {@inheritDoc} */
@Override public IgniteProductVersion version() {
return VER;
}
@@ -2960,7 +2966,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
return comp;
if (cls.equals(IgniteCacheObjectProcessor.class))
- return (T)new IgniteCacheObjectProcessorImpl(ctx);
+ return (T)new CacheObjectPortableProcessorImpl(ctx);
if (cls.equals(DiscoveryNodeValidationProcessor.class))
return (T)new OsDiscoveryNodeValidationProcessor(ctx);
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/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 7fe8da8..2acfd2b 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
@@ -22,6 +22,7 @@ import org.apache.ignite.internal.*;
import org.apache.ignite.internal.managers.checkpoint.*;
import org.apache.ignite.internal.managers.deployment.*;
import org.apache.ignite.internal.managers.eventstorage.*;
+import org.apache.ignite.internal.portable.*;
import org.apache.ignite.internal.processors.affinity.*;
import org.apache.ignite.internal.processors.cache.*;
import org.apache.ignite.internal.processors.cache.distributed.*;
@@ -600,6 +601,11 @@ public class GridIoMessageFactory implements MessageFactory {
break;
+ case 113:
+ msg = new PortableObjectImpl();
+
+ break;
+
// [-3..112] - this
// [120..123] - DR
// [-4..-22] - SQL
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableMarshaller.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableMarshaller.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableMarshaller.java
new file mode 100644
index 0000000..2969261
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/GridPortableMarshaller.java
@@ -0,0 +1,304 @@
+/*
+ * 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.portable.streams.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+/**
+ * Portable objects marshaller.
+ */
+public class GridPortableMarshaller {
+ /** */
+ public static final ThreadLocal<Boolean> KEEP_PORTABLES = new ThreadLocal<Boolean>() {
+ @Override protected Boolean initialValue() {
+ return true;
+ }
+ };
+
+ /** */
+ static final byte OPTM_MARSH = -2;
+
+ /** */
+ public static final byte BYTE = 1;
+
+ /** */
+ public static final byte SHORT = 2;
+
+ /** */
+ public static final byte INT = 3;
+
+ /** */
+ public static final byte LONG = 4;
+
+ /** */
+ public static final byte FLOAT = 5;
+
+ /** */
+ public static final byte DOUBLE = 6;
+
+ /** */
+ public static final byte CHAR = 7;
+
+ /** */
+ public static final byte BOOLEAN = 8;
+
+ /** */
+ public static final byte DECIMAL = 30;
+
+ /** */
+ public static final byte STRING = 9;
+
+ /** */
+ public static final byte UUID = 10;
+
+ /** */
+ public static final byte DATE = 11;
+
+ /** */
+ public static final byte BYTE_ARR = 12;
+
+ /** */
+ public static final byte SHORT_ARR = 13;
+
+ /** */
+ public static final byte INT_ARR = 14;
+
+ /** */
+ public static final byte LONG_ARR = 15;
+
+ /** */
+ public static final byte FLOAT_ARR = 16;
+
+ /** */
+ public static final byte DOUBLE_ARR = 17;
+
+ /** */
+ public static final byte CHAR_ARR = 18;
+
+ /** */
+ public static final byte BOOLEAN_ARR = 19;
+
+ /** */
+ public static final byte DECIMAL_ARR = 31;
+
+ /** */
+ public static final byte STRING_ARR = 20;
+
+ /** */
+ public static final byte UUID_ARR = 21;
+
+ /** */
+ public static final byte DATE_ARR = 22;
+
+ /** */
+ public static final byte OBJ_ARR = 23;
+
+ /** */
+ public static final byte COL = 24;
+
+ /** */
+ public static final byte MAP = 25;
+
+ /** */
+ public static final byte MAP_ENTRY = 26;
+
+ /** */
+ public static final byte PORTABLE_OBJ = 27;
+
+ /** */
+ public static final byte ENUM = 28;
+
+ /** */
+ public static final byte ENUM_ARR = 29;
+
+ /** */
+ public static final byte CLASS = 32;
+
+ /** */
+ public static final byte NULL = (byte)101;
+
+ /** */
+ public static final byte HANDLE = (byte)102;
+
+ /** */
+ public static final byte OBJ = (byte)103;
+
+ /** */
+ static final byte USER_SET = -1;
+
+ /** */
+ static final byte USER_COL = 0;
+
+ /** */
+ static final byte ARR_LIST = 1;
+
+ /** */
+ static final byte LINKED_LIST = 2;
+
+ /** */
+ static final byte HASH_SET = 3;
+
+ /** */
+ static final byte LINKED_HASH_SET = 4;
+
+ /** */
+ static final byte TREE_SET = 5;
+
+ /** */
+ static final byte CONC_SKIP_LIST_SET = 6;
+
+ /** */
+ static final byte HASH_MAP = 1;
+
+ /** */
+ static final byte LINKED_HASH_MAP = 2;
+
+ /** */
+ static final byte TREE_MAP = 3;
+
+ /** */
+ static final byte CONC_HASH_MAP = 4;
+
+ /** */
+ static final byte PROPERTIES_MAP = 5;
+
+ /** */
+ static final int OBJECT_TYPE_ID = -1;
+
+ /** */
+ static final int UNREGISTERED_TYPE_ID = 0;
+
+ /** */
+ static final int TYPE_ID_POS = 2;
+
+ /** */
+ static final int HASH_CODE_POS = 6;
+
+ /** */
+ static final int TOTAL_LEN_POS = 10;
+
+ /** */
+ static final byte RAW_DATA_OFF_POS = 14;
+
+ /** */
+ static final int CLS_NAME_POS = 18;
+
+ /** */
+ static final byte DFLT_HDR_LEN = 18;
+
+ /** */
+ private final PortableContext ctx;
+
+ /**
+ * @param ctx Context.
+ */
+ public GridPortableMarshaller(PortableContext ctx) {
+ this.ctx = ctx;
+ }
+
+ /**
+ * @param obj Object to marshal.
+ * @param off Offset.
+ * @return Byte array.
+ * @throws PortableException In case of error.
+ */
+ public byte[] marshal(@Nullable Object obj, int off) throws PortableException {
+ if (obj == null)
+ return new byte[] { NULL };
+
+ try (PortableWriterExImpl writer = new PortableWriterExImpl(ctx, off)) {
+ writer.marshal(obj, false);
+
+ return writer.array();
+ }
+ }
+
+ /**
+ * @param bytes Bytes array.
+ * @return Portable object.
+ * @throws PortableException In case of error.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable public <T> T unmarshal(byte[] bytes, @Nullable ClassLoader clsLdr) throws PortableException {
+ assert bytes != null;
+
+ PortableReaderExImpl reader = new PortableReaderExImpl(ctx, bytes, 0, clsLdr);
+
+ return (T)reader.unmarshal();
+ }
+
+ /**
+ * @param in Input stream.
+ * @return Portable object.
+ * @throws PortableException In case of error.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable public <T> T unmarshal(PortableInputStream in) throws PortableException {
+ return (T)reader(in).unmarshal();
+ }
+
+ /**
+ * @param arr Byte array.
+ * @param ldr Class loader.
+ * @return Deserialized object.
+ * @throws PortableException In case of error.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable public <T> T deserialize(byte[] arr, @Nullable ClassLoader ldr) throws PortableException {
+ assert arr != null;
+ assert arr.length > 0;
+
+ if (arr[0] == NULL)
+ return null;
+
+ PortableReaderExImpl reader = new PortableReaderExImpl(ctx, arr, 0, ldr);
+
+ return (T)reader.deserialize();
+ }
+
+ /**
+ * Gets writer for the given output stream.
+ *
+ * @param out Output stream.
+ * @return Writer.
+ */
+ public PortableWriterExImpl writer(PortableOutputStream out) {
+ return new PortableWriterExImpl(ctx, out, 0);
+ }
+
+ /**
+ * Gets reader for the given input stream.
+ *
+ * @param in Input stream.
+ * @return Reader.
+ */
+ public PortableReaderExImpl reader(PortableInputStream in) {
+ // TODO: IGNITE-1272 - Is class loader needed here?
+ return new PortableReaderExImpl(ctx, in, in.position(), null);
+ }
+
+ /**
+ * @return Context.
+ */
+ public PortableContext context() {
+ return ctx;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableAbstractLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableAbstractLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableAbstractLazyValue.java
new file mode 100644
index 0000000..83b3050
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableAbstractLazyValue.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ *
+ */
+abstract class PortableAbstractLazyValue implements PortableLazyValue {
+ /** */
+ protected Object val;
+
+ /** */
+ protected final PortableBuilderReader reader;
+
+ /** */
+ protected final int valOff;
+
+ /**
+ * @param reader Reader.
+ * @param valOff Value.
+ */
+ protected PortableAbstractLazyValue(PortableBuilderReader reader, int valOff) {
+ this.reader = reader;
+ this.valOff = valOff;
+ }
+
+ /**
+ * @return Value.
+ */
+ protected abstract Object init();
+
+ /** {@inheritDoc} */
+ @Override public Object value() {
+ if (val == null) {
+ val = init();
+
+ assert val != null;
+ }
+
+ return val;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderEnum.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderEnum.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderEnum.java
new file mode 100644
index 0000000..9d29669
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderEnum.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+/**
+ *
+ */
+public class PortableBuilderEnum implements PortableBuilderSerializationAware {
+ /** */
+ private final int ordinal;
+
+ /** */
+ private final int typeId;
+
+ /** */
+ private final String clsName;
+
+ /**
+ * @param typeId Type ID.
+ * @param anEnum Enum instance.
+ */
+ public PortableBuilderEnum(int typeId, Enum anEnum) {
+ ordinal = anEnum.ordinal();
+ this.typeId = typeId;
+ clsName = null;
+ }
+
+ /**
+ * @param reader PortableBuilderReader.
+ */
+ public PortableBuilderEnum(PortableBuilderReader reader) {
+ int typeId = reader.readInt();
+
+ if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+ clsName = reader.readString();
+
+ Class cls;
+
+ try {
+ // TODO: IGNITE-1272 - Is class loader needed here?
+ cls = U.forName(reader.readString(), null);
+ }
+ catch (ClassNotFoundException e) {
+ throw new PortableInvalidClassException("Failed to load the class: " + clsName, e);
+ }
+
+ this.typeId = reader.portableContext().descriptorForClass(cls).typeId();
+ }
+ else {
+ this.typeId = typeId;
+ this.clsName = null;
+ }
+
+ ordinal = reader.readInt();
+ }
+
+ /**
+ * @return Ordinal.
+ */
+ public int getOrdinal() {
+ return ordinal;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ writer.writeByte(GridPortableMarshaller.ENUM);
+
+ if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+ writer.writeInt(GridPortableMarshaller.UNREGISTERED_TYPE_ID);
+ writer.writeString(clsName);
+ }
+ else
+ writer.writeInt(typeId);
+
+ writer.writeInt(ordinal);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ PortableBuilderEnum that = (PortableBuilderEnum)o;
+
+ return ordinal == that.ordinal && typeId == that.typeId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int result = ordinal;
+
+ result = 31 * result + typeId;
+
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderImpl.java
new file mode 100644
index 0000000..26f1d25
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableBuilderImpl.java
@@ -0,0 +1,519 @@
+/*
+ * 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.processors.cache.portable.*;
+import org.apache.ignite.internal.util.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.util.*;
+
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.*;
+
+/**
+ *
+ */
+public class PortableBuilderImpl implements PortableBuilder {
+ /** */
+ private static final Object REMOVED_FIELD_MARKER = new Object();
+
+ /** */
+ private final PortableContext ctx;
+
+ /** */
+ private final int typeId;
+
+ /** May be null. */
+ private String typeName;
+
+ /** May be null. */
+ private String clsNameToWrite;
+
+ /** */
+ private boolean registeredType = true;
+
+ /** */
+ private Map<String, Object> assignedVals;
+
+ /** */
+ private Map<Integer, Object> readCache;
+
+ /** Position of object in source array, or -1 if object is not created from PortableObject. */
+ private final int start;
+
+ /** Total header length */
+ private final int hdrLen;
+
+ /**
+ * Context of PortableObject reading process. Or {@code null} if object is not created from PortableObject.
+ */
+ private final PortableBuilderReader reader;
+
+ /** */
+ private int hashCode;
+
+ /**
+ * @param clsName Class name.
+ * @param ctx Portable context.
+ */
+ public PortableBuilderImpl(PortableContext ctx, String clsName) {
+ this(ctx, ctx.typeId(clsName), PortableContext.typeName(clsName));
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @param ctx Portable context.
+ */
+ public PortableBuilderImpl(PortableContext ctx, int typeId) {
+ this(ctx, typeId, null);
+ }
+
+ /**
+ * @param typeName Type name.
+ * @param ctx Context.
+ * @param typeId Type id.
+ */
+ public PortableBuilderImpl(PortableContext ctx, int typeId, String typeName) {
+ this.typeId = typeId;
+ this.typeName = typeName;
+ this.ctx = ctx;
+
+ start = -1;
+ reader = null;
+ hdrLen = DFLT_HDR_LEN;
+
+ readCache = Collections.emptyMap();
+ }
+
+ /**
+ * @param obj Object to wrap.
+ */
+ public PortableBuilderImpl(PortableObjectImpl obj) {
+ this(new PortableBuilderReader(obj), obj.start());
+
+ reader.registerObject(this);
+ }
+
+ /**
+ * @param reader ctx
+ * @param start Start.
+ */
+ PortableBuilderImpl(PortableBuilderReader reader, int start) {
+ this.reader = reader;
+ this.start = start;
+
+ int typeId = reader.readIntAbsolute(start + TYPE_ID_POS);
+ ctx = reader.portableContext();
+ hashCode = reader.readIntAbsolute(start + HASH_CODE_POS);
+
+ if (typeId == UNREGISTERED_TYPE_ID) {
+ int mark = reader.position();
+
+ reader.position(start + CLS_NAME_POS);
+
+ clsNameToWrite = reader.readString();
+
+ Class cls;
+
+ try {
+ // TODO: IGNITE-1272 - Is class loader needed here?
+ cls = U.forName(clsNameToWrite, null);
+ }
+ catch (ClassNotFoundException e) {
+ throw new PortableInvalidClassException("Failed to load the class: " + clsNameToWrite, e);
+ }
+
+ this.typeId = ctx.descriptorForClass(cls).typeId();
+
+ registeredType = false;
+
+ hdrLen = reader.position() - mark;
+
+ reader.position(mark);
+ }
+ else {
+ this.typeId = typeId;
+ hdrLen = DFLT_HDR_LEN;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public PortableObject build() {
+ try (PortableWriterExImpl writer = new PortableWriterExImpl(ctx, 0, typeId, false)) {
+
+ PortableBuilderSerializer serializationCtx = new PortableBuilderSerializer();
+
+ serializationCtx.registerObjectWriting(this, 0);
+
+ serializeTo(writer, serializationCtx);
+
+ byte[] arr = writer.array();
+
+ return new PortableObjectImpl(ctx, arr, 0);
+ }
+ }
+
+ /**
+ * @param writer Writer.
+ * @param serializer Serializer.
+ */
+ void serializeTo(PortableWriterExImpl writer, PortableBuilderSerializer serializer) {
+ writer.doWriteByte(GridPortableMarshaller.OBJ);
+ writer.doWriteBoolean(true);
+ writer.doWriteInt(registeredType ? typeId : UNREGISTERED_TYPE_ID);
+ writer.doWriteInt(hashCode);
+
+ // Length and raw offset.
+ writer.reserve(8);
+
+ if (!registeredType)
+ writer.writeString(clsNameToWrite);
+
+
+ Set<Integer> remainsFlds = null;
+
+ if (reader != null) {
+ Map<Integer, Object> assignedFldsById;
+
+ if (assignedVals != null) {
+ assignedFldsById = U.newHashMap(assignedVals.size());
+
+ for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
+ int fldId = ctx.fieldId(typeId, entry.getKey());
+
+ assignedFldsById.put(fldId, entry.getValue());
+ }
+
+ remainsFlds = assignedFldsById.keySet();
+ }
+ else
+ assignedFldsById = Collections.emptyMap();
+
+ int rawOff = start + reader.readIntAbsolute(start + RAW_DATA_OFF_POS);
+
+ reader.position(start + hdrLen);
+
+ int cpStart = -1;
+
+ while (reader.position() < rawOff) {
+ int fldId = reader.readInt();
+
+ int len = reader.readInt();
+
+ if (assignedFldsById.containsKey(fldId)) {
+ if (cpStart >= 0) {
+ writer.write(reader.array(), cpStart, reader.position() - 4 - 4 - cpStart);
+
+ cpStart = -1;
+ }
+
+ Object assignedVal = assignedFldsById.remove(fldId);
+
+ reader.skip(len);
+
+ if (assignedVal != REMOVED_FIELD_MARKER) {
+ writer.writeInt(fldId);
+
+ int lenPos = writer.reserveAndMark(4);
+
+ serializer.writeValue(writer, assignedVal);
+
+ writer.writeDelta(lenPos);
+ }
+ }
+ else {
+ if (len != 0 && PortableUtils.isPlainType(reader.readByte(0))) {
+ if (cpStart < 0)
+ cpStart = reader.position() - 4 - 4;
+
+ reader.skip(len);
+ }
+ else {
+ if (cpStart >= 0) {
+ writer.write(reader.array(), cpStart, reader.position() - 4 - cpStart);
+
+ cpStart = -1;
+ }
+ else
+ writer.writeInt(fldId);
+
+ Object val;
+
+ if (len == 0)
+ val = null;
+ else if (readCache == null) {
+ int savedPos = reader.position();
+
+ val = reader.parseValue();
+
+ assert reader.position() == savedPos + len;
+ }
+ else {
+ val = readCache.get(fldId);
+
+ reader.skip(len);
+ }
+
+ int lenPos = writer.reserveAndMark(4);
+
+ serializer.writeValue(writer, val);
+
+ writer.writeDelta(lenPos);
+ }
+ }
+ }
+
+ if (cpStart >= 0)
+ writer.write(reader.array(), cpStart, reader.position() - cpStart);
+ }
+
+ if (assignedVals != null && (remainsFlds == null || !remainsFlds.isEmpty())) {
+ boolean metadataEnabled = ctx.isMetaDataEnabled(typeId);
+
+ PortableMetadata metadata = null;
+
+ if (metadataEnabled)
+ metadata = ctx.metaData(typeId);
+
+ Map<String, String> newFldsMetadata = null;
+
+ for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
+ Object val = entry.getValue();
+
+ if (val == REMOVED_FIELD_MARKER)
+ continue;
+
+ String name = entry.getKey();
+
+ int fldId = ctx.fieldId(typeId, name);
+
+ if (remainsFlds != null && !remainsFlds.contains(fldId))
+ continue;
+
+ writer.writeInt(fldId);
+
+ int lenPos = writer.reserveAndMark(4);
+
+ serializer.writeValue(writer, val);
+
+ writer.writeDelta(lenPos);
+
+ if (metadataEnabled) {
+ String oldFldTypeName = metadata == null ? null : metadata.fieldTypeName(name);
+
+ String newFldTypeName;
+
+ if (val instanceof PortableValueWithType)
+ newFldTypeName = ((PortableValueWithType)val).typeName();
+ else {
+ byte type = PortableUtils.typeByClass(val.getClass());
+
+ newFldTypeName = CacheObjectPortableProcessorImpl.fieldTypeName(type);
+ }
+
+ if (oldFldTypeName == null) {
+ // It's a new field, we have to add it to metadata.
+
+ if (newFldsMetadata == null)
+ newFldsMetadata = new HashMap<>();
+
+ newFldsMetadata.put(name, newFldTypeName);
+ }
+ else {
+ if (!"Object".equals(oldFldTypeName) && !oldFldTypeName.equals(newFldTypeName)) {
+ throw new PortableException(
+ "Wrong value has been set [" +
+ "typeName=" + (typeName == null ? metadata.typeName() : typeName) +
+ ", fieldName=" + name +
+ ", fieldType=" + oldFldTypeName +
+ ", assignedValueType=" + newFldTypeName +
+ ", assignedValue=" + (((PortableValueWithType)val).value()) + ']'
+ );
+ }
+ }
+ }
+ }
+
+ if (newFldsMetadata != null) {
+ String typeName = this.typeName;
+
+ if (typeName == null)
+ typeName = metadata.typeName();
+
+ ctx.updateMetaData(typeId, typeName, newFldsMetadata);
+ }
+ }
+
+ writer.writeRawOffsetIfNeeded();
+
+ if (reader != null) {
+ int rawOff = reader.readIntAbsolute(start + RAW_DATA_OFF_POS);
+ int len = reader.readIntAbsolute(start + TOTAL_LEN_POS);
+
+ if (rawOff < len)
+ writer.write(reader.array(), rawOff, len - rawOff);
+ }
+
+ writer.writeLength();
+ }
+
+ /** {@inheritDoc} */
+ @Override public PortableBuilderImpl hashCode(int hashCode) {
+ this.hashCode = hashCode;
+
+ return this;
+ }
+
+ /**
+ *
+ */
+ private void ensureReadCacheInit() {
+ if (readCache == null) {
+ Map<Integer, Object> readCache = new HashMap<>();
+
+ int pos = start + hdrLen;
+ int end = start + reader.readIntAbsolute(start + RAW_DATA_OFF_POS);
+
+ while (pos < end) {
+ int fieldId = reader.readIntAbsolute(pos);
+
+ pos += 4;
+
+ int len = reader.readIntAbsolute(pos);
+
+ pos += 4;
+
+ Object val = reader.getValueQuickly(pos, len);
+
+ readCache.put(fieldId, val);
+
+ pos += len;
+ }
+
+ this.readCache = readCache;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public <F> F getField(String name) {
+ Object val;
+
+ if (assignedVals != null && assignedVals.containsKey(name)) {
+ val = assignedVals.get(name);
+
+ if (val == REMOVED_FIELD_MARKER)
+ return null;
+ }
+ else {
+ ensureReadCacheInit();
+
+ int fldId = ctx.fieldId(typeId, name);
+
+ val = readCache.get(fldId);
+ }
+
+ return (F)PortableUtils.unwrapLazy(val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PortableBuilder setField(String name, Object val) {
+ GridArgumentCheck.notNull(val, "val");
+
+ if (assignedVals == null)
+ assignedVals = new LinkedHashMap<>();
+
+ Object oldVal = assignedVals.put(name, val);
+
+ if (oldVal instanceof PortableValueWithType) {
+ ((PortableValueWithType)oldVal).value(val);
+
+ assignedVals.put(name, oldVal);
+ }
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T> PortableBuilder setField(String name, @Nullable T val, Class<? super T> type) {
+ if (assignedVals == null)
+ assignedVals = new LinkedHashMap<>();
+
+ //int fldId = ctx.fieldId(typeId, fldName);
+
+ assignedVals.put(name, new PortableValueWithType(PortableUtils.typeByClass(type), val));
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public PortableBuilder setField(String name, @Nullable PortableBuilder builder) {
+ if (builder == null)
+ return setField(name, null, Object.class);
+ else
+ return setField(name, (Object)builder);
+ }
+
+ /**
+ * Removes field from portable object.
+ *
+ * @param name Field name.
+ * @return {@code this} instance for chaining.
+ */
+ @Override public PortableBuilderImpl removeField(String name) {
+ if (assignedVals == null)
+ assignedVals = new LinkedHashMap<>();
+
+ assignedVals.put(name, REMOVED_FIELD_MARKER);
+
+ return this;
+ }
+
+ /**
+ * Creates builder initialized by specified portable object.
+ *
+ * @param obj Portable object to initialize builder.
+ * @return New builder.
+ */
+ public static PortableBuilderImpl wrap(PortableObject obj) {
+ PortableObjectImpl heapObj;
+
+ if (obj instanceof PortableObjectOffheapImpl)
+ heapObj = (PortableObjectImpl)((PortableObjectOffheapImpl)obj).heapCopy();
+ else
+ heapObj = (PortableObjectImpl)obj;
+
+ return new PortableBuilderImpl(heapObj);
+ }
+
+ /**
+ * @return Object start position in source array.
+ */
+ int start() {
+ return start;
+ }
+
+ /**
+ * @return Object type id.
+ */
+ int typeId() {
+ return typeId;
+ }
+}