All Downloads are FREE. Search and download functionalities are using the official Maven repository.

wanActiveActive.WANConflictResolver Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
package wanActiveActive;

import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.Declarable;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.util.GatewayConflictHelper;
import com.gemstone.gemfire.cache.util.GatewayConflictResolver;
import com.gemstone.gemfire.cache.util.TimestampedEntryEvent;
import com.gemstone.gemfire.pdx.PdxInstance;

import java.util.List;
import java.util.Properties;


/**
 * Class WANConflictResolver is a GatewayConflictResolver 
 * provides several alternative methods for resolving conflicts in a WAN configuration
 * when multiple sites are modifying the same objects.

* The resolver is installed into the cache through eu-hub/cache.xml and * us-hub/cache.xml. These determine the type of conflict resolution that * will be performed. The options are: *

 *      use-latest
 *      merge
 *      highest-int
 * 
* use-latest will select to keep the event with the latest timestamp.
* merge will select to merge the values together.
* highest-int will select to keep the event with the highest modificationCount field. */ public class WANConflictResolver implements GatewayConflictResolver, Declarable, Dialog.CloseCallback { /** Counter tracking the number of conflicting events received */ protected int numberOfConflicts; Dialog dialog; private Thread guiUpdateThread; /** Conflict resolution options */ static enum conflictResolutionEnum { USE_LATEST, MERGE, HIGHEST_INT }; conflictResolutionEnum resolutionType = conflictResolutionEnum.USE_LATEST; /** * Initializes this WANConflictResolver. * @param p The Properties with which to initialize this * WANConflictResolver */ public void init(Properties p) { String rType = p.getProperty("resolution-type"); if (rType == null) { System.out.println("WANConflictResolver: server conflict resolution will keep the latest change"); resolutionType = conflictResolutionEnum.USE_LATEST; } else if (rType.equalsIgnoreCase("use-latest")) { System.out.println("WANConflictResolver: server conflict resolution will keep the latest change"); resolutionType = conflictResolutionEnum.USE_LATEST; } else if (rType.equalsIgnoreCase("merge")) { System.out.println("WANConflictResolver: server conflict resolution will merge conflicting changes"); resolutionType = conflictResolutionEnum.MERGE; } else if (rType.equalsIgnoreCase("highest-int")) { System.out.println("WANConflictResolver: server conflict resolution will keep the highest modificationCount"); resolutionType = conflictResolutionEnum.HIGHEST_INT; } /** open GUI to show state and conflict count */ String site = p.getProperty("site", ""); this.dialog = new Dialog(site + " conflict resolver", this, true); this.dialog.open(); this.dialog.setModification("conflict resolver initialized with \"" + rType + "\""); startGUIUpdateThread(); } /* (non-Javadoc) * @see com.gemstone.gemfire.cache.util.GatewayConflictResolver#onEvent(com.gemstone.gemfire.cache.util.TimestampedEntryEvent, com.gemstone.gemfire.cache.util.GatewayConflictHelper) */ @Override public void onEvent(TimestampedEntryEvent event, GatewayConflictHelper helper) { LogWriter log = CacheFactory.getAnyInstance().getLogger(); if (!event.getOperation().isUpdate()) { // for things other than updates use the default timestamp and ID based resolution if (event.getOldTimestamp() > event.getNewTimestamp()) { helper.disallowEvent(); } if (event.getOldTimestamp() == event.getNewTimestamp() && event.getOldDistributedSystemID() > event.getNewDistributedSystemID()) { helper.disallowEvent(); } return; } // cache.xml sets the PDX read-serialized attribute so that the conflict resolver // can work directly with serialized objects. PdxInstance oldObj = (PdxInstance)event.getOldValue(); PdxInstance newObj = (PdxInstance)event.getNewValue(); // log information about the conflict. This will appear in cacheserver.log log.info("WANConflictResolver: \nexisting timestamp=" + event.getOldTimestamp() + "\nexisting value=" + oldObj + "\nproposed timestamp=" + event.getNewTimestamp() + "\nproposed value=" + newObj); switch (this.resolutionType) { case USE_LATEST: if (event.getOldTimestamp() > event.getNewTimestamp()) { log.info("event is older than cached value - disallowing event"); helper.disallowEvent(); } else if (event.getOldTimestamp() == event.getNewTimestamp() && event.getOldDistributedSystemID() != 2) { /* eu hub always wins if timestamps are the same */ log.info("event has the same timestamp as the cached value but is from US - disallowing event"); helper.disallowEvent(); } // else { // log.info("event is newer than the cached value - allowing event"); // } break; case MERGE: // Grab the conflicting event's history and add the current value's change to it. // This assumes there has been only one conflicting modification. Otherwise we // would need to do a more thorough job of merging the histories. String lastChange = Value.getModification(oldObj); List history = Value.getHistory(newObj); if (!history.contains(lastChange)) { log.info("merging existing change '" + lastChange +"' into new value"); history.add(0, lastChange); PdxInstance newValue = Value.setHistory(newObj, history); helper.changeEventValue(newValue); } else { log.info("new history contains my last known change, so I'm allowing this event"); } break; case HIGHEST_INT: // The value is stored as a PDX serialized object, so we use PDX to access it int oldInt = Value.getModificationCount(oldObj); int newInt = Value.getModificationCount(newObj); if (oldInt > newInt) { log.info("disallowing event"); helper.disallowEvent(); } break; } this.dialog.setConflictCount(++this.numberOfConflicts); } private void startGUIUpdateThread() { this.guiUpdateThread = new Thread("wanActiveActive GUI update thread") { public void run() { while (true) { try { Region region = CacheFactory.getAnyInstance().getRegion("/wanActiveActive"); if (region != null) { PdxInstance serializedValue = (PdxInstance)region.get("MyValue"); if (serializedValue != null) { dialog.setModification(Value.getModification(serializedValue)); dialog.setHistory(Value.getHistory(serializedValue).toString()); } } try { Thread.sleep(2000); } catch (InterruptedException e) { return; } } catch (CacheClosedException e) { return; } catch (RuntimeException e) { e.printStackTrace(); return; } } } }; this.guiUpdateThread.setDaemon(true); this.guiUpdateThread.start(); } /** This is invoked when the dialog closes. It will cause the server to shut down. */ public void close() { System.exit(0); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy