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.tctest;
006:
007: import org.apache.xmlbeans.XmlObject;
008:
009: import com.tc.cluster.Cluster;
010: import com.tc.config.schema.NewCommonL2Config;
011: import com.tc.config.schema.NewHaConfig;
012: import com.tc.config.schema.NewSystemConfig;
013: import com.tc.config.schema.UpdateCheckConfig;
014: import com.tc.config.schema.dynamic.BooleanConfigItem;
015: import com.tc.config.schema.dynamic.ConfigItem;
016: import com.tc.config.schema.dynamic.ConfigItemListener;
017: import com.tc.config.schema.dynamic.IntConfigItem;
018: import com.tc.config.schema.dynamic.StringConfigItem;
019: import com.tc.config.schema.setup.ConfigurationSetupException;
020: import com.tc.config.schema.setup.L1TVSConfigurationSetupManager;
021: import com.tc.config.schema.setup.L2TVSConfigurationSetupManager;
022: import com.tc.config.schema.setup.TestTVSConfigurationSetupManagerFactory;
023: import com.tc.exception.TCLockUpgradeNotSupportedError;
024: import com.tc.lang.StartupHelper;
025: import com.tc.lang.TCThreadGroup;
026: import com.tc.lang.ThrowableHandler;
027: import com.tc.lang.StartupHelper.StartupAction;
028: import com.tc.logging.TCLogging;
029: import com.tc.net.protocol.transport.NullConnectionPolicy;
030: import com.tc.object.BaseDSOTestCase;
031: import com.tc.object.DistributedObjectClient;
032: import com.tc.object.bytecode.MockClassProvider;
033: import com.tc.object.bytecode.NullManager;
034: import com.tc.object.bytecode.hook.impl.PreparedComponentsFromL2Connection;
035: import com.tc.object.config.DSOClientConfigHelper;
036: import com.tc.object.config.StandardDSOClientConfigHelperImpl;
037: import com.tc.object.config.schema.NewDSOApplicationConfig;
038: import com.tc.object.config.schema.NewL2DSOConfig;
039: import com.tc.object.lockmanager.api.LockID;
040: import com.tc.object.lockmanager.api.LockLevel;
041: import com.tc.object.lockmanager.api.ThreadID;
042: import com.tc.object.lockmanager.impl.ClientLockManagerImpl;
043: import com.tc.objectserver.impl.DistributedObjectServer;
044: import com.tc.objectserver.lockmanager.api.DeadlockChain;
045: import com.tc.objectserver.lockmanager.api.DeadlockResults;
046: import com.tc.objectserver.managedobject.ManagedObjectStateFactory;
047: import com.tc.properties.TCPropertiesImpl;
048: import com.tc.server.NullTCServerInfo;
049: import com.tc.util.concurrent.SetOnceFlag;
050: import com.tc.util.concurrent.ThreadUtil;
051:
052: import java.io.InputStream;
053: import java.util.ArrayList;
054: import java.util.List;
055:
056: public class LockManagerSystemTest extends BaseDSOTestCase {
057:
058: // please keep this set to true so that tests on slow/loaded machines don't fail. When working on this test though, it
059: // can be convenient to temporarily flip it to false
060: private static final boolean slow = true;
061:
062: private DistributedObjectServer server;
063: private DistributedObjectClient client;
064: private ClientLockManagerImpl lockManager;
065: private TCThreadGroup group = new TCThreadGroup(
066: new ThrowableHandler(TCLogging
067: .getLogger(DistributedObjectServer.class)));
068:
069: static {
070: /*
071: * This test run against JDK1.4 and creates and executes multiple embedded DistributedObjectServers. L2Management
072: * creates an RMI registry on the JMX port, which is randomly changing in this test. In 1.4 it's not possible to
073: * create multiple RMI Registries in a single VM. Remove this when we no longer support 1.4.
074: */
075: System.setProperty("org.terracotta.server.disableJmxConnector",
076: "true");
077: /*
078: * disable OOO temporary because:
079: * It keeps starting and stopping different client/server within the same process
080: * and cleaning up the environement etc. Since the shutdown methods are poorly supported
081: * as of now Sometimes the clients are still trying to reconnect to non-exisitent servers
082: * and with OOO it seems to happen more.
083: */
084: TCPropertiesImpl.setProperty("l1.reconnect.enabled", "false");
085: }
086:
087: private class StartAction implements StartupAction {
088: private final L2TVSConfigurationSetupManager l2Manager;
089:
090: StartAction(L2TVSConfigurationSetupManager l2manager) {
091: this .l2Manager = l2manager;
092: }
093:
094: public void execute() throws Throwable {
095: server = new DistributedObjectServer(new ConfigOverride(
096: l2Manager), group, new NullConnectionPolicy(),
097: new NullTCServerInfo());
098: server.start();
099: }
100: }
101:
102: public void setUp() throws Exception {
103: TestTVSConfigurationSetupManagerFactory factory = createDistributedConfigFactory();
104:
105: ManagedObjectStateFactory.disableSingleton(true);
106: L2TVSConfigurationSetupManager l2Manager = factory
107: .createL2TVSConfigurationSetupManager(null);
108:
109: new StartupHelper(group, new StartAction(l2Manager)).startUp();
110:
111: factory.addServerToL1Config(null, server.getListenPort(), -1);
112:
113: L1TVSConfigurationSetupManager manager = factory
114: .createL1TVSConfigurationSetupManager();
115: DSOClientConfigHelper configHelper = new StandardDSOClientConfigHelperImpl(
116: manager);
117:
118: PreparedComponentsFromL2Connection components = new PreparedComponentsFromL2Connection(
119: manager);
120:
121: client = new DistributedObjectClient(configHelper,
122: new TCThreadGroup(new ThrowableHandler(TCLogging
123: .getLogger(DistributedObjectClient.class))),
124: new MockClassProvider(), components, NullManager
125: .getInstance(), new Cluster());
126: client.start();
127:
128: lockManager = (ClientLockManagerImpl) client.getLockManager();
129: }
130:
131: protected void tearDown() {
132: if (client != null) {
133: try {
134: // client created by NullManager, the stop() is an empty call.
135: // Locate CommunicationManager to do real shutdown.
136: // client.stop();
137: client.getCommunicationsManager().shutdown();
138: } catch (Exception e) {
139: e.printStackTrace();
140: }
141: }
142: ThreadUtil.reallySleep(3000);
143: if (server != null) {
144: try {
145: server.stop();
146: } catch (Exception e) {
147: e.printStackTrace();
148: }
149: }
150: }
151:
152: public void disableTestUpgradeDeadlock() throws Exception {
153: final LockID l1 = new LockID("1");
154:
155: final ThreadID tid1 = new ThreadID(1);
156: final ThreadID tid2 = new ThreadID(2);
157:
158: lockManager.lock(l1, tid1, LockLevel.READ);
159: lockManager.lock(l1, tid2, LockLevel.READ);
160:
161: Thread t1 = new Thread() {
162: public void run() {
163: LockManagerSystemTest.this .lockManager.lock(l1, tid1,
164: LockLevel.WRITE);
165: }
166: };
167:
168: Thread t2 = new Thread() {
169: public void run() {
170: LockManagerSystemTest.this .lockManager.lock(l1, tid2,
171: LockLevel.WRITE);
172: }
173: };
174:
175: t1.start();
176: t2.start();
177:
178: t1.join(2000);
179: t2.join(2000);
180:
181: assertTrue(t1.isAlive());
182: assertTrue(t2.isAlive());
183:
184: // make sure we "see" the deadlock on the server side
185: final List deadlocks = new ArrayList();
186: DeadlockResults results = new DeadlockResults() {
187: public void foundDeadlock(DeadlockChain chain) {
188: deadlocks.add(chain);
189: }
190: };
191:
192: server.getContext().getLockManager().scanForDeadlocks(results);
193:
194: assertEquals(1, deadlocks.size());
195: DeadlockChain chain = (DeadlockChain) deadlocks.remove(0);
196:
197: ThreadID id1 = chain.getWaiter().getClientThreadID();
198: LockID lid1 = chain.getWaitingOn();
199: chain = chain.getNextLink();
200: ThreadID id2 = chain.getWaiter().getClientThreadID();
201: LockID lid2 = chain.getWaitingOn();
202:
203: assertEquals(id1, chain.getNextLink().getWaiter()
204: .getClientThreadID());
205:
206: assertEquals(lid1, l1);
207: assertEquals(lid2, l1);
208: assertEquals(lid1, lid2);
209:
210: if (id1.equals(tid1)) {
211: assertEquals(id2, tid2);
212: } else {
213: assertEquals(id1, tid2);
214: }
215:
216: }
217:
218: private static void sleep(long amount) {
219: amount *= (slow ? 300 : 50);
220: ThreadUtil.reallySleep(amount);
221: }
222:
223: public void testUpgradeNotSupported() throws Exception {
224: final LockID l1 = new LockID("1");
225:
226: final ThreadID tid1 = new ThreadID(1);
227: final ThreadID tid2 = new ThreadID(2);
228: final ThreadID tid3 = new ThreadID(3);
229:
230: final SetOnceFlag flag = new SetOnceFlag();
231: lockManager.lock(l1, tid1, LockLevel.READ);
232: lockManager.lock(l1, tid2, LockLevel.READ);
233: lockManager.lock(l1, tid3, LockLevel.READ);
234:
235: Thread t = new Thread() {
236: public void run() {
237: try {
238: LockManagerSystemTest.this .lockManager.lock(l1,
239: tid1, LockLevel.WRITE);
240: throw new AssertionError(
241: "Should have thrown a TCLockUpgradeNotSupportedError.");
242: } catch (TCLockUpgradeNotSupportedError e) {
243: flag.set();
244: }
245: }
246: };
247: t.start();
248:
249: sleep(5);
250: assertTrue(flag.isSet());
251:
252: lockManager.unlock(l1, tid2);
253: lockManager.unlock(l1, tid3);
254:
255: t.join();
256:
257: Thread secondReader = new Thread() {
258: public void run() {
259: System.out.println("Read requested !");
260: LockManagerSystemTest.this .lockManager.lock(l1, tid2,
261: LockLevel.READ);
262: System.out.println("Got Read !");
263: }
264: };
265: secondReader.start();
266:
267: Thread secondWriter = new Thread() {
268: public void run() {
269: System.out.println("Write requested !");
270: LockManagerSystemTest.this .lockManager.lock(l1, tid3,
271: LockLevel.WRITE);
272: System.out.println("Got Write !");
273: }
274: };
275: secondWriter.start();
276:
277: sleep(5);
278: lockManager.unlock(l1, tid1);
279: sleep(5);
280: secondReader.join(5000);
281: assertFalse(secondReader.isAlive());
282: assertTrue(secondWriter.isAlive());
283:
284: lockManager.unlock(l1, tid2);
285: secondWriter.join(60000);
286: assertFalse(secondWriter.isAlive());
287: }
288:
289: public void testBasic() throws Exception {
290: final LockID l1 = new LockID("1");
291: final LockID l3 = new LockID("3");
292:
293: final ThreadID tid1 = new ThreadID(1);
294: final ThreadID tid2 = new ThreadID(2);
295: final ThreadID tid3 = new ThreadID(3);
296: final ThreadID tid4 = new ThreadID(4);
297:
298: // Get the lock for threadID 1
299: System.out.println("Asked for first lock");
300: lockManager.lock(l1, tid1, LockLevel.WRITE);
301:
302: System.out.println("Got first lock");
303:
304: // Try to get it again, this should pretty much be a noop as we handle recursive lock calls
305: lockManager.lock(l1, tid1, LockLevel.WRITE);
306: System.out.println("Got first lock again");
307:
308: final boolean[] done = new boolean[2];
309:
310: // try obtaining a write lock on l1 in a second thread. This should block initially since a write lock is already
311: // held on l1
312: Thread t = new Thread() {
313: public void run() {
314: System.out.println("Asked for second lock");
315: lockManager.lock(l1, tid2, LockLevel.WRITE);
316: System.out.println("Got second lock");
317: done[0] = true;
318: }
319: };
320:
321: t.start();
322: sleep(5);
323: assertFalse(done[0]);
324: lockManager.unlock(l1, tid1);
325: lockManager.unlock(l1, tid1); // should unblock thread above
326: sleep(5);
327: assertTrue(done[0]); // thread should have been unblocked and finished
328:
329: // Get a bunch of read locks on l3
330: lockManager.lock(l3, tid1, LockLevel.READ);
331: lockManager.lock(l3, tid2, LockLevel.READ);
332: lockManager.lock(l3, tid3, LockLevel.READ);
333: done[0] = false;
334: t = new Thread() {
335: public void run() {
336: System.out.println("Asking for write lock");
337: lockManager.lock(l3, tid4, LockLevel.WRITE);
338: System.out.println("Got write lock");
339: done[0] = true;
340: }
341: };
342: t.start();
343: sleep(5);
344: assertFalse(done[0]);
345:
346: lockManager.unlock(l3, tid1);
347: sleep(5);
348: assertFalse(done[0]);
349:
350: lockManager.unlock(l3, tid2);
351: sleep(5);
352: assertFalse(done[0]);
353:
354: lockManager.unlock(l3, tid3);
355: sleep(5);
356: assertTrue(done[0]);
357:
358: done[0] = false;
359: t = new Thread() {
360: public void run() {
361: System.out.println("Asking for read lock");
362: lockManager.lock(l3, tid1, LockLevel.READ);
363: System.out.println("Got read lock");
364: done[0] = true;
365: }
366: };
367: t.start();
368:
369: done[1] = false;
370: t = new Thread() {
371: public void run() {
372: System.out.println("Asking for read lock");
373: lockManager.lock(l3, tid2, LockLevel.READ);
374: System.out.println("Got read lock");
375: done[1] = true;
376: }
377: };
378:
379: t.start();
380: sleep(5);
381: assertFalse(done[0]);
382: assertFalse(done[1]);
383: lockManager.unlock(l3, tid4);
384: sleep(5);
385: assertTrue(done[0]);
386: assertTrue(done[1]);
387: lockManager.unlock(l3, tid1);
388: lockManager.unlock(l3, tid2);
389: }
390:
391: private static class ConfigOverride implements
392: L2TVSConfigurationSetupManager {
393:
394: private final L2TVSConfigurationSetupManager realConfig;
395:
396: ConfigOverride(L2TVSConfigurationSetupManager realConfig) {
397: this .realConfig = realConfig;
398: }
399:
400: public String[] allCurrentlyKnownServers() {
401: return realConfig.allCurrentlyKnownServers();
402: }
403:
404: public String[] applicationNames() {
405: return realConfig.applicationNames();
406: }
407:
408: public NewCommonL2Config commonl2Config() {
409: return realConfig.commonl2Config();
410: }
411:
412: public NewCommonL2Config commonL2ConfigFor(String name)
413: throws ConfigurationSetupException {
414: return realConfig.commonL2ConfigFor(name);
415: }
416:
417: public String describeSources() {
418: return realConfig.describeSources();
419: }
420:
421: public NewDSOApplicationConfig dsoApplicationConfigFor(
422: String applicationName) {
423: return realConfig.dsoApplicationConfigFor(applicationName);
424: }
425:
426: public NewL2DSOConfig dsoL2Config() {
427: return new L2ConfigOverride(realConfig.dsoL2Config());
428: }
429:
430: public NewL2DSOConfig dsoL2ConfigFor(String name)
431: throws ConfigurationSetupException {
432: return realConfig.dsoL2ConfigFor(name);
433: }
434:
435: public InputStream rawConfigFile() {
436: return realConfig.rawConfigFile();
437: }
438:
439: public NewSystemConfig systemConfig() {
440: return realConfig.systemConfig();
441: }
442:
443: public NewHaConfig haConfig() {
444: return realConfig.haConfig();
445: }
446:
447: public UpdateCheckConfig updateCheckConfig() {
448: return realConfig.updateCheckConfig();
449: }
450:
451: private static class L2ConfigOverride implements NewL2DSOConfig {
452:
453: private final NewL2DSOConfig config;
454:
455: public L2ConfigOverride(NewL2DSOConfig config) {
456: this .config = config;
457: }
458:
459: public void changesInItemForbidden(ConfigItem item) {
460: config.changesInItemForbidden(item);
461: }
462:
463: public void changesInItemIgnored(ConfigItem item) {
464: config.changesInItemIgnored(item);
465: }
466:
467: public IntConfigItem clientReconnectWindow() {
468: return config.clientReconnectWindow();
469: }
470:
471: public BooleanConfigItem garbageCollectionEnabled() {
472: return config.garbageCollectionEnabled();
473: }
474:
475: public IntConfigItem garbageCollectionInterval() {
476: return config.garbageCollectionInterval();
477: }
478:
479: public BooleanConfigItem garbageCollectionVerbose() {
480: return config.garbageCollectionVerbose();
481: }
482:
483: public IntConfigItem l2GroupPort() {
484: return config.l2GroupPort();
485: }
486:
487: public IntConfigItem listenPort() {
488: return new IntConfigItem() {
489: public int getInt() {
490: return 0;
491: }
492:
493: public void addListener(
494: ConfigItemListener changeListener) {
495: //
496: }
497:
498: public Object getObject() {
499: return new Integer(0);
500: }
501:
502: public void removeListener(
503: ConfigItemListener changeListener) {
504: //
505: }
506: };
507: }
508:
509: public ConfigItem persistenceMode() {
510: return config.persistenceMode();
511: }
512:
513: public XmlObject getBean() {
514: return config.getBean();
515: }
516:
517: public StringConfigItem host() {
518: return new StringConfigItem() {
519:
520: public String getString() {
521: return "localhost";
522: }
523:
524: public void addListener(
525: ConfigItemListener changeListener) {
526: //
527: }
528:
529: public Object getObject() {
530: return getString();
531: }
532:
533: public void removeListener(
534: ConfigItemListener changeListener) {
535: //
536: }
537:
538: };
539: }
540:
541: }
542:
543: }
544:
545: }
|