Return-Path: X-Original-To: apmail-ignite-commits-archive@minotaur.apache.org Delivered-To: apmail-ignite-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 1406B1811C for ; Thu, 27 Aug 2015 00:11:28 +0000 (UTC) Received: (qmail 38414 invoked by uid 500); 27 Aug 2015 00:11:28 -0000 Delivered-To: apmail-ignite-commits-archive@ignite.apache.org Received: (qmail 38340 invoked by uid 500); 27 Aug 2015 00:11:28 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 37886 invoked by uid 99); 27 Aug 2015 00:11:27 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 27 Aug 2015 00:11:27 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 68ADFE7140; Thu, 27 Aug 2015 00:11:27 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: vkulichenko@apache.org To: commits@ignite.apache.org Date: Thu, 27 Aug 2015 00:11:39 -0000 Message-Id: <59c551b2eb8446bea23c92d224a60af6@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [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 Authored: Tue Aug 25 10:05:15 2015 +0300 Committer: Denis Magda 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 @@ ignite-core 1.4.1-SNAPSHOT + + + ignite-portables-test-repo + file://${basedir}/src/test/portables/repo + + + javax.cache @@ -164,6 +171,20 @@ 2.4 test + + + org.apache.ignite.portable + test1 + 1.1 + test + + + + org.apache.ignite.portable + test2 + 1.1 + test + 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 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)}. *

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 extends javax.cache.Cache, IgniteAsyncS public IgniteCache withNoRetries(); /** + * Returns cache that will operate with portable objects. + *

+ * 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: + *

    + *
  • org.apache.ignite.portable.PortableObject for portable classes
  • + *
  • All primitives (byte, int, ...) and there boxed versions (Byte, Integer, ...)
  • + *
  • Arrays of primitives (byte[], int[], ...)
  • + *
  • {@link String} and array of {@link String}s
  • + *
  • {@link UUID} and array of {@link UUID}s
  • + *
  • {@link Date} and array of {@link Date}s
  • + *
  • {@link Timestamp} and array of {@link Timestamp}s
  • + *
  • Enums and array of enums
  • + *
  • + * Maps, collections and array of objects (but objects inside + * them will still be converted if they are portable) + *
  • + *
+ *

+ * 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: + *

+     * IgniteCache prj = cache.withKeepPortable();
+     *
+     * // Value is not deserialized and returned in portable format.
+     * PortableObject po = prj.get(1);
+     * 
+ *

+ * 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 IgniteCache 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: + *

    + *
  • Seamlessly interoperate between Java, .NET, and C++.
  • + *
  • Make any object portable with zero code change to your existing code.
  • + *
  • Nest portable objects within each other.
  • + *
  • Automatically handle {@code circular} or {@code null} references.
  • + *
  • Automatically convert collections and maps between Java, .NET, and C++.
  • + *
  • + * Optionally avoid deserialization of objects on the server side + * (objects are stored in {@link PortableObject} format). + *
  • + *
  • Avoid need to have concrete class definitions on the server side.
  • + *
  • Dynamically change structure of the classes without having to restart the cluster.
  • + *
  • Index into portable objects for querying purposes.
  • + *
+ *

Working With Portables Directly

+ * 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). + *

+ * 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: + *

+ * 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");
+ * 
+ * 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: + *
+ * IgniteCache<MyKey.class, MyValue.class> cache = grid.cache(null);
+ *
+ * MyValue val = cache.get(new MyKey());
+ *
+ * // Normal java getter.
+ * String fieldVal = val.getMyFieldName();
+ * 
+ * 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: + *
+ * IgniteCache<Integer.class, PortableObject> prj = cache.withKeepPortable();
+ * 
+ *

Automatic Portable Types

+ * 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): + *
    + *
  • All primitives (byte, int, ...) and there boxed versions (Byte, Integer, ...)
  • + *
  • Arrays of primitives (byte[], int[], ...)
  • + *
  • {@link String} and array of {@link String}s
  • + *
  • {@link UUID} and array of {@link UUID}s
  • + *
  • {@link Date} and array of {@link Date}s
  • + *
  • {@link Timestamp} and array of {@link Timestamp}s
  • + *
  • Enums and array of enums
  • + *
  • + * Maps, collections and array of objects (but objects inside + * them will still be converted if they are portable) + *
  • + *
+ *

Working With Maps and Collections

+ * 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. + *

Building Portable Objects

+ * Ignite comes with {@link PortableBuilder} which allows to build portable objects dynamically: + *
+ * PortableBuilder builder = Ignition.ignite().portables().builder();
+ *
+ * builder.typeId("MyObject");
+ *
+ * builder.stringField("fieldA", "A");
+ * build.intField("fieldB", "B");
+ *
+ * PortableObject portableObj = builder.build();
+ * 
+ * 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: + *
+ * MyObject obj = new MyObject();
+ *
+ * obj.setFieldA("A");
+ * obj.setFieldB(123);
+ *
+ * PortableObject portableObj = Ignition.ignite().portables().toPortable(obj);
+ * 
+ * NOTE: you don't need to convert typed objects to portable format before storing + * them in cache, Ignite will do that automatically. + *

Portable Metadata

+ * 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. + *

Dynamic Structure Changes

+ * 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. + *

Configuration

+ * 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. + *

+ * Here is an example of portable configuration (note that star (*) notation is supported): + *

+ * ...
+ * <!-- 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>
+ * ...
+ * 
+ * or from code: + *
+ * IgniteConfiguration cfg = new IgniteConfiguration();
+ *
+ * PortableMarshaller marsh = new PortableMarshaller();
+ *
+ * marsh.setClassNames(Arrays.asList(
+ *     Employee.class.getName(),
+ *     Address.class.getName())
+ * );
+ *
+ * cfg.setMarshaller(marsh);
+ * 
+ * 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. + *

Custom Affinity Keys

+ * 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: + *
+ * <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>
+ * 
+ *

Serialization

+ * 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: + *
+ * 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");
+ *     }
+ * }
+ * 
+ * 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. + *

+ * Similar to java serialization you can use {@code writeReplace()} and {@code readResolve()} methods. + *

    + *
  • + * {@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. + *
  • + *
  • + * {@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. + *
  • + *
+ * + *

Custom ID Mappers

+ * 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. + *

+ * ID-mapper may be provided either globally in {@link PortableMarshaller}, + * or for a specific type via {@link PortableTypeConfiguration} instance. + *

Query Indexing

+ * 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: + *
+ * ...
+ * <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>
+ * 
+ */ +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 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 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 extends MutableConfiguration { /** 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 extends MutableConfiguration { 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 extends MutableConfiguration { 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 extends MutableConfiguration { } /** + * 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. + *

+ * If set to {@code false}, Ignite will deserialize keys and + * values stored in portable format before they are passed + * to cache store. + *

+ * 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 KEEP_PORTABLES = new ThreadLocal() { + @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 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 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 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 assignedVals; + + /** */ + private Map 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 remainsFlds = null; + + if (reader != null) { + Map assignedFldsById; + + if (assignedVals != null) { + assignedFldsById = U.newHashMap(assignedVals.size()); + + for (Map.Entry 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 newFldsMetadata = null; + + for (Map.Entry 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 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 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 PortableBuilder setField(String name, @Nullable T val, Class 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; + } +}