001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.object;
006:
007: import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier;
008:
009: import com.tc.exception.ImplementMe;
010: import com.tc.net.protocol.tcm.TestChannelIDProvider;
011: import com.tc.object.bytecode.MockClassProvider;
012: import com.tc.object.cache.EvictionPolicy;
013: import com.tc.object.cache.NullCache;
014: import com.tc.object.config.DSOClientConfigHelper;
015: import com.tc.object.dna.api.DNA;
016: import com.tc.object.dna.api.DNACursor;
017: import com.tc.object.dna.api.DNAException;
018: import com.tc.object.idprovider.api.ObjectIDProvider;
019: import com.tc.object.loaders.ClassProvider;
020: import com.tc.object.logging.NullRuntimeLogger;
021: import com.tc.object.logging.RuntimeLogger;
022: import com.tc.object.tx.MockTransactionManager;
023: import com.tc.util.concurrent.ThreadUtil;
024:
025: import java.util.HashSet;
026:
027: public class ClientObjectManagerTest extends BaseDSOTestCase {
028: private ClientObjectManager mgr;
029: private TestRemoteObjectManager remoteObjectManager;
030: private DSOClientConfigHelper clientConfiguration;
031: private ObjectIDProvider idProvider;
032: private EvictionPolicy cache;
033: private RuntimeLogger runtimeLogger;
034: private ClassProvider classProvider;
035: private TCClassFactory classFactory;
036: private TestObjectFactory objectFactory;
037: private String rootName;
038: private Object object;
039: private ObjectID objectID;
040: private TCObject tcObject;
041:
042: public void setUp() throws Exception {
043: remoteObjectManager = new TestRemoteObjectManager();
044: classProvider = new MockClassProvider();
045: clientConfiguration = configHelper();
046: classFactory = new TestClassFactory();
047: objectFactory = new TestObjectFactory();
048: cache = new NullCache();
049: runtimeLogger = new NullRuntimeLogger();
050:
051: rootName = "myRoot";
052: object = new Object();
053: objectID = new ObjectID(1);
054: tcObject = new MockTCObject(objectID, object);
055: objectFactory.peerObject = object;
056: objectFactory.tcObject = tcObject;
057:
058: mgr = new ClientObjectManagerImpl(remoteObjectManager,
059: clientConfiguration, idProvider, cache, runtimeLogger,
060: new TestChannelIDProvider(), classProvider,
061: classFactory, objectFactory, new PortabilityImpl(
062: clientConfiguration), null);
063: mgr.setTransactionManager(new MockTransactionManager());
064: }
065:
066: /**
067: * Test to make sure that a root request that is blocked waiting for a server response doesn't block reconnect.
068: */
069: public void testLookupOrCreateRootDoesntBlockReconnect()
070: throws Exception {
071: // prepare a successful object lookup
072: TestDNA dna = newEmptyDNA();
073: prepareObjectLookupResults(dna);
074:
075: // prepare the lookup thread
076: CyclicBarrier barrier = new CyclicBarrier(1);
077: LookupRootAgent lookup1 = new LookupRootAgent(barrier, mgr,
078: rootName, object);
079:
080: // start the lookup
081: new Thread(lookup1).start();
082: // make sure the first caller has called down into the remote object manager
083: remoteObjectManager.retrieveRootIDCalls.take();
084: assertNull(remoteObjectManager.retrieveRootIDCalls.poll(0));
085:
086: // now make sure that concurrent reconnect activity doesn't block
087: mgr.pause();
088: mgr.starting();
089: mgr.getAllObjectIDsAndClear(new HashSet());
090: }
091:
092: /**
093: * Test to make sure that concurrent root requests only result in a single lookup to the server.
094: */
095: public void testLookupOrCreateRootConcurrently() throws Exception {
096:
097: // prepare a successful object lookup
098: TestDNA dna = newEmptyDNA();
099: prepareObjectLookupResults(dna);
100:
101: // prepare the lookup threads.
102: CyclicBarrier barrier = new CyclicBarrier(3);
103: LookupRootAgent lookup1 = new LookupRootAgent(barrier, mgr,
104: rootName, object);
105: LookupRootAgent lookup2 = new LookupRootAgent(barrier, mgr,
106: rootName, object);
107:
108: // start the first lookup
109: new Thread(lookup1).start();
110: // make sure the first caller has called down into the remote object manager.
111: remoteObjectManager.retrieveRootIDCalls.take();
112: assertNull(remoteObjectManager.retrieveRootIDCalls.poll(0));
113:
114: // now start another lookup and make sure that it doesn't call down into the remote object manager.
115: new Thread(lookup2).start();
116: ThreadUtil.reallySleep(5000);
117: assertNull(remoteObjectManager.retrieveRootIDCalls.poll(0));
118:
119: // allow the first lookup to proceed.
120: remoteObjectManager.retrieveRootIDResults.put(objectID);
121:
122: // make sure both lookups are complete.
123: barrier.barrier();
124:
125: assertTrue(lookup1.success() && lookup2.success());
126:
127: // They should both have the same result
128: assertEquals(lookup1, lookup2);
129:
130: // but, the remote object manager retrieveRootID() should only have been called the first time.
131: assertNull(remoteObjectManager.retrieveRootIDCalls.poll(0));
132: }
133:
134: private TestDNA newEmptyDNA() {
135: TestDNA dna = new TestDNA();
136: dna.type = Object.class;
137: dna.objectID = objectID;
138: dna.arraySize = 0;
139: return dna;
140: }
141:
142: private void prepareObjectLookupResults(TestDNA dna) {
143: remoteObjectManager.retrieveResults.put(dna);
144: }
145:
146: private static final class LookupRootAgent implements Runnable {
147: private final ClientObjectManager manager;
148: private final String rootName;
149: private final Object object;
150: public Object result;
151: public Throwable exception;
152: private final CyclicBarrier barrier;
153:
154: private LookupRootAgent(CyclicBarrier barrier,
155: ClientObjectManager manager, String rootName,
156: Object object) {
157: this .barrier = barrier;
158: this .manager = manager;
159: this .rootName = rootName;
160: this .object = object;
161: }
162:
163: public boolean success() {
164: return exception == null;
165: }
166:
167: public void run() {
168: try {
169: result = manager.lookupOrCreateRoot(this .rootName,
170: this .object);
171: } catch (Throwable t) {
172: t.printStackTrace();
173: this .exception = t;
174: } finally {
175: try {
176: barrier.barrier();
177: } catch (InterruptedException e) {
178: e.printStackTrace();
179: }
180: }
181: }
182:
183: public boolean equals(Object o) {
184: if (o instanceof LookupRootAgent) {
185: LookupRootAgent cmp = (LookupRootAgent) o;
186: if (result == null)
187: return cmp.result == null;
188: return result.equals(cmp.result);
189: } else
190: return false;
191: }
192:
193: public int hashCode() {
194: throw new RuntimeException("Don't ask me that.");
195: }
196:
197: }
198:
199: private static final class TestDNA implements DNA {
200:
201: public Class type;
202: public ObjectID objectID;
203: public ObjectID parentObjectID = ObjectID.NULL_ID;
204: public int arraySize;
205:
206: public long getVersion() {
207: throw new ImplementMe();
208: }
209:
210: public boolean hasLength() {
211: throw new ImplementMe();
212: }
213:
214: public int getArraySize() {
215: return this .arraySize;
216: }
217:
218: public String getTypeName() {
219: return getClass().getName();
220: }
221:
222: public ObjectID getObjectID() throws DNAException {
223: return objectID;
224: }
225:
226: public ObjectID getParentObjectID() throws DNAException {
227: return parentObjectID;
228: }
229:
230: public DNACursor getCursor() {
231: throw new ImplementMe();
232: }
233:
234: public String getDefiningLoaderDescription() {
235: return "";
236: }
237:
238: public boolean isDelta() {
239: return false;
240: }
241:
242: }
243:
244: }
|