...
ID | IEP-97 | ||||||||
Author | Anton Vinogradov | ||||||||
Sponsor | |||||||||
Created |
| ||||||||
Status |
|
...
...
Code Block | ||||
---|---|---|---|---|
| ||||
protected byte[] valueBytesFromValue(CacheObjectValueContext ctx) throws IgniteCheckedException { byte[] bytes = ctx.kernalContext().cacheObjects().marshal(ctx, val); return CacheObjectTransformerCacheObjectTransformerUtils.transformIfNecessary(bytes, ctx); } protected Object valueFromValueBytes(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { byte[] bytes = CacheObjectTransformerCacheObjectTransformerUtils.restoreIfNecessary(valBytes, ctx); return ctx.kernalContext().cacheObjects().unmarshal(ctx, bytes, ldr); } public void prepareMarshal(CacheObjectValueContext ctx) throws IgniteCheckedException { if (valBytes == null) valBytes = valueBytesFromValue(ctx); } public void finishUnmarshal(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { if (val == null) val = valueFromValueBytes(ctx, ldr); } |
...
It's not possible to just replace arr with valBytes because, unlike , for example, from CacheObjectImpl arr is not just a mashalled bytes, it's an object's value that is required, for example, to provide hashCode/schemaId/typeId/objectField, and we must keep it as is.
...
Code Block | ||||
---|---|---|---|---|
| ||||
private byte[] arrayFromValueBytes(CacheObjectValueContext ctx) { return CacheObjectTransformerCacheObjectTransformerUtils.restoreIfNecessary(valBytes, ctx); } private byte[] valueBytesFromArray(CacheObjectValueContext ctx) { return CacheObjectTransformerCacheObjectTransformerUtils.transformIfNecessary(arr, start, arr.length, ctx); } public void finishUnmarshal(CacheObjectValueContext ctx, ClassLoader ldr) throws IgniteCheckedException { if (arr == null) arr = arrayFromValueBytes(ctx); } public void prepareMarshal(CacheObjectValueContext ctx) { if (valBytes == null) valBytes = valueBytesFromArray(ctx); } |
...
Some customers may want to encrypt the data, some to compress it, while some just keep it as is.
So, we must provide a simple way to append any transformation.
The simplest way is to use Service Provider Interface (IgniteSpi):
Code Block | ||||
---|---|---|---|---|
| ||||
public interface CacheObjectTransformerSpiCacheObjectTransformerManager extends IgniteSpiGridCacheSharedManager { /** Additional space required to store the transformed data. */ public int OVERHEAD = 2; /** * Transforms the data. * * @param bytes Byte array contains the data. * @param offset Data offset. * @param length Data lengthoriginal Original data. * @return Byte array contains the transformed Transformed data (started with non-filled area with {@link #OVERHEADGridBinaryMarshaller#TRANSFORMED} when size.restorable) * or @throws{@code IgniteCheckedExceptionnull} when transformation is not possible/suitable. */ public @Nullable byte[]ByteBuffer transform(byte[] bytes, int offset, int length) throws IgniteCheckedException; ByteBuffer original); /** * Restores the data. * * @param bytes Byte array contains the transformed data. * @param offset Data offsettransformed Transformed data. * @param length Data length. * @return Byte array contains the restored @return Restored data. */ public byte[]ByteBuffer restore(byte[] bytes, int offset, int lengthByteBuffer transformed); } |
...
Every customer may implement this interface in a proper way if necessary and specify it in the via plugin configuration:
Code Block | ||||
---|---|---|---|---|
| ||||
IgniteConfiguration getConfiguration() { IgniteConfiguration cfg = ... cfg.setCacheObjectTransformerSpisetPluginProviders(new XXXTransformerSpiXXXPluginProvider()); return cfg; } |
But, most customers just want to transform the data, so, they may extend the adapter with the simplified API:
Code Block | ||||
---|---|---|---|---|
| ||||
public abstract class CacheObjectTransformerSpiAdapter extends IgniteSpiAdapter implements CacheObjectTransformerSpi { ... /** * Transforms the data. * * @param original Original data. * @return Transformed data. * @throws IgniteCheckedException when transformation is not possible/suitable. Which provides */ protected abstract ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException; /** * Restores the data. * * @param transformed Transformed data. * @return Restored data. */ protected abstract ByteBuffer restore(ByteBuffer transformed); }some XXXCacheObjectTransformerManager() return cfg; } |
Code Block | ||||
---|---|---|---|---|
| ||||
class CompressionTransformerSpiCompressionTransformer extends CacheObjectTransformerSpiAdapterCacheObjectTransformerAdapter { protected ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException { int locOverheadoverhead = 45; // OriginalTransformed length. flag int totalOverhead = CacheObjectTransformerSpi.OVERHEAD + locOverhead;+ length. int origSize = original.remaining(); int lim = origSize - totalOverheadoverhead; if (lim <= 0) throw new IgniteCheckedException(" return null; // Compression is not profitable."); ByteBuffer compressed = byteBuffer(lim); (overhead + (int)Zstd.compressBound(origSize)); compressed.put(TRANSFORMED); compressed.positionputInt(locOverheadorigSize); int size = Zstd.compress(compressed, original, 1); if (size >= lim) compressed.flip(); compressed.putInt(origSize); return null; // Compression is not profitable. compressed.rewindflip(); return compressed; } protected ByteBuffer restore(ByteBuffer transformed) { ByteBuffer restored = byteBuffer(transformed.getInt()); Zstd.decompress(restored, transformed); restored.flip(); return restored; } } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
class EncryptionTransformerSpiEncryptionTransformer extends CacheObjectTransformerSpiAdapterCacheObjectTransformerAdapter { private static final int SHIFT = 42; // Secret! protected ByteBuffer transform(ByteBuffer original) throws IgniteCheckedException { ByteBuffer transformed = byteBuffer(original.remaining() + 1); // Same capacity is required. transformed.put(TRANSFORMED); while (original.hasRemaining()) transformed.put((byte)(original.get() + SHIFT)); transformed.flip(); return transformed; } protected ByteBuffer restore(ByteBuffer transformed, int length) { ByteBuffer restored = byteBuffer(transformed.remaining()); // Same size. while (transformed.hasRemaining()) restored.put((byte)(transformed.get() - SHIFT)); restored.flip(); return restored; } } |
...