/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.pojo.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Handling the Collection class management. Has no consideration of object graph here.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: CollectionClassHandler.java 4081 2007-06-28 00:56:07Z jgreene $
*/
class CollectionClassHandler
{
private final Log log = LogFactory.getLog(CollectionClassHandler.class);
private Cache<Object, Object> cache_;
private PojoCacheImpl pCache_;
private InternalHelper internal_;
public CollectionClassHandler(PojoCacheImpl pCache, InternalHelper internal)
{
pCache_ = pCache;
cache_ = pCache_.getCache();
internal_ = internal;
}
Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
throws CacheException
{
Object obj = null;
try
{
if (Map.class.isAssignableFrom(clazz))
{
Object map = clazz.newInstance();
obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) map);
}
else if (List.class.isAssignableFrom(clazz))
{
Object list = clazz.newInstance();
obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) list);
}
else if (Set.class.isAssignableFrom(clazz))
{
Object set = clazz.newInstance();
obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) set);
}
}
catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
return obj;
}
void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
{
boolean isCollection = false;
CachedType type = null;
if (obj instanceof ClassProxy)
{
throw new IllegalStateException("CollectionClassHandler.put(): obj is an ClassProxy instance " + obj);
}
type = pCache_.getCachedType(obj.getClass());
//JBCACHE-760: for collection - put initialized aopInstance in fqn
if (!(obj instanceof Map || obj instanceof List || obj instanceof Set))
{
return;
}
// Always initialize the ref count so that we can mark this as an AopNode.
PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
pojoInstance.set(obj);
pojoInstance.setPojoClass(type.getType());
cache_.put(fqn, PojoInstance.KEY, pojoInstance);
if (obj instanceof Map)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
}
// Let's replace it with a proxy if necessary
Map map = (Map) obj;
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) obj);
}
catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkMapRecursion(map, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = map.entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = (Map.Entry) i.next();
((Map) obj).put(entry.getKey(), entry.getValue());
}
}
else if (obj instanceof List)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a List type of size: "
+ ((List) obj).size());
}
List list = (List) obj;
// Let's replace it with a proxy if necessary
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) obj);
}
catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkListRecursion(list, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = list.iterator(); i.hasNext();)
{
((List) obj).add(i.next());
}
}
else if (obj instanceof Set)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a Set type of size: "
+ ((Set) obj).size());
}
Set set = (Set) obj;
// Let's replace it with a proxy if necessary
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) obj);
}
catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkSetRecursion(set, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = set.iterator(); i.hasNext();)
{
((Set) obj).add(i.next());
}
}
if (isCollection)
{
// Need to reset it here in case this is a new proxy instance
pojoInstance.set(obj);
// Attach pojoReference to that interceptor
BaseInterceptor baseInterceptor = (BaseInterceptor) CollectionInterceptorUtil.getInterceptor(
(ClassProxy) obj);
baseInterceptor.setAopInstance(pojoInstance);
}
}
private void checkListRecursion(List list, Object obj)
{
while (true)
{
int i = list.indexOf(list); // check for recursion
if (i == -1) break;
list.remove(list);
list.add(i, obj);
}
}
private void checkSetRecursion(Set set, Object obj)
{
if (set.remove(set))
{
set.add(obj); // replace with proxy
throw new PojoCacheException("CollectionClassHandler.checkSetRecursion(): " +
"detect a recursive set (e.g., set inside the same set). This will fail to " +
"replicate even outside of PojoCache with HashSet. " + set);
}
}
private void checkMapRecursion(Map map, Object obj)
{
Map m = java.util.Collections.unmodifiableMap(map);
for (Object k : m.keySet())
{
if (m == k)
{
throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
" Can't handle the recursion map where it is nested in a constant key " + map);
}
Object v = m.get(k);
if (v == map)
{
throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
"detect a recursive map (e.g., map inside the same map). This will fail to " +
"replicate even outside of PojoCache with HashMap because of hashCode. " + map);
// recursion here, replace it with proxy
// map.put(k, obj);
}
}
}
Object remove(Fqn fqn, Object obj) throws CacheException
{
if (!(obj instanceof ClassProxy))
{
throw new PojoCacheException("CollectionClassHandler.collectionRemoveObject(): object is not a proxy :" + obj);
}
Interceptor interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
boolean removeFromCache = true;
// detach the interceptor. This will trigger a copy and remove.
((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);
return ((AbstractCollectionInterceptor) interceptor).getCurrentCopy();
}
}