blob: 65c2c51ad2f4c5b36f2fe1dc645356cea2b2205e [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.util;
import com.gemstone.gemfire.cache.*;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.execute.*;
import com.gemstone.gemfire.cache.partition.PartitionRegionHelper;
import com.gemstone.gemfire.distributed.DistributedLockService;
import com.gemstone.gemfire.distributed.internal.locks.DistributedMemberLock;
import com.gemstone.gemfire.internal.cache.PartitionedRegion;
import com.gemstone.gemfire.internal.cache.xmlcache.CacheXmlGenerator;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
public class CreateRegionFunction implements Function, Declarable {
private static final long serialVersionUID = -9210226844302128969L;
private final Cache cache;
private final Region<String,RegionConfiguration> regionConfigurationsRegion;
public static final String ID = "create-region-function";
private static final boolean DUMP_SESSION_CACHE_XML =
Boolean.getBoolean("gemfiremodules.dumpSessionCacheXml");
private static final String REGION_CONFIGURATION_METADATA_REGION = "__regionConfigurationMetadata";
public CreateRegionFunction() {
this(CacheFactory.getAnyInstance());
}
public CreateRegionFunction(Cache cache) {
this.cache = cache;
this.regionConfigurationsRegion = createRegionConfigurationMetadataRegion();
}
public CreateRegionFunction(ClientCache cache) {
this.cache = null;
this.regionConfigurationsRegion = null;
}
public void execute(FunctionContext context) {
RegionConfiguration configuration = (RegionConfiguration) context.getArguments();
if (this.cache.getLogger().fineEnabled()) {
StringBuilder builder = new StringBuilder();
builder
.append("Function ")
.append(ID)
.append(" received request: ")
.append(configuration);
this.cache.getLogger().fine(builder.toString());
}
// Create or retrieve region
RegionStatus status = createOrRetrieveRegion(configuration);
// Dump XML
if (DUMP_SESSION_CACHE_XML) {
writeCacheXml();
}
// Return status
context.getResultSender().lastResult(status);
}
private RegionStatus createOrRetrieveRegion(RegionConfiguration configuration) {
RegionStatus status = null;
String regionName = configuration.getRegionName();
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger().fine("Function " + ID + " retrieving region named: " + regionName);
}
Region region = this.cache.getRegion(regionName);
if (region == null) {
status = createRegion(configuration);
} else {
status = RegionStatus.VALID;
try {
RegionHelper.validateRegion(this.cache, configuration, region);
} catch (Exception e) {
if (!e.getMessage()
.equals(
LocalizedStrings.RegionAttributesCreation_CACHELISTENERS_ARE_NOT_THE_SAME
.toLocalizedString())) {
this.cache.getLogger().warning(e);
}
status = RegionStatus.INVALID;
}
}
return status;
}
public String getId() {
return ID;
}
public boolean optimizeForWrite() {
return false;
}
public boolean isHA() {
return true;
}
public boolean hasResult() {
return true;
}
public void init(Properties properties) {
}
private RegionStatus createRegion(RegionConfiguration configuration) {
// Get a distributed lock
DistributedMemberLock dml = getDistributedLock();
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger().fine(this + ": Attempting to lock " + dml);
}
long start=0, end=0;
RegionStatus status = null;
try {
if (this.cache.getLogger().fineEnabled()) {
start = System.currentTimeMillis();
}
// Obtain a lock on the distributed lock
dml.lockInterruptibly();
if (this.cache.getLogger().fineEnabled()) {
end = System.currentTimeMillis();
this.cache.getLogger().fine(this + ": Obtained lock on " + dml + " in " + (end-start) + " ms");
}
// Attempt to get the region again after the lock has been obtained
String regionName = configuration.getRegionName();
Region region = this.cache.getRegion(regionName);
// If it exists now, validate it.
// Else put an entry into the sessionRegionConfigurationsRegion
// while holding the lock. This will create the region in all VMs.
if (region == null) {
this.regionConfigurationsRegion.put(regionName, configuration);
// Retrieve the region after creating it
region = this.cache.getRegion(regionName);
// If the region is null now, it wasn't created for some reason
// (e.g. the region attributes id were invalid)
if (region == null) {
status = RegionStatus.INVALID;
} else {
// Create the PR buckets if necessary)
if (region instanceof PartitionedRegion) {
PartitionedRegion pr = (PartitionedRegion) region;
createBuckets(pr);
}
status = RegionStatus.VALID;
}
} else {
status = RegionStatus.VALID;
try {
RegionHelper.validateRegion(this.cache, configuration, region);
} catch (Exception e) {
if (!e.getMessage()
.equals(
LocalizedStrings.RegionAttributesCreation_CACHELISTENERS_ARE_NOT_THE_SAME
.toLocalizedString())) {
this.cache.getLogger().warning(e);
}
status = RegionStatus.INVALID;
}
}
} catch(Exception e) {
StringBuilder builder = new StringBuilder();
builder
.append(this)
.append(": Caught Exception attempting to create region named ")
.append(configuration.getRegionName())
.append(":");
this.cache.getLogger().warning(builder.toString(), e);
status = RegionStatus.INVALID;
}
finally {
// Unlock the distributed lock
try {
dml.unlock();
}
catch (Exception e) {}
}
return status;
}
private void createBuckets(PartitionedRegion region) {
PartitionRegionHelper.assignBucketsToPartitions(region);
}
private Region<String,RegionConfiguration> createRegionConfigurationMetadataRegion() {
// a sessionFactory in hibernate could have been re-started
// so, it is possible that this region exists already
Region<String, RegionConfiguration> r = this.cache
.getRegion(REGION_CONFIGURATION_METADATA_REGION);
if (r != null) {
return r;
}
RegionFactory<String, RegionConfiguration> factory = this.cache
.createRegionFactory(RegionShortcut.REPLICATE);
factory.addCacheListener(new RegionConfigurationCacheListener());
return factory.create(REGION_CONFIGURATION_METADATA_REGION);
}
private void writeCacheXml() {
File file = new File("cache-" + System.currentTimeMillis() + ".xml");
try {
PrintWriter pw = new PrintWriter(new FileWriter(file), true);
CacheXmlGenerator.generate(this.cache, pw);
pw.close();
} catch (IOException e) {}
}
private DistributedMemberLock getDistributedLock() {
String dlsName = this.regionConfigurationsRegion.getName();
DistributedLockService lockService = initializeDistributedLockService(dlsName);
String lockToken = dlsName + "_token";
return new DistributedMemberLock(lockService, lockToken);
}
private DistributedLockService initializeDistributedLockService(String dlsName) {
DistributedLockService lockService = DistributedLockService.getServiceNamed(dlsName);
if (lockService == null) {
lockService = DistributedLockService.create(dlsName, this.cache.getDistributedSystem());
}
return lockService;
}
}