LockManagerSystemTest.java :  » Net » Terracotta » com » tctest » Java Open Source

Java Open Source » Net » Terracotta 
Terracotta » com » tctest » LockManagerSystemTest.java
/*
 * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
 * notice. All rights reserved.
 */
package com.tctest;

import org.apache.xmlbeans.XmlObject;

import com.tc.cluster.Cluster;
import com.tc.config.schema.NewCommonL2Config;
import com.tc.config.schema.NewHaConfig;
import com.tc.config.schema.NewSystemConfig;
import com.tc.config.schema.UpdateCheckConfig;
import com.tc.config.schema.dynamic.BooleanConfigItem;
import com.tc.config.schema.dynamic.ConfigItem;
import com.tc.config.schema.dynamic.ConfigItemListener;
import com.tc.config.schema.dynamic.IntConfigItem;
import com.tc.config.schema.dynamic.StringConfigItem;
import com.tc.config.schema.setup.ConfigurationSetupException;
import com.tc.config.schema.setup.L1TVSConfigurationSetupManager;
import com.tc.config.schema.setup.L2TVSConfigurationSetupManager;
import com.tc.config.schema.setup.TestTVSConfigurationSetupManagerFactory;
import com.tc.exception.TCLockUpgradeNotSupportedError;
import com.tc.lang.StartupHelper;
import com.tc.lang.TCThreadGroup;
import com.tc.lang.ThrowableHandler;
import com.tc.lang.StartupHelper.StartupAction;
import com.tc.logging.TCLogging;
import com.tc.net.protocol.transport.NullConnectionPolicy;
import com.tc.object.BaseDSOTestCase;
import com.tc.object.DistributedObjectClient;
import com.tc.object.bytecode.MockClassProvider;
import com.tc.object.bytecode.NullManager;
import com.tc.object.bytecode.hook.impl.PreparedComponentsFromL2Connection;
import com.tc.object.config.DSOClientConfigHelper;
import com.tc.object.config.StandardDSOClientConfigHelperImpl;
import com.tc.object.config.schema.NewDSOApplicationConfig;
import com.tc.object.config.schema.NewL2DSOConfig;
import com.tc.object.lockmanager.api.LockID;
import com.tc.object.lockmanager.api.LockLevel;
import com.tc.object.lockmanager.api.ThreadID;
import com.tc.object.lockmanager.impl.ClientLockManagerImpl;
import com.tc.objectserver.impl.DistributedObjectServer;
import com.tc.objectserver.lockmanager.api.DeadlockChain;
import com.tc.objectserver.lockmanager.api.DeadlockResults;
import com.tc.objectserver.managedobject.ManagedObjectStateFactory;
import com.tc.properties.TCPropertiesImpl;
import com.tc.server.NullTCServerInfo;
import com.tc.util.concurrent.SetOnceFlag;
import com.tc.util.concurrent.ThreadUtil;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class LockManagerSystemTest extends BaseDSOTestCase {

  // please keep this set to true so that tests on slow/loaded machines don't fail. When working on this test though, it
  // can be convenient to temporarily flip it to false
  private static final boolean    slow  = true;

  private DistributedObjectServer server;
  private DistributedObjectClient client;
  private ClientLockManagerImpl   lockManager;
  private TCThreadGroup           group = new TCThreadGroup(new ThrowableHandler(TCLogging
                                            .getLogger(DistributedObjectServer.class)));

  static {
    /*
     * This test run against JDK1.4 and creates and executes multiple embedded DistributedObjectServers. L2Management
     * creates an RMI registry on the JMX port, which is randomly changing in this test. In 1.4 it's not possible to
     * create multiple RMI Registries in a single VM. Remove this when we no longer support 1.4.
     */
    System.setProperty("org.terracotta.server.disableJmxConnector", "true");
    /*
     * disable OOO temporary because:
     * It keeps starting and stopping different client/server within the same process 
     * and cleaning up the environement etc. Since the shutdown methods are poorly supported 
     * as of now Sometimes the clients are still trying to reconnect to non-exisitent servers 
     * and with OOO it seems to happen more.
     */
    TCPropertiesImpl.setProperty("l1.reconnect.enabled", "false");
  }

  private class StartAction implements StartupAction {
    private final L2TVSConfigurationSetupManager l2Manager;

    StartAction(L2TVSConfigurationSetupManager l2manager) {
      this.l2Manager = l2manager;
    }

    public void execute() throws Throwable {
      server = new DistributedObjectServer(new ConfigOverride(l2Manager), group, new NullConnectionPolicy(),
                                           new NullTCServerInfo());
      server.start();
    }
  }

  public void setUp() throws Exception {
    TestTVSConfigurationSetupManagerFactory factory = createDistributedConfigFactory();

    ManagedObjectStateFactory.disableSingleton(true);
    L2TVSConfigurationSetupManager l2Manager = factory.createL2TVSConfigurationSetupManager(null);

    new StartupHelper(group, new StartAction(l2Manager)).startUp();

    factory.addServerToL1Config(null, server.getListenPort(), -1);

    L1TVSConfigurationSetupManager manager = factory.createL1TVSConfigurationSetupManager();
    DSOClientConfigHelper configHelper = new StandardDSOClientConfigHelperImpl(manager);

    PreparedComponentsFromL2Connection components = new PreparedComponentsFromL2Connection(manager);

    client = new DistributedObjectClient(configHelper, new TCThreadGroup(new ThrowableHandler(TCLogging
        .getLogger(DistributedObjectClient.class))), new MockClassProvider(), components, NullManager.getInstance(),
                                         new Cluster());
    client.start();

    lockManager = (ClientLockManagerImpl) client.getLockManager();
  }

  protected void tearDown() {
    if (client != null) {
      try {
        // client created by NullManager, the stop() is an empty call.
        // Locate CommunicationManager to do real shutdown.
        // client.stop();
        client.getCommunicationsManager().shutdown();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    ThreadUtil.reallySleep(3000);
    if (server != null) {
      try {
        server.stop();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public void disableTestUpgradeDeadlock() throws Exception {
    final LockID l1 = new LockID("1");

    final ThreadID tid1 = new ThreadID(1);
    final ThreadID tid2 = new ThreadID(2);

    lockManager.lock(l1, tid1, LockLevel.READ);
    lockManager.lock(l1, tid2, LockLevel.READ);

    Thread t1 = new Thread() {
      public void run() {
        LockManagerSystemTest.this.lockManager.lock(l1, tid1, LockLevel.WRITE);
      }
    };

    Thread t2 = new Thread() {
      public void run() {
        LockManagerSystemTest.this.lockManager.lock(l1, tid2, LockLevel.WRITE);
      }
    };

    t1.start();
    t2.start();

    t1.join(2000);
    t2.join(2000);

    assertTrue(t1.isAlive());
    assertTrue(t2.isAlive());

    // make sure we "see" the deadlock on the server side
    final List deadlocks = new ArrayList();
    DeadlockResults results = new DeadlockResults() {
      public void foundDeadlock(DeadlockChain chain) {
        deadlocks.add(chain);
      }
    };

    server.getContext().getLockManager().scanForDeadlocks(results);

    assertEquals(1, deadlocks.size());
    DeadlockChain chain = (DeadlockChain) deadlocks.remove(0);

    ThreadID id1 = chain.getWaiter().getClientThreadID();
    LockID lid1 = chain.getWaitingOn();
    chain = chain.getNextLink();
    ThreadID id2 = chain.getWaiter().getClientThreadID();
    LockID lid2 = chain.getWaitingOn();

    assertEquals(id1, chain.getNextLink().getWaiter().getClientThreadID());

    assertEquals(lid1, l1);
    assertEquals(lid2, l1);
    assertEquals(lid1, lid2);

    if (id1.equals(tid1)) {
      assertEquals(id2, tid2);
    } else {
      assertEquals(id1, tid2);
    }

  }

  private static void sleep(long amount) {
    amount *= (slow ? 300 : 50);
    ThreadUtil.reallySleep(amount);
  }

  public void testUpgradeNotSupported() throws Exception {
    final LockID l1 = new LockID("1");

    final ThreadID tid1 = new ThreadID(1);
    final ThreadID tid2 = new ThreadID(2);
    final ThreadID tid3 = new ThreadID(3);

    final SetOnceFlag flag = new SetOnceFlag();
    lockManager.lock(l1, tid1, LockLevel.READ);
    lockManager.lock(l1, tid2, LockLevel.READ);
    lockManager.lock(l1, tid3, LockLevel.READ);

    Thread t = new Thread() {
      public void run() {
        try {
          LockManagerSystemTest.this.lockManager.lock(l1, tid1, LockLevel.WRITE);
          throw new AssertionError("Should have thrown a TCLockUpgradeNotSupportedError.");
        } catch (TCLockUpgradeNotSupportedError e) {
          flag.set();
        }
      }
    };
    t.start();

    sleep(5);
    assertTrue(flag.isSet());

    lockManager.unlock(l1, tid2);
    lockManager.unlock(l1, tid3);

    t.join();

    Thread secondReader = new Thread() {
      public void run() {
        System.out.println("Read requested !");
        LockManagerSystemTest.this.lockManager.lock(l1, tid2, LockLevel.READ);
        System.out.println("Got Read !");
      }
    };
    secondReader.start();

    Thread secondWriter = new Thread() {
      public void run() {
        System.out.println("Write requested !");
        LockManagerSystemTest.this.lockManager.lock(l1, tid3, LockLevel.WRITE);
        System.out.println("Got Write !");
      }
    };
    secondWriter.start();

    sleep(5);
    lockManager.unlock(l1, tid1);
    sleep(5);
    secondReader.join(5000);
    assertFalse(secondReader.isAlive());
    assertTrue(secondWriter.isAlive());

    lockManager.unlock(l1, tid2);
    secondWriter.join(60000);
    assertFalse(secondWriter.isAlive());
  }

  public void testBasic() throws Exception {
    final LockID l1 = new LockID("1");
    final LockID l3 = new LockID("3");

    final ThreadID tid1 = new ThreadID(1);
    final ThreadID tid2 = new ThreadID(2);
    final ThreadID tid3 = new ThreadID(3);
    final ThreadID tid4 = new ThreadID(4);

    // Get the lock for threadID 1
    System.out.println("Asked for first lock");
    lockManager.lock(l1, tid1, LockLevel.WRITE);

    System.out.println("Got first lock");

    // Try to get it again, this should pretty much be a noop as we handle recursive lock calls
    lockManager.lock(l1, tid1, LockLevel.WRITE);
    System.out.println("Got first lock again");

    final boolean[] done = new boolean[2];

    // try obtaining a write lock on l1 in a second thread. This should block initially since a write lock is already
    // held on l1
    Thread t = new Thread() {
      public void run() {
        System.out.println("Asked for second lock");
        lockManager.lock(l1, tid2, LockLevel.WRITE);
        System.out.println("Got second lock");
        done[0] = true;
      }
    };

    t.start();
    sleep(5);
    assertFalse(done[0]);
    lockManager.unlock(l1, tid1);
    lockManager.unlock(l1, tid1); // should unblock thread above
    sleep(5);
    assertTrue(done[0]); // thread should have been unblocked and finished

    // Get a bunch of read locks on l3
    lockManager.lock(l3, tid1, LockLevel.READ);
    lockManager.lock(l3, tid2, LockLevel.READ);
    lockManager.lock(l3, tid3, LockLevel.READ);
    done[0] = false;
    t = new Thread() {
      public void run() {
        System.out.println("Asking for write lock");
        lockManager.lock(l3, tid4, LockLevel.WRITE);
        System.out.println("Got write lock");
        done[0] = true;
      }
    };
    t.start();
    sleep(5);
    assertFalse(done[0]);

    lockManager.unlock(l3, tid1);
    sleep(5);
    assertFalse(done[0]);

    lockManager.unlock(l3, tid2);
    sleep(5);
    assertFalse(done[0]);

    lockManager.unlock(l3, tid3);
    sleep(5);
    assertTrue(done[0]);

    done[0] = false;
    t = new Thread() {
      public void run() {
        System.out.println("Asking for read lock");
        lockManager.lock(l3, tid1, LockLevel.READ);
        System.out.println("Got read lock");
        done[0] = true;
      }
    };
    t.start();

    done[1] = false;
    t = new Thread() {
      public void run() {
        System.out.println("Asking for read lock");
        lockManager.lock(l3, tid2, LockLevel.READ);
        System.out.println("Got read lock");
        done[1] = true;
      }
    };

    t.start();
    sleep(5);
    assertFalse(done[0]);
    assertFalse(done[1]);
    lockManager.unlock(l3, tid4);
    sleep(5);
    assertTrue(done[0]);
    assertTrue(done[1]);
    lockManager.unlock(l3, tid1);
    lockManager.unlock(l3, tid2);
  }

  private static class ConfigOverride implements L2TVSConfigurationSetupManager {

    private final L2TVSConfigurationSetupManager realConfig;

    ConfigOverride(L2TVSConfigurationSetupManager realConfig) {
      this.realConfig = realConfig;
    }

    public String[] allCurrentlyKnownServers() {
      return realConfig.allCurrentlyKnownServers();
    }

    public String[] applicationNames() {
      return realConfig.applicationNames();
    }

    public NewCommonL2Config commonl2Config() {
      return realConfig.commonl2Config();
    }

    public NewCommonL2Config commonL2ConfigFor(String name) throws ConfigurationSetupException {
      return realConfig.commonL2ConfigFor(name);
    }

    public String describeSources() {
      return realConfig.describeSources();
    }

    public NewDSOApplicationConfig dsoApplicationConfigFor(String applicationName) {
      return realConfig.dsoApplicationConfigFor(applicationName);
    }

    public NewL2DSOConfig dsoL2Config() {
      return new L2ConfigOverride(realConfig.dsoL2Config());
    }

    public NewL2DSOConfig dsoL2ConfigFor(String name) throws ConfigurationSetupException {
      return realConfig.dsoL2ConfigFor(name);
    }

    public InputStream rawConfigFile() {
      return realConfig.rawConfigFile();
    }

    public NewSystemConfig systemConfig() {
      return realConfig.systemConfig();
    }

    public NewHaConfig haConfig() {
      return realConfig.haConfig();
    }

    public UpdateCheckConfig updateCheckConfig() {
      return realConfig.updateCheckConfig();
    }
    
    private static class L2ConfigOverride implements NewL2DSOConfig {

      private final NewL2DSOConfig config;

      public L2ConfigOverride(NewL2DSOConfig config) {
        this.config = config;
      }

      public void changesInItemForbidden(ConfigItem item) {
        config.changesInItemForbidden(item);
      }

      public void changesInItemIgnored(ConfigItem item) {
        config.changesInItemIgnored(item);
      }

      public IntConfigItem clientReconnectWindow() {
        return config.clientReconnectWindow();
      }

      public BooleanConfigItem garbageCollectionEnabled() {
        return config.garbageCollectionEnabled();
      }

      public IntConfigItem garbageCollectionInterval() {
        return config.garbageCollectionInterval();
      }

      public BooleanConfigItem garbageCollectionVerbose() {
        return config.garbageCollectionVerbose();
      }

      public IntConfigItem l2GroupPort() {
        return config.l2GroupPort();
      }

      public IntConfigItem listenPort() {
        return new IntConfigItem() {
          public int getInt() {
            return 0;
          }

          public void addListener(ConfigItemListener changeListener) {
            //
          }

          public Object getObject() {
            return new Integer(0);
          }

          public void removeListener(ConfigItemListener changeListener) {
            //
          }
        };
      }

      public ConfigItem persistenceMode() {
        return config.persistenceMode();
      }

      public XmlObject getBean() {
        return config.getBean();
      }

      public StringConfigItem host() {
        return new StringConfigItem() {

          public String getString() {
            return "localhost";
          }

          public void addListener(ConfigItemListener changeListener) {
            //
          }

          public Object getObject() {
            return getString();
          }

          public void removeListener(ConfigItemListener changeListener) {
            //
          }

        };
      }

    }

  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.