blob: a6dbc7a4a27ad432a9dafb2844d02e9c857e1f2d [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.modules.hibernate.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import org.hibernate.cache.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.EntityRegion;
import org.hibernate.cache.access.AccessType;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.internal.cache.LocalRegion;
import com.gemstone.gemfire.modules.hibernate.GemFireRegionFactory;
import com.gemstone.gemfire.modules.util.ModuleStatistics;
public class GemFireEntityRegion extends GemFireBaseRegion implements EntityRegion {
private final Logger log = LoggerFactory.getLogger(getClass());
private final boolean USE_JTA = Boolean.getBoolean("gemfiremodules.useJTA");
/**
* keys for which interest has been registered already
*/
private ConcurrentMap<Object, Boolean> registeredKeys = new ConcurrentHashMap<Object, Boolean>();
/**
* map to store the entries that were pre-fetched when the underlying region has no local storage
*/
protected ConcurrentMap<Object, EntityWrapper> preFetchMap = new ConcurrentHashMap<Object, EntityWrapper>();
public GemFireEntityRegion(Region<Object, EntityWrapper> region,
boolean isClient, CacheDataDescription metadata, GemFireRegionFactory regionFactory) {
super(region, isClient, metadata, regionFactory);
}
@Override
public boolean isTransactionAware() {
// there are no colocation guarantees while using hibernate
// so return false for a PartitionedRegion for now
if (USE_JTA) {
return true;
}
return false;
}
@Override
public CacheDataDescription getCacheDataDescription() {
return this.metadata;
}
@Override
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType)
throws CacheException {
if (AccessType.READ_ONLY.equals(accessType)) {
log.info("creating read-only access for region: " + this.getName());
return new ReadOnlyAccess(this);
}
else if (AccessType.NONSTRICT_READ_WRITE.equals(accessType)) {
log.info("creating nonstrict-read-write access for region: "
+ this.getName());
return new NonStrictReadWriteAccess(this);
}
else if (AccessType.READ_WRITE.equals(accessType)) {
log.info("creating read-write access for region: "
+ this.getName());
return new ReadWriteAccess(this);
}
else if (AccessType.TRANSACTIONAL.equals(accessType)) {
log.info("creating transactional access for region: "
+ this.getName());
return new TransactionalAccess(this);
}
throw new UnsupportedOperationException("Unknown access type: "
+ accessType);
}
/**
* Should this region should register interest in keys.
* @return true for client regions with storage
*/
public boolean isRegisterInterestRequired() {
return this.isClientRegion && this.region.getAttributes().getDataPolicy().withStorage();
}
/**
* register interest in this key, if not already registered
* @param key
*/
public void registerInterest(Object key) {
if (!this.registeredKeys.containsKey(key)) {
this.region.registerInterest(key);
this.registeredKeys.put(key, Boolean.TRUE);
log.debug("registered interest in key{}", key);
}
}
public void registerInterest(Collection<?> list) {
// build a list of keys for which interest is not
// already registered
List<Object> interestList = new ArrayList<Object>();
for (Object o : list) {
if (!this.registeredKeys.containsKey(o)) {
interestList.add(o);
}
}
// register interest in this list
this.region.registerInterest(interestList);
log.debug("registered interest in {} keys", interestList.size());
}
/**
* wraps the keys in {@link KeyWrapper} and calls getAll
* on the underlying GemFire region. When the underlying region
* is a proxy region, the fetched entries are stored in a local
* map.
* @param keys
*/
public void getAll(Collection<?> keys) {
Set<KeyWrapper> wrappedKeys = new HashSet<KeyWrapper>();
for (Object o : keys) {
wrappedKeys.add(new KeyWrapper(o));
}
if (isRegisterInterestRequired()) {
registerInterest(wrappedKeys);
} else {
Map<Object, EntityWrapper> retVal = this.region.getAll(wrappedKeys);
putInLocalMap(retVal);
}
}
/**
* if the underlying gemfire region does not have local storage, put
* the pre-fetched entries in {@link #preFetchMap}
* @param map map of prefetched entries
*/
private void putInLocalMap(Map<Object, EntityWrapper> map) {
if (!this.region.getAttributes().getDataPolicy().withStorage()) {
// if the value is null, do not cache in preFetchMap
for (Entry<Object, EntityWrapper> e : map.entrySet()) {
if (e.getValue() != null) {
this.preFetchMap.put(e.getKey(), e.getValue());
log.debug("putting key: {} value: {} in local map", e.getKey(), e.getValue());
}
}
}
}
/**
* If this key was pre-fetched, get the entity.
* @param key
* @return the prefetched entity
*/
public EntityWrapper get(Object key) {
return this.preFetchMap.remove(key);
}
}