Author: manik.surtani(a)jboss.com
Date: 2008-05-29 11:08:09 -0400 (Thu, 29 May 2008)
New Revision: 5918
Added:
core/trunk/src/main/java/org/jboss/cache/RegionEmptyException.java
core/trunk/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java
Modified:
core/trunk/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java
core/trunk/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java
Log:
JBCACHE-1349: Buddy replication state transfer fails if a marshalling region is empty
Added: core/trunk/src/main/java/org/jboss/cache/RegionEmptyException.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/RegionEmptyException.java
(rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/RegionEmptyException.java 2008-05-29 15:08:09
UTC (rev 5918)
@@ -0,0 +1,29 @@
+package org.jboss.cache;
+
+/**
+ * Exception to represent a region being empty when state was expected in that region.
+ *
+ * @author Manik Surtani (<a
href="mailto:manik@jboss.org">manik@jboss.org</a>)
+ * @since 2.2.0
+ */
+public class RegionEmptyException extends CacheException
+{
+ public RegionEmptyException()
+ {
+ }
+
+ public RegionEmptyException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public RegionEmptyException(String msg)
+ {
+ super(msg);
+ }
+
+ public RegionEmptyException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+}
Modified: core/trunk/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java 2008-05-29
14:49:10 UTC (rev 5917)
+++ core/trunk/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java 2008-05-29
15:08:09 UTC (rev 5918)
@@ -15,6 +15,7 @@
import org.jboss.cache.Node;
import org.jboss.cache.RPCManager;
import org.jboss.cache.Region;
+import org.jboss.cache.RegionEmptyException;
import org.jboss.cache.RegionManager;
import org.jboss.cache.commands.ReplicableCommand;
import org.jboss.cache.commands.VisitableCommand;
@@ -788,10 +789,7 @@
{
Fqn f = r.getFqn();
state = acquireState(f);
- if (state != null)
- {
- stateMap.put(f, state);
- }
+ if (state != null) stateMap.put(f, state);
}
}
else if (!configuration.isInactiveOnStartup())
@@ -885,7 +883,10 @@
byte[] state = generateState(fqn, timeouts[i], force);
if (log.isDebugEnabled())
{
- log.debug("acquireState(): got state");
+ if (state == null)
+ log.debug("acquireState(): Got null state. Region is probably
empty.");
+ else
+ log.debug("acquireState(): Got state");
}
return state;
}
@@ -950,7 +951,14 @@
{
ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(16 *
1024);
out = new MarshalledValueOutputStream(baos);
- stateTransferManager.getState(out, fqn, timeout, force, false);
+ try
+ {
+ stateTransferManager.getState(out, fqn, timeout, force, false);
+ }
+ catch (RegionEmptyException ree)
+ {
+ return null;
+ }
result = baos.getRawBuffer();
}
finally
Modified:
core/trunk/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java
===================================================================
---
core/trunk/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java 2008-05-29
14:49:10 UTC (rev 5917)
+++
core/trunk/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java 2008-05-29
15:08:09 UTC (rev 5918)
@@ -12,6 +12,7 @@
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.NodeSPI;
+import org.jboss.cache.RegionEmptyException;
import org.jboss.cache.RegionManager;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.factories.annotations.Inject;
@@ -20,6 +21,7 @@
import org.jboss.cache.lock.LockManager;
import static org.jboss.cache.lock.LockType.READ;
import org.jboss.cache.lock.TimeoutException;
+import org.jboss.cache.marshall.InactiveRegionException;
import org.jboss.cache.marshall.Marshaller;
import org.jboss.cache.marshall.NodeData;
import org.jboss.cache.marshall.NodeDataMarker;
@@ -83,7 +85,7 @@
public void getState(ObjectOutputStream out, Fqn fqn, long timeout, boolean force,
boolean suppressErrors) throws Throwable
{
// can't give state for regions currently being activated/inactivated
- boolean canProvideState = (!regionManager.isInactive(fqn) &&
cache.peek(fqn, false, false) != null);
+ boolean canProvideState = (!regionManager.isInactive(fqn) &&
cache.peek(fqn, false) != null);
boolean fetchTransientState = configuration.isFetchInMemoryState();
CacheLoaderManager cacheLoaderManager = cache.getCacheLoaderManager();
@@ -126,19 +128,20 @@
if (regionManager.isInactive(fqn))
{
exceptionMessage += " Region for fqn " + fqn + " is
inactive.";
+ e = new InactiveRegionException(exceptionMessage);
}
+ // this is not really an exception. Just provide empty state. The exception
is just a signal. Yes, lousy. - JBCACHE-1349
if (cache.peek(fqn, false, false) == null)
{
- exceptionMessage += " There is no cache node at fqn " + fqn;
+ e = new RegionEmptyException();
}
- e = new CacheException(exceptionMessage);
}
if (!fetchPersistentState && !fetchTransientState)
{
e = new CacheException("Cache instance at " +
cache.getLocalAddress() + " is not configured to provide state");
}
marshaller.objectToObjectStream(e, out);
- throw e;
+ if (e != null) throw e;
}
}
Added: core/trunk/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java
(rev 0)
+++
core/trunk/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java 2008-05-29
15:08:09 UTC (rev 5918)
@@ -0,0 +1,88 @@
+package org.jboss.cache.buddyreplication;
+
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Region;
+import org.jboss.cache.notifications.annotation.BuddyGroupChanged;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.event.Event;
+import org.jboss.cache.util.CachePrinter;
+import org.jboss.cache.util.TestingUtil;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * To test
http://jira.jboss.org/jira/browse/JBCACHE-1349
+ *
+ * @author Manik Surtani (<a
href="mailto:manik@jboss.org">manik@jboss.org</a>)
+ */
+@Test(groups = "functional")
+public class EmptyRegionTest extends BuddyReplicationTestsBase
+{
+ CacheSPI c1, c2;
+ Fqn regionFqn = Fqn.fromString("/a/b/c");
+ Fqn region2Fqn = Fqn.fromString("/d/e/f");
+ Region region, region2;
+ CountDownLatch buddyJoinLatch = new CountDownLatch(1);
+
+ @BeforeTest
+ public void setUp() throws Exception
+ {
+ c1 = createCache(1, null, false, false, false);
+ c1.getConfiguration().setUseRegionBasedMarshalling(true);
+ c1.getConfiguration().setFetchInMemoryState(true);
+ c2 = (CacheSPI) new
DefaultCacheFactory().createCache(c1.getConfiguration().clone(), false);
+ c1.start();
+ region = c1.getRegion(regionFqn, true);
+ region2 = c1.getRegion(region2Fqn, true);
+ region.registerContextClassLoader(getClass().getClassLoader());
+ region2.registerContextClassLoader(getClass().getClassLoader());
+ c1.put(region2Fqn, "key", "value");
+
+ c2.create();
+ c2.addCacheListener(new BuddyJoinListener());
+ }
+
+ @AfterTest
+ public void tearDown()
+ {
+ TestingUtil.killCaches(c1, c2);
+ }
+
+ public void testEmptyRegion() throws InterruptedException
+ {
+ // region on c1 is empty - with no root node.
+ assert c1.getNode(regionFqn) == null : "Node should not exist";
+ assert c1.getRegion(regionFqn, false) != null : "Region should exist";
+ assert c1.getRegion(regionFqn, false).isActive() : "Region should be
active";
+
+ // now start c2
+ c2.start();
+
+ // wait for buddy join notifications to complete.
+ buddyJoinLatch.await(60, TimeUnit.SECONDS);
+
+ // should not throw any exceptions!!
+
+ System.out.println("Cache1 " + CachePrinter.printCacheDetails(c1));
+ System.out.println("Cache2 " + CachePrinter.printCacheDetails(c2));
+
+ // make sure region2 stuff did get transmitted!
+ assert c2.peek(BuddyFqnTransformer.getBackupFqn(c1.getLocalAddress(), region2Fqn),
false) != null : "Region2 state should have transferred!";
+ }
+
+ @CacheListener
+ public class BuddyJoinListener
+ {
+ @BuddyGroupChanged
+ public void buddyJoined(Event e)
+ {
+ buddyJoinLatch.countDown();
+ }
+ }
+}