/*
* 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.processors.cacheobject;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.UUID;
import org.apache.ignite.IgniteBinary;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.CacheObjectByteArrayImpl;
import org.apache.ignite.internal.processors.cache.CacheObjectContext;
import org.apache.ignite.internal.processors.cache.CacheObjectImpl;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
import org.apache.ignite.internal.processors.cache.IncompleteCacheObject;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.Nullable;
/**
*
*/
public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter implements IgniteCacheObjectProcessor {
/** Immutable classes. */
private static final Collection<Class<?>> IMMUTABLE_CLS = new HashSet<>();
/** */
private IgniteBinary noOpBinary = new NoOpBinary();
/**
*
*/
static {
IMMUTABLE_CLS.add(String.class);
IMMUTABLE_CLS.add(Boolean.class);
IMMUTABLE_CLS.add(Byte.class);
IMMUTABLE_CLS.add(Short.class);
IMMUTABLE_CLS.add(Character.class);
IMMUTABLE_CLS.add(Integer.class);
IMMUTABLE_CLS.add(Long.class);
IMMUTABLE_CLS.add(Float.class);
IMMUTABLE_CLS.add(Double.class);
IMMUTABLE_CLS.add(UUID.class);
IMMUTABLE_CLS.add(IgniteUuid.class);
IMMUTABLE_CLS.add(BigDecimal.class);
}
/**
* @param ctx Context.
*/
public IgniteCacheObjectProcessorImpl(GridKernalContext ctx) {
super(ctx);
}
/** {@inheritDoc} */
@Override public String affinityField(String keyType) {
return null;
}
/** {@inheritDoc} */
@Override public IgniteBinary binary() {
return noOpBinary;
}
/** {@inheritDoc} */
@Nullable @Override public CacheObject prepareForCache(@Nullable CacheObject obj, GridCacheContext cctx) {
if (obj == null)
return null;
return obj.prepareForCache(cctx.cacheObjectContext());
}
/** {@inheritDoc} */
@Override public byte[] marshal(CacheObjectContext ctx, Object val) throws IgniteCheckedException {
return CU.marshal(ctx.kernalContext().cache().context(), ctx.addDeploymentInfo(), val);
}
/** {@inheritDoc} */
@Override public Object unmarshal(CacheObjectContext ctx, byte[] bytes, ClassLoader clsLdr)
throws IgniteCheckedException {
return U.unmarshal(ctx.kernalContext(), bytes, U.resolveClassLoader(clsLdr, ctx.kernalContext().config()));
}
/** {@inheritDoc} */
@Override public KeyCacheObject toCacheKeyObject(CacheObjectContext ctx,
@Nullable GridCacheContext cctx,
Object obj,
boolean userObj) {
if (obj instanceof KeyCacheObject) {
KeyCacheObject key = (KeyCacheObject)obj;
if (key.partition() == -1)
// Assume all KeyCacheObjects except BinaryObject can not be reused for another cache.
key.partition(partition(ctx, cctx, key));
return (KeyCacheObject)obj;
}
return toCacheKeyObject0(ctx, cctx, obj, userObj);
}
/**
* @param obj Object.
* @param userObj If {@code true} then given object is object provided by user and should be copied
* before stored in cache.
* @return Key cache object.
*/
@SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
protected KeyCacheObject toCacheKeyObject0(CacheObjectContext ctx,
@Nullable GridCacheContext cctx,
Object obj,
boolean userObj) {
int part = partition(ctx, cctx, obj);
if (!userObj)
return new KeyCacheObjectImpl(obj, null, part);
return new UserKeyCacheObjectImpl(obj, part);
}
/** {@inheritDoc} */
@Override public CacheObject toCacheObject(CacheObjectContext ctx, byte type, byte[] bytes) {
switch (type) {
case CacheObject.TYPE_BYTE_ARR:
return new CacheObjectByteArrayImpl(bytes);
case CacheObject.TYPE_REGULAR:
return new CacheObjectImpl(null, bytes);
}
throw new IllegalArgumentException("Invalid object type: " + type);
}
/** {@inheritDoc} */
@Override public KeyCacheObject toKeyCacheObject(CacheObjectContext ctx, byte type, byte[] bytes) throws IgniteCheckedException {
switch (type) {
case CacheObject.TYPE_BYTE_ARR:
throw new IllegalArgumentException("Byte arrays cannot be used as cache keys.");
case CacheObject.TYPE_REGULAR:
return new KeyCacheObjectImpl(ctx.processor().unmarshal(ctx, bytes, null), bytes, -1);
}
throw new IllegalArgumentException("Invalid object type: " + type);
}
/** {@inheritDoc} */
@Override public CacheObject toCacheObject(CacheObjectContext ctx, ByteBuffer buf) {
int len = buf.getInt();
assert len >= 0 : len;
byte type = buf.get();
byte[] data = new byte[len];
buf.get(data);
return toCacheObject(ctx, type, data);
}
/** {@inheritDoc} */
@Override public IncompleteCacheObject toCacheObject(
final CacheObjectContext ctx,
final ByteBuffer buf,
@Nullable IncompleteCacheObject incompleteObj
) throws IgniteCheckedException {
if (incompleteObj == null)
incompleteObj = new IncompleteCacheObject(buf);
if (incompleteObj.isReady())
return incompleteObj;
incompleteObj.readData(buf);
if (incompleteObj.isReady())
incompleteObj.object(toCacheObject(ctx, incompleteObj.type(), incompleteObj.data()));
return incompleteObj;
}
/** {@inheritDoc} */
@Override public IncompleteCacheObject toKeyCacheObject(
final CacheObjectContext ctx,
final ByteBuffer buf,
@Nullable IncompleteCacheObject incompleteObj
) throws IgniteCheckedException {
if (incompleteObj == null)
incompleteObj = new IncompleteCacheObject(buf);
if (incompleteObj.isReady())
return incompleteObj;
incompleteObj.readData(buf);
if (incompleteObj.isReady())
incompleteObj.object(toKeyCacheObject(ctx, incompleteObj.type(), incompleteObj.data()));
return incompleteObj;
}
/** {@inheritDoc} */
@Nullable @Override public CacheObject toCacheObject(CacheObjectContext ctx,
@Nullable Object obj,
boolean userObj) {
if (obj == null || obj instanceof CacheObject)
return (CacheObject)obj;
return toCacheObject0(obj, userObj);
}
/**
* @param obj Object.
* @param userObj If {@code true} then given object is object provided by user and should be copied
* before stored in cache.
* @return Cache object.
*/
@SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
protected CacheObject toCacheObject0(@Nullable Object obj, boolean userObj) {
assert obj != null;
if (obj instanceof byte[]) {
if (!userObj)
return new CacheObjectByteArrayImpl((byte[])obj);
return new UserCacheObjectByteArrayImpl((byte[])obj);
}
if (!userObj)
return new CacheObjectImpl(obj, null);
return new UserCacheObjectImpl(obj, null);
}
/**
* @param ctx Cache objects context.
* @param cctx Cache context.
* @param obj Object.
* @return Object partition.
*/
protected final int partition(CacheObjectContext ctx, @Nullable GridCacheContext cctx, Object obj) {
try {
return cctx != null ?
cctx.affinity().partition(obj, false) :
ctx.kernalContext().affinity().partition0(ctx.cacheName(), obj, null);
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to get partition", e);
return -1;
}
}
/** {@inheritDoc} */
@Override public CacheObjectContext contextForCache(CacheConfiguration ccfg) throws IgniteCheckedException {
assert ccfg != null;
boolean storeVal = !ccfg.isCopyOnRead() || (!isBinaryEnabled(ccfg) &&
(QueryUtils.isEnabled(ccfg) || ctx.config().isPeerClassLoadingEnabled()));
CacheObjectContext res = new CacheObjectContext(ctx,
ccfg.getName(),
ccfg.getAffinityMapper() != null ? ccfg.getAffinityMapper() : new GridCacheDefaultAffinityKeyMapper(),
ccfg.isCopyOnRead(),
storeVal,
ctx.config().isPeerClassLoadingEnabled() && !isBinaryEnabled(ccfg));
ctx.resource().injectGeneric(res.defaultAffMapper());
return res;
}
/** {@inheritDoc} */
@Override public boolean immutable(Object obj) {
assert obj != null;
return IMMUTABLE_CLS.contains(obj.getClass());
}
/** {@inheritDoc} */
@Override public void onContinuousProcessorStarted(GridKernalContext ctx) throws IgniteCheckedException {
// No-op.
}
/** {@inheritDoc} */
@Override public void onUtilityCacheStarted() throws IgniteCheckedException {
// No-op.
}
/** {@inheritDoc} */
@Override public int typeId(String typeName) {
return 0;
}
/** {@inheritDoc} */
@Override public Object unwrapTemporary(GridCacheContext ctx, Object obj) throws IgniteException {
return obj;
}
/** {@inheritDoc} */
@Override public boolean isBinaryObject(Object obj) {
return false;
}
/** {@inheritDoc} */
@Override public boolean isBinaryEnabled(CacheConfiguration<?, ?> ccfg) {
return false;
}
/** {@inheritDoc} */
@Override public int typeId(Object obj) {
return 0;
}
/** {@inheritDoc} */
@Override public Object field(Object obj, String fieldName) {
return null;
}
/** {@inheritDoc} */
@Override public boolean hasField(Object obj, String fieldName) {
return false;
}
/**
* Wraps key provided by user, must be serialized before stored in cache.
*/
private static class UserKeyCacheObjectImpl extends KeyCacheObjectImpl {
/** */
private static final long serialVersionUID = 0L;
/**
*
*/
public UserKeyCacheObjectImpl() {
//No-op.
}
/**
* @param key Key.
* @param part Partition.
*/
UserKeyCacheObjectImpl(Object key, int part) {
super(key, null, part);
}
/**
* @param key Key.
* @param valBytes Marshalled key.
* @param part Partition.
*/
UserKeyCacheObjectImpl(Object key, byte[] valBytes, int part) {
super(key, valBytes, part);
}
/** {@inheritDoc} */
@Override public KeyCacheObject copy(int part) {
if (this.partition() == part)
return this;
return new UserKeyCacheObjectImpl(val, valBytes, part);
}
/** {@inheritDoc} */
@Override public CacheObject prepareForCache(CacheObjectContext ctx) {
try {
if (!ctx.processor().immutable(val)) {
if (valBytes == null)
valBytes = ctx.processor().marshal(ctx, val);
ClassLoader ldr = ctx.p2pEnabled() ?
IgniteUtils.detectClassLoader(IgniteUtils.detectClass(this.val)) : U.gridClassLoader();
Object val = ctx.processor().unmarshal(ctx, valBytes, ldr);
KeyCacheObject key = new KeyCacheObjectImpl(val, valBytes, partition());
key.partition(partition());
return key;
}
KeyCacheObject key = new KeyCacheObjectImpl(val, valBytes, partition());
key.partition(partition());
return key;
}
catch (IgniteCheckedException e) {
throw new IgniteException("Failed to marshal object: " + val, e);
}
}
}
/**
* Wraps value provided by user, must be serialized before stored in cache.
*/
private static class UserCacheObjectImpl extends CacheObjectImpl {
/** */
private static final long serialVersionUID = 0L;
/**
*
*/
public UserCacheObjectImpl() {
//No-op.
}
/**
* @param val Value.
* @param valBytes Value bytes.
*/
public UserCacheObjectImpl(Object val, byte[] valBytes) {
super(val, valBytes);
}
/** {@inheritDoc} */
@Nullable @Override public <T> T value(CacheObjectContext ctx, boolean cpy) {
return super.value(ctx, false); // Do not need copy since user value is not in cache.
}
/** {@inheritDoc} */
@Override public CacheObject prepareForCache(CacheObjectContext ctx) {
try {
if (valBytes == null)
valBytes = ctx.processor().marshal(ctx, val);
if (ctx.storeValue()) {
ClassLoader ldr = ctx.p2pEnabled() ?
IgniteUtils.detectClass(this.val).getClassLoader() : val.getClass().getClassLoader();
Object val = this.val != null && ctx.processor().immutable(this.val) ? this.val :
ctx.processor().unmarshal(ctx, valBytes, ldr);
return new CacheObjectImpl(val, valBytes);
}
return new CacheObjectImpl(null, valBytes);
}
catch (IgniteCheckedException e) {
throw new IgniteException("Failed to marshal object: " + val, e);
}
}
}
/**
* Wraps value provided by user, must be copied before stored in cache.
*/
private static class UserCacheObjectByteArrayImpl extends CacheObjectByteArrayImpl {
/** */
private static final long serialVersionUID = 0L;
/**
*
*/
public UserCacheObjectByteArrayImpl() {
// No-op.
}
/**
* @param val Value.
*/
public UserCacheObjectByteArrayImpl(byte[] val) {
super(val);
}
/** {@inheritDoc} */
@Nullable @Override public <T> T value(CacheObjectContext ctx, boolean cpy) {
return super.value(ctx, false); // Do not need copy since user value is not in cache.
}
/** {@inheritDoc} */
@Override public CacheObject prepareForCache(CacheObjectContext ctx) {
byte[] valCpy = Arrays.copyOf(val, val.length);
return new CacheObjectByteArrayImpl(valCpy);
}
}
}