org.apache.ignite.internal.portable.api

## Interface IgnitePortables

All Known Implementing Classes:
  IgnitePortablesImpl

    public interface IgnitePortables

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 `circular` or `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 `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 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 `PortableMarshaller.isKeepDeserialized()` is `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 `PortableObject` format. Following classes are never converted (e.g., `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[], ...)
- `String` and array of `String`s
- `UUID` and array of `UUID`s
- `Date` and array of `Date`s
- `Timestamp` and array of `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, `ArrayList` in Java will become `List` in C#, `LinkedList` in Java is `LinkedList` in C#, `HashMap` in Java is `Dictionary` in C#, and `TreeMap` in Java becomes `SortedDictionary` in C#, etc.

# Building Portable Objects

Ignite comes with `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 `metadata(Class)` methods. Having metadata also allows for proper formatting of `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 `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.internal.portable.api.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 `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 `Employee` object with `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.internal.portable.api.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 `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 `PortableSerializer` either globally in `PortableMarshaller` or for a specific type via `PortableTypeConfiguration` instance.

Similar to java serialization you can use `writeReplace()` and `readResolve()` methods.

- `readResolve` is defined as follows: `ANY-ACCESS-MODIFIER Object readResolve()`. It may be used to replace the de-serialized object by another one of your choice.
- `writeReplace` is defined as follows: `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 `PortableIdMapper` implementation.

ID-mapper may be provided either globally in `PortableMarshaller`, or for a specific type via `PortableTypeConfiguration` instance.

# Query Indexing

Portable objects can be indexed for querying by specifying index fields in `CacheTypeMetadata` inside of specific `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>

### Method Summary

Methods
`PortableBuilder` `builder(int typeId)` Creates new portable builder.
`PortableBuilder` `builder(PortableObject portableObj)` Creates portable builder initialized by existing portable object.
`PortableBuilder` `builder(String typeName)` Creates new portable builder.
`Collection<PortableMetadata>` `metadata()` Gets metadata for all known types.
`PortableMetadata` `metadata(Class<?> cls)` Gets metadata for provided class.
`PortableMetadata` `metadata(int typeId)` Gets metadata for provided type ID.
`PortableMetadata` `metadata(String typeName)` Gets metadata for provided class name.
`<T> T` `toPortable(Object obj)` Converts provided object to instance of `PortableObject`.
`int` `typeId(String typeName)` Gets type ID for given type name.

### Method Detail

#### typeId

    int typeId(String typeName)

Gets type ID for given type name.

Parameters:
  `typeName` - Type name.
Returns:
  Type ID.

#### toPortable

    <T> T toPortable(@Nullable
                   Object obj)
                 throws PortableException

Converts provided object to instance of `PortableObject`.

Parameters:
  `obj` - Object to convert.
Returns:
  Converted object.
Throws:
  `PortableException` - In case of error.

#### builder

    PortableBuilder builder(int typeId)

Creates new portable builder.

Parameters:
  `typeId` - ID of the type.
Returns:
  Newly portable builder.

#### builder

    PortableBuilder builder(String typeName)

Creates new portable builder.

Parameters:
  `typeName` - Type name.
Returns:
  Newly portable builder.

#### builder

    PortableBuilder builder(PortableObject portableObj)

Creates portable builder initialized by existing portable object.

Parameters:
  `portableObj` - Portable object to initialize builder.
Returns:
  Portable builder.

#### metadata

    @Nullable
    PortableMetadata metadata(Class<?> cls)
                              throws PortableException

Gets metadata for provided class.

Parameters:
  `cls` - Class.
Returns:
  Metadata.
Throws:
  `PortableException` - In case of error.

#### metadata

    @Nullable
    PortableMetadata metadata(String typeName)
                              throws PortableException

Gets metadata for provided class name.

Parameters:
  `typeName` - Type name.
Returns:
  Metadata.
Throws:
  `PortableException` - In case of error.

#### metadata

    @Nullable
    PortableMetadata metadata(int typeId)
                              throws PortableException

Gets metadata for provided type ID.

Parameters:
  `typeId` - Type ID.
Returns:
  Metadata.
Throws:
  `PortableException` - In case of error.

#### metadata

    Collection<PortableMetadata> metadata()
                                          throws PortableException

Gets metadata for all known types.

Returns:
  Metadata.
Throws:
  `PortableException` - In case of error.

2015 Copyright © Apache Software Foundation

Follow @ApacheIgnite

Ignite Fabric | : ver. 1.4.0
Release Date | : September 24 2015