You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
33 KiB
713 lines
33 KiB
/** |
|
* Copyright 2018 Nikita Koksharov |
|
* |
|
* Licensed 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 com.fr.third.org.redisson; |
|
|
|
import java.lang.annotation.Annotation; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Field; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Collection; |
|
import java.util.Deque; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.LinkedHashMap; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Map.Entry; |
|
import java.util.Queue; |
|
import java.util.Set; |
|
import java.util.SortedSet; |
|
import java.util.TreeSet; |
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
import com.fr.third.org.redisson.api.RCascadeType; |
|
import com.fr.third.org.redisson.api.RDeque; |
|
import com.fr.third.org.redisson.api.RExpirable; |
|
import com.fr.third.org.redisson.api.RExpirableAsync; |
|
import com.fr.third.org.redisson.api.RList; |
|
import com.fr.third.org.redisson.api.RLiveObject; |
|
import com.fr.third.org.redisson.api.RLiveObjectService; |
|
import com.fr.third.org.redisson.api.RMap; |
|
import com.fr.third.org.redisson.api.RMapAsync; |
|
import com.fr.third.org.redisson.api.RObject; |
|
import com.fr.third.org.redisson.api.RObjectAsync; |
|
import com.fr.third.org.redisson.api.RQueue; |
|
import com.fr.third.org.redisson.api.RSet; |
|
import com.fr.third.org.redisson.api.RSortedSet; |
|
import com.fr.third.org.redisson.api.RedissonClient; |
|
import com.fr.third.org.redisson.api.annotation.RCascade; |
|
import com.fr.third.org.redisson.api.annotation.REntity; |
|
import com.fr.third.org.redisson.api.annotation.RFieldAccessor; |
|
import com.fr.third.org.redisson.api.annotation.RId; |
|
import com.fr.third.org.redisson.api.RCascadeType; |
|
import com.fr.third.org.redisson.api.RDeque; |
|
import com.fr.third.org.redisson.api.RExpirable; |
|
import com.fr.third.org.redisson.api.RExpirableAsync; |
|
import com.fr.third.org.redisson.api.RList; |
|
import com.fr.third.org.redisson.api.RLiveObject; |
|
import com.fr.third.org.redisson.api.RLiveObjectService; |
|
import com.fr.third.org.redisson.api.RMap; |
|
import com.fr.third.org.redisson.api.RMapAsync; |
|
import com.fr.third.org.redisson.api.RObject; |
|
import com.fr.third.org.redisson.api.RObjectAsync; |
|
import com.fr.third.org.redisson.api.RQueue; |
|
import com.fr.third.org.redisson.api.RSet; |
|
import com.fr.third.org.redisson.api.RSortedSet; |
|
import com.fr.third.org.redisson.api.RedissonClient; |
|
import com.fr.third.org.redisson.api.annotation.RCascade; |
|
import com.fr.third.org.redisson.api.annotation.REntity; |
|
import com.fr.third.org.redisson.api.annotation.RFieldAccessor; |
|
import com.fr.third.org.redisson.api.annotation.RId; |
|
import com.fr.third.org.redisson.liveobject.LiveObjectTemplate; |
|
import com.fr.third.org.redisson.liveobject.core.AccessorInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.FieldAccessorInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.LiveObjectInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.RExpirableInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.RMapInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.RObjectInterceptor; |
|
import com.fr.third.org.redisson.liveobject.core.RedissonObjectBuilder; |
|
import com.fr.third.org.redisson.liveobject.misc.AdvBeanCopy; |
|
import com.fr.third.org.redisson.liveobject.misc.ClassUtils; |
|
import com.fr.third.org.redisson.liveobject.misc.Introspectior; |
|
import com.fr.third.org.redisson.liveobject.resolver.Resolver; |
|
|
|
import io.netty.util.internal.PlatformDependent; |
|
import com.fr.third.jodd.bean.BeanCopy; |
|
import com.fr.third.jodd.bean.BeanUtil; |
|
import com.fr.third.net.bytebuddy.ByteBuddy; |
|
import com.fr.third.net.bytebuddy.description.field.FieldDescription; |
|
import com.fr.third.net.bytebuddy.description.field.FieldList; |
|
import com.fr.third.net.bytebuddy.dynamic.DynamicType; |
|
import com.fr.third.net.bytebuddy.dynamic.loading.ClassLoadingStrategy; |
|
import com.fr.third.net.bytebuddy.implementation.MethodDelegation; |
|
import com.fr.third.net.bytebuddy.implementation.bind.annotation.FieldProxy; |
|
import com.fr.third.net.bytebuddy.matcher.ElementMatchers; |
|
|
|
public class RedissonLiveObjectService implements RLiveObjectService { |
|
|
|
private static final ConcurrentMap<Class<? extends Resolver>, Resolver<?, ?, ?>> providerCache = PlatformDependent.newConcurrentHashMap(); |
|
private final ConcurrentMap<Class<?>, Class<?>> classCache; |
|
private final RedissonClient redisson; |
|
private final RedissonObjectBuilder objectBuilder; |
|
|
|
public RedissonLiveObjectService(RedissonClient redisson, ConcurrentMap<Class<?>, Class<?>> classCache) { |
|
this.redisson = redisson; |
|
this.classCache = classCache; |
|
this.objectBuilder = new RedissonObjectBuilder(redisson); |
|
} |
|
|
|
//TODO: Add ttl renewal functionality |
|
// @Override |
|
// public <T, K> T get(Class<T> entityClass, K id, long timeToLive, TimeUnit timeUnit) { |
|
// T instance = get(entityClass, id); |
|
// RMap map = ((RLiveObject) instance).getLiveObjectLiveMap(); |
|
// map.put("RLiveObjectDefaultTimeToLiveValue", timeToLive); |
|
// map.put("RLiveObjectDefaultTimeToLiveUnit", timeUnit.toString()); |
|
// map.expire(timeToLive, timeUnit); |
|
// return instance; |
|
// } |
|
|
|
public RMap<String, Object> getMap(Object proxied) { |
|
return ClassUtils.getField(proxied, "liveObjectLiveMap"); |
|
} |
|
|
|
private <T> Object generateId(Class<T> entityClass) throws NoSuchFieldException { |
|
String idFieldName = getRIdFieldName(entityClass); |
|
RId annotation = ClassUtils.getDeclaredField(entityClass, idFieldName) |
|
.getAnnotation(RId.class); |
|
Resolver resolver = getResolver(entityClass, annotation.generator(), annotation); |
|
Object id = resolver.resolve(entityClass, annotation, idFieldName, redisson); |
|
return id; |
|
} |
|
|
|
private Resolver<?, ?, ?> getResolver(Class<?> cls, Class<? extends Resolver> resolverClass, Annotation anno) { |
|
if (!providerCache.containsKey(resolverClass)) { |
|
try { |
|
providerCache.putIfAbsent(resolverClass, resolverClass.newInstance()); |
|
} catch (Exception ex) { |
|
throw new RuntimeException(ex); |
|
} |
|
} |
|
return providerCache.get(resolverClass); |
|
} |
|
|
|
@Override |
|
public <T, K> T get(Class<T> entityClass, K id) { |
|
try { |
|
T proxied = instantiateLiveObject(getProxyClass(entityClass), id); |
|
return asLiveObject(proxied).isExists() ? proxied : null; |
|
} catch (Exception ex) { |
|
unregisterClass(entityClass); |
|
throw ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex); |
|
} |
|
} |
|
|
|
@Override |
|
public <T> T attach(T detachedObject) { |
|
validateDetached(detachedObject); |
|
Class<T> entityClass = (Class<T>) detachedObject.getClass(); |
|
try { |
|
String idFieldName = getRIdFieldName(detachedObject.getClass()); |
|
Object id = ClassUtils.getField(detachedObject, idFieldName); |
|
Class<? extends T> proxyClass = getProxyClass(entityClass); |
|
return instantiateLiveObject(proxyClass, id); |
|
} catch (Exception ex) { |
|
unregisterClass(entityClass); |
|
throw ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex); |
|
} |
|
} |
|
|
|
@Override |
|
public <T> T merge(T detachedObject) { |
|
Map<Object, Object> alreadyPersisted = new HashMap<Object, Object>(); |
|
return persist(detachedObject, alreadyPersisted, RCascadeType.MERGE); |
|
} |
|
|
|
@Override |
|
public <T> T persist(T detachedObject) { |
|
Map<Object, Object> alreadyPersisted = new HashMap<Object, Object>(); |
|
return persist(detachedObject, alreadyPersisted, RCascadeType.PERSIST); |
|
} |
|
|
|
private <T> T persist(T detachedObject, Map<Object, Object> alreadyPersisted, RCascadeType type) { |
|
String idFieldName = getRIdFieldName(detachedObject.getClass()); |
|
Object id = ClassUtils.getField(detachedObject, idFieldName); |
|
if (id == null) { |
|
try { |
|
id = generateId(detachedObject.getClass()); |
|
} catch (NoSuchFieldException e) { |
|
throw new IllegalArgumentException(e); |
|
} |
|
ClassUtils.setField(detachedObject, idFieldName, id); |
|
} |
|
|
|
T attachedObject = attach(detachedObject); |
|
alreadyPersisted.put(detachedObject, attachedObject); |
|
RMap<String, Object> liveMap = getMap(attachedObject); |
|
|
|
List<String> excludedFields = new ArrayList<String>(); |
|
excludedFields.add(idFieldName); |
|
boolean fastResult = liveMap.fastPut("redisson_live_object", "1"); |
|
if (type == RCascadeType.PERSIST && !fastResult) { |
|
throw new IllegalArgumentException("This REntity already exists."); |
|
} |
|
|
|
for (FieldDescription.InDefinedShape field : Introspectior.getAllFields(detachedObject.getClass())) { |
|
Object object = ClassUtils.getField(detachedObject, field.getName()); |
|
|
|
if (object == null) { |
|
continue; |
|
} |
|
|
|
RObject rObject = objectBuilder.createObject(id, detachedObject.getClass(), object.getClass(), field.getName()); |
|
if (rObject != null) { |
|
objectBuilder.store(rObject, field.getName(), liveMap); |
|
if (rObject instanceof SortedSet) { |
|
((RSortedSet)rObject).trySetComparator(((SortedSet)object).comparator()); |
|
} |
|
|
|
if (rObject instanceof Collection) { |
|
for (Object obj : (Collection<Object>)object) { |
|
if (obj != null && ClassUtils.isAnnotationPresent(obj.getClass(), REntity.class)) { |
|
Object persisted = alreadyPersisted.get(obj); |
|
if (persisted == null) { |
|
if (checkCascade(detachedObject, type, field.getName())) { |
|
persisted = persist(obj, alreadyPersisted, type); |
|
} |
|
} |
|
obj = persisted; |
|
} |
|
((Collection)rObject).add(obj); |
|
} |
|
} else if (rObject instanceof Map) { |
|
Map<Object, Object> rMap = (Map<Object, Object>) rObject; |
|
Map<?, ?> map = (Map<?, ?>)rObject; |
|
for (Entry<?, ?> entry : map.entrySet()) { |
|
Object key = entry.getKey(); |
|
Object value = entry.getValue(); |
|
|
|
if (key != null && ClassUtils.isAnnotationPresent(key.getClass(), REntity.class)) { |
|
Object persisted = alreadyPersisted.get(key); |
|
if (persisted == null) { |
|
if (checkCascade(detachedObject, type, field.getName())) { |
|
persisted = persist(key, alreadyPersisted, type); |
|
} |
|
} |
|
key = persisted; |
|
} |
|
|
|
if (value != null && ClassUtils.isAnnotationPresent(value.getClass(), REntity.class)) { |
|
Object persisted = alreadyPersisted.get(value); |
|
if (persisted == null) { |
|
if (checkCascade(detachedObject, type, field.getName())) { |
|
persisted = persist(value, alreadyPersisted, type); |
|
} |
|
} |
|
value = persisted; |
|
} |
|
|
|
rMap.put(key, value); |
|
} |
|
} |
|
excludedFields.add(field.getName()); |
|
} else if (ClassUtils.isAnnotationPresent(object.getClass(), REntity.class)) { |
|
Object persisted = alreadyPersisted.get(object); |
|
if (persisted == null) { |
|
if (checkCascade(detachedObject, type, field.getName())) { |
|
persisted = persist(object, alreadyPersisted, type); |
|
} |
|
} |
|
|
|
excludedFields.add(field.getName()); |
|
BeanUtil.pojo.setSimpleProperty(attachedObject, field.getName(), persisted); |
|
} else { |
|
validateAnnotation(detachedObject, field.getName()); |
|
} |
|
|
|
} |
|
copy(detachedObject, attachedObject, excludedFields); |
|
return attachedObject; |
|
} |
|
|
|
private void validateAnnotation(Object instance, String fieldName) { |
|
Class<?> clazz = instance.getClass(); |
|
if (isLiveObject(instance)) { |
|
clazz = clazz.getSuperclass(); |
|
} |
|
|
|
RCascade annotation = ClassUtils.getAnnotation(clazz, fieldName, RCascade.class); |
|
if (annotation != null) { |
|
throw new IllegalArgumentException("RCascade annotation couldn't be defined for non-Redisson object '" + clazz + "' and field '" + fieldName + "'"); |
|
} |
|
} |
|
|
|
private <T> boolean checkCascade(Object instance, RCascadeType type, String fieldName) { |
|
Class<?> clazz = instance.getClass(); |
|
if (isLiveObject(instance)) { |
|
clazz = clazz.getSuperclass(); |
|
} |
|
|
|
RCascade annotation = ClassUtils.getAnnotation(clazz, fieldName, RCascade.class); |
|
if (annotation != null && (Arrays.asList(annotation.value()).contains(type) |
|
|| Arrays.asList(annotation.value()).contains(RCascadeType.ALL))) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
@Override |
|
public <T> T detach(T attachedObject) { |
|
Map<String, Object> alreadyDetached = new HashMap<String, Object>(); |
|
return detach(attachedObject, alreadyDetached); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
private <T> T detach(T attachedObject, Map<String, Object> alreadyDetached) { |
|
validateAttached(attachedObject); |
|
try { |
|
T detached = instantiateDetachedObject((Class<T>) attachedObject.getClass().getSuperclass(), asLiveObject(attachedObject).getLiveObjectId()); |
|
BeanCopy.beans(attachedObject, detached).declared(true, true).copy(); |
|
alreadyDetached.put(getMap(attachedObject).getName(), detached); |
|
|
|
for (Entry<String, Object> obj : getMap(attachedObject).entrySet()) { |
|
if (!checkCascade(attachedObject, RCascadeType.DETACH, obj.getKey())) { |
|
continue; |
|
} |
|
|
|
if (obj.getValue() instanceof RSortedSet) { |
|
SortedSet<Object> redissonSet = (SortedSet<Object>) obj.getValue(); |
|
Set<Object> set = new TreeSet<Object>(redissonSet.comparator()); |
|
for (Object object : redissonSet) { |
|
if (isLiveObject(object)) { |
|
Object detachedObject = alreadyDetached.get(getMap(object).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(object, alreadyDetached); |
|
} |
|
object = detachedObject; |
|
} |
|
set.add(object); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), set); |
|
} else if (obj.getValue() instanceof RDeque) { |
|
Collection<Object> redissonDeque = (Collection<Object>) obj.getValue(); |
|
Deque<Object> deque = new LinkedList<Object>(); |
|
for (Object object : redissonDeque) { |
|
if (isLiveObject(object)) { |
|
Object detachedObject = alreadyDetached.get(getMap(object).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(object, alreadyDetached); |
|
} |
|
object = detachedObject; |
|
} |
|
deque.add(object); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), deque); |
|
} else if (obj.getValue() instanceof RQueue) { |
|
Collection<Object> redissonQueue = (Collection<Object>) obj.getValue(); |
|
Queue<Object> queue = new LinkedList<Object>(); |
|
for (Object object : redissonQueue) { |
|
if (isLiveObject(object)) { |
|
Object detachedObject = alreadyDetached.get(getMap(object).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(object, alreadyDetached); |
|
} |
|
object = detachedObject; |
|
} |
|
queue.add(object); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), queue); |
|
} else if (obj.getValue() instanceof RSet) { |
|
Set<Object> set = new HashSet<Object>(); |
|
Collection<Object> redissonSet = (Collection<Object>) obj.getValue(); |
|
for (Object object : redissonSet) { |
|
if (isLiveObject(object)) { |
|
Object detachedObject = alreadyDetached.get(getMap(object).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(object, alreadyDetached); |
|
} |
|
object = detachedObject; |
|
} |
|
set.add(object); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), set); |
|
} else if (obj.getValue() instanceof RList) { |
|
List<Object> list = new ArrayList<Object>(); |
|
Collection<Object> redissonList = (Collection<Object>) obj.getValue(); |
|
for (Object object : redissonList) { |
|
if (isLiveObject(object)) { |
|
Object detachedObject = alreadyDetached.get(getMap(object).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(object, alreadyDetached); |
|
} |
|
object = detachedObject; |
|
} |
|
list.add(object); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), list); |
|
} else if (isLiveObject(obj.getValue())) { |
|
Object detachedObject = alreadyDetached.get(getMap(obj.getValue()).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(obj.getValue(), alreadyDetached); |
|
} |
|
ClassUtils.setField(detached, obj.getKey(), detachedObject); |
|
} else if (obj.getValue() instanceof RMap) { |
|
Map<Object, Object> map = new LinkedHashMap<Object, Object>(); |
|
Map<Object, Object> redissonMap = (Map<Object, Object>) obj.getValue(); |
|
for (Entry<Object, Object> entry : redissonMap.entrySet()) { |
|
Object key = entry.getKey(); |
|
Object value = entry.getValue(); |
|
|
|
if (isLiveObject(key)) { |
|
Object detachedObject = alreadyDetached.get(getMap(key).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(key, alreadyDetached); |
|
} |
|
key = detachedObject; |
|
} |
|
|
|
if (isLiveObject(value)) { |
|
Object detachedObject = alreadyDetached.get(getMap(value).getName()); |
|
if (detachedObject == null) { |
|
detachedObject = detach(value, alreadyDetached); |
|
} |
|
value = detachedObject; |
|
} |
|
map.put(key, value); |
|
} |
|
|
|
ClassUtils.setField(detached, obj.getKey(), map); |
|
} else { |
|
validateAnnotation(detached, obj.getKey()); |
|
} |
|
} |
|
|
|
return detached; |
|
} catch (Exception ex) { |
|
throw ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex); |
|
} |
|
} |
|
|
|
@Override |
|
public <T> void delete(T attachedObject) { |
|
Set<String> deleted = new HashSet<String>(); |
|
delete(attachedObject, deleted); |
|
} |
|
|
|
private <T> void delete(T attachedObject, Set<String> deleted) { |
|
validateAttached(attachedObject); |
|
|
|
for (Entry<String, Object> obj : getMap(attachedObject).entrySet()) { |
|
if (!checkCascade(attachedObject, RCascadeType.DELETE, obj.getKey())) { |
|
continue; |
|
} |
|
|
|
if (obj.getValue() instanceof RSortedSet) { |
|
deleteCollection(deleted, (Iterable<?>)obj.getValue()); |
|
((RObject)obj.getValue()).delete(); |
|
} else if (obj.getValue() instanceof RDeque) { |
|
deleteCollection(deleted, (Iterable<?>)obj.getValue()); |
|
((RObject)obj.getValue()).delete(); |
|
} else if (obj.getValue() instanceof RQueue) { |
|
deleteCollection(deleted, (Iterable<?>)obj.getValue()); |
|
((RObject)obj.getValue()).delete(); |
|
} else if (obj.getValue() instanceof RSet) { |
|
deleteCollection(deleted, (Iterable<?>)obj.getValue()); |
|
((RObject)obj.getValue()).delete(); |
|
} else if (obj.getValue() instanceof RList) { |
|
deleteCollection(deleted, (Iterable<?>)obj.getValue()); |
|
((RObject)obj.getValue()).delete(); |
|
} else if (isLiveObject(obj.getValue())) { |
|
if (deleted.add(getMap(obj.getValue()).getName())) { |
|
delete(obj.getValue(), deleted); |
|
} |
|
} else if (obj.getValue() instanceof RMap) { |
|
RMap<Object, Object> map = (RMap<Object, Object>)obj.getValue(); |
|
deleteCollection(deleted, map.keySet()); |
|
deleteCollection(deleted, map.values()); |
|
((RObject)obj.getValue()).delete(); |
|
} else { |
|
validateAnnotation(attachedObject, obj.getKey()); |
|
} |
|
|
|
} |
|
asLiveObject(attachedObject).delete(); |
|
} |
|
|
|
private void deleteCollection(Set<String> deleted, Iterable<?> objs) { |
|
for (Object object : objs) { |
|
if (isLiveObject(object)) { |
|
if (deleted.add(getMap(object).getName())) { |
|
delete(object, deleted); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public <T, K> void delete(Class<T> entityClass, K id) { |
|
asLiveObject(get(entityClass, id)).delete(); |
|
} |
|
|
|
@Override |
|
public <T> RLiveObject asLiveObject(T instance) { |
|
return (RLiveObject) instance; |
|
} |
|
|
|
@Override |
|
public <T> RExpirable asRExpirable(T instance) { |
|
return (RExpirable) instance; |
|
} |
|
|
|
@Override |
|
public <T, K, V> RMap<K, V> asRMap(T instance) { |
|
return (RMap) instance; |
|
} |
|
|
|
@Override |
|
public <T> boolean isLiveObject(T instance) { |
|
return instance instanceof RLiveObject; |
|
} |
|
|
|
@Override |
|
public <T> boolean isExists(T instance) { |
|
return instance instanceof RLiveObject && asLiveObject(instance).isExists(); |
|
} |
|
|
|
@Override |
|
public void registerClass(Class cls) { |
|
if (!classCache.containsKey(cls)) { |
|
validateClass(cls); |
|
registerClassInternal(cls); |
|
} |
|
} |
|
|
|
@Override |
|
public void unregisterClass(Class cls) { |
|
classCache.remove(cls.isAssignableFrom(RLiveObject.class) ? cls.getSuperclass() : cls); |
|
} |
|
|
|
@Override |
|
public boolean isClassRegistered(Class cls) { |
|
return classCache.containsKey(cls) || classCache.containsValue(cls); |
|
} |
|
|
|
private <T> void copy(T detachedObject, T attachedObject, List<String> excludedFields) { |
|
new AdvBeanCopy(detachedObject, attachedObject) |
|
.ignoreNulls(true) |
|
.exclude(excludedFields.toArray(new String[excludedFields.size()])) |
|
.copy(); |
|
} |
|
|
|
private String getRIdFieldName(Class cls) { |
|
return Introspectior.getFieldsWithAnnotation(cls, RId.class) |
|
.getOnly() |
|
.getName(); |
|
} |
|
|
|
private <T, K> T instantiateLiveObject(Class<T> proxyClass, K id) throws Exception { |
|
if (id == null) { |
|
throw new IllegalStateException("Non-null value is required for the field with RId annotation."); |
|
} |
|
T instance = instantiate(proxyClass, id); |
|
asLiveObject(instance).setLiveObjectId(id); |
|
return instance; |
|
} |
|
|
|
private <T, K> T instantiateDetachedObject(Class<T> cls, K id) throws Exception { |
|
T instance = instantiate(cls, id); |
|
String fieldName = getRIdFieldName(cls); |
|
if (ClassUtils.getField(instance, fieldName) == null) { |
|
ClassUtils.setField(instance, fieldName, id); |
|
} |
|
return instance; |
|
} |
|
|
|
private <T, K> T instantiate(Class<T> cls, K id) throws Exception { |
|
for (Constructor<?> constructor : cls.getDeclaredConstructors()) { |
|
if (constructor.getParameterTypes().length == 0) { |
|
constructor.setAccessible(true); |
|
return (T) constructor.newInstance(); |
|
} |
|
} |
|
throw new IllegalArgumentException("Can't find default constructor for " + cls); |
|
} |
|
|
|
private <T> Class<? extends T> getProxyClass(Class<T> entityClass) { |
|
registerClass(entityClass); |
|
return (Class<? extends T>) classCache.get(entityClass); |
|
} |
|
|
|
private <T> void validateClass(Class<T> entityClass) { |
|
if (entityClass.isAnonymousClass() || entityClass.isLocalClass()) { |
|
throw new IllegalArgumentException(entityClass.getName() + " is not publically accessable."); |
|
} |
|
if (!ClassUtils.isAnnotationPresent(entityClass, REntity.class)) { |
|
throw new IllegalArgumentException("REntity annotation is missing from class type declaration."); |
|
} |
|
FieldList<FieldDescription.InDefinedShape> fieldsWithRIdAnnotation |
|
= Introspectior.getFieldsWithAnnotation(entityClass, RId.class); |
|
if (fieldsWithRIdAnnotation.size() == 0) { |
|
throw new IllegalArgumentException("RId annotation is missing from class field declaration."); |
|
} |
|
if (fieldsWithRIdAnnotation.size() > 1) { |
|
throw new IllegalArgumentException("Only one field with RId annotation is allowed in class field declaration."); |
|
} |
|
FieldDescription.InDefinedShape idFieldDescription = fieldsWithRIdAnnotation.getOnly(); |
|
String idFieldName = idFieldDescription.getName(); |
|
Field idField = null; |
|
try { |
|
idField = ClassUtils.getDeclaredField(entityClass, idFieldName); |
|
} catch (Exception e) { |
|
throw new IllegalStateException(e); |
|
} |
|
if (ClassUtils.isAnnotationPresent(idField.getType(), REntity.class)) { |
|
throw new IllegalArgumentException("Field with RId annotation cannot be a type of which class is annotated with REntity."); |
|
} |
|
if (idField.getType().isAssignableFrom(RObject.class)) { |
|
throw new IllegalArgumentException("Field with RId annotation cannot be a type of RObject"); |
|
} |
|
} |
|
|
|
private <T> void validateDetached(T detachedObject) { |
|
if (detachedObject instanceof RLiveObject) { |
|
throw new IllegalArgumentException("The object supplied is already a RLiveObject"); |
|
} |
|
} |
|
|
|
private <T> void validateAttached(T attachedObject) { |
|
if (!(attachedObject instanceof RLiveObject)) { |
|
throw new IllegalArgumentException("The object supplied is must be a RLiveObject"); |
|
} |
|
} |
|
|
|
private <T> void registerClassInternal(Class<T> entityClass) { |
|
DynamicType.Builder<T> builder = new ByteBuddy() |
|
.subclass(entityClass); |
|
for (FieldDescription.InDefinedShape field |
|
: Introspectior.getTypeDescription(LiveObjectTemplate.class) |
|
.getDeclaredFields()) { |
|
builder = builder.define(field); |
|
} |
|
|
|
Class<? extends T> proxied = builder.method(ElementMatchers.isDeclaredBy( |
|
Introspectior.getTypeDescription(RLiveObject.class)) |
|
.and(ElementMatchers.isGetter().or(ElementMatchers.isSetter()) |
|
.or(ElementMatchers.named("isPhantom")) |
|
.or(ElementMatchers.named("delete")))) |
|
.intercept(MethodDelegation.withDefaultConfiguration() |
|
.withBinders(FieldProxy.Binder |
|
.install(LiveObjectInterceptor.Getter.class, |
|
LiveObjectInterceptor.Setter.class)) |
|
.to(new LiveObjectInterceptor(redisson, entityClass, |
|
getRIdFieldName(entityClass)))) |
|
// .intercept(MethodDelegation.to( |
|
// new LiveObjectInterceptor(redisson, codecProvider, entityClass, |
|
// getRIdFieldName(entityClass))) |
|
// .appendParameterBinder(FieldProxy.Binder |
|
// .install(LiveObjectInterceptor.Getter.class, |
|
// LiveObjectInterceptor.Setter.class))) |
|
.implement(RLiveObject.class) |
|
.method(ElementMatchers.isAnnotatedWith(RFieldAccessor.class) |
|
.and(ElementMatchers.named("get") |
|
.or(ElementMatchers.named("set")))) |
|
.intercept(MethodDelegation.to(FieldAccessorInterceptor.class)) |
|
|
|
.method(ElementMatchers.isDeclaredBy(RObject.class) |
|
.or(ElementMatchers.isDeclaredBy(RObjectAsync.class))) |
|
.intercept(MethodDelegation.to(RObjectInterceptor.class)) |
|
.implement(RObject.class) |
|
|
|
.method(ElementMatchers.isDeclaredBy(RExpirable.class) |
|
.or(ElementMatchers.isDeclaredBy(RExpirableAsync.class))) |
|
.intercept(MethodDelegation.to(RExpirableInterceptor.class)) |
|
.implement(RExpirable.class) |
|
|
|
.method(ElementMatchers.isDeclaredBy(Map.class) |
|
.or(ElementMatchers.isDeclaredBy(ConcurrentMap.class)) |
|
.or(ElementMatchers.isDeclaredBy(RMapAsync.class)) |
|
.or(ElementMatchers.isDeclaredBy(RMap.class))) |
|
.intercept(MethodDelegation.to(RMapInterceptor.class)) |
|
.implement(RMap.class) |
|
|
|
.method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RLiveObject.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RExpirable.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RExpirableAsync.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RObject.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RObjectAsync.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(ConcurrentMap.class))) |
|
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Map.class))) |
|
.and(ElementMatchers.isGetter() |
|
.or(ElementMatchers.isSetter())) |
|
.and(ElementMatchers.isPublic() |
|
.or(ElementMatchers.isProtected())) |
|
) |
|
.intercept(MethodDelegation.to( |
|
new AccessorInterceptor(redisson, objectBuilder))) |
|
|
|
.make().load(getClass().getClassLoader(), |
|
ClassLoadingStrategy.Default.WRAPPER) |
|
.getLoaded(); |
|
classCache.putIfAbsent(entityClass, proxied); |
|
} |
|
}
|
|
|