geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject [16/16] incubator-geode git commit: Extracting IntegrationTests from UnitTests
Date Tue, 26 Apr 2016 23:17:28 GMT
Extracting IntegrationTests from UnitTests


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/74c625ed
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/74c625ed
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/74c625ed

Branch: refs/heads/feature/GEODE-1276
Commit: 74c625edbda0a52e73e0accf7240b62e612688cd
Parents: ef0a624
Author: Kirk Lund <klund@apache.org>
Authored: Tue Apr 26 16:16:33 2016 -0700
Committer: Kirk Lund <klund@apache.org>
Committed: Tue Apr 26 16:16:33 2016 -0700

----------------------------------------------------------------------
 ...auncherLifecycleCommandsIntegrationTest.java | 155 ++++++
 .../LocatorLoadSnapshotIntegrationTest.java     | 122 +++++
 .../DeadlockDetectorIntegrationTest.java        | 299 +++++++++++
 .../ClassPathLoaderIntegrationTest.java         | 503 +++++++++++++++++++
 4 files changed, 1079 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/74c625ed/geode-assembly/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/LauncherLifecycleCommandsIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/LauncherLifecycleCommandsIntegrationTest.java
b/geode-assembly/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/LauncherLifecycleCommandsIntegrationTest.java
new file mode 100644
index 0000000..c7479bb
--- /dev/null
+++ b/geode-assembly/src/test/java/com/gemstone/gemfire/management/internal/cli/commands/LauncherLifecycleCommandsIntegrationTest.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package com.gemstone.gemfire.management.internal.cli.commands;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.internal.util.IOUtils;
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+
+/**
+ * The LauncherLifecycleCommandsJUnitTest class is a test suite of test cases testing the
contract and functionality of
+ * the lifecycle launcher GemFire shell (Gfsh) commands.
+ *
+ * @see com.gemstone.gemfire.management.internal.cli.commands.LauncherLifecycleCommands
+ * @see org.junit.Assert
+ * @see org.junit.Test
+ * @since 7.0
+ */
+@Category(IntegrationTest.class)
+public class LauncherLifecycleCommandsIntegrationTest {
+
+  private LauncherLifecycleCommands launcherCommands;
+
+  @Before
+  public void setup() {
+    launcherCommands = new LauncherLifecycleCommands();
+  }
+
+  @After
+  public void tearDown() {
+    launcherCommands = null;
+  }
+
+  @Test
+  public void testGemFireCoreClasspath() throws IOException {
+    File coreDependenciesJar = new File(LauncherLifecycleCommands.CORE_DEPENDENCIES_JAR_PATHNAME);
+
+    assertNotNull(coreDependenciesJar);
+    assertTrue(coreDependenciesJar + " is not a file", coreDependenciesJar.isFile());
+
+    Collection<String> expectedJarDependencies = Arrays.asList("antlr", "commons-io",
"commons-lang", "commons-logging",
+            "geode", "jackson-annotations", "jackson-core", "jackson-databind", "jansi",
"jline", "snappy-java",
+            "spring-core", "spring-shell", "jetty-server", "jetty-servlet", "jetty-webapp",
"jetty-util", "jetty-http",
+            "servlet-api", "jetty-io", "jetty-security", "jetty-xml"
+
+    );
+
+    assertJarFileManifestClassPath(coreDependenciesJar, expectedJarDependencies);
+  }
+
+  @Test
+  public void testReadPid() throws IOException {
+    final int expectedPid = 12345;
+
+    File pidFile = new File(getClass().getSimpleName().concat("_testReadPid.pid"));
+
+    assertTrue(pidFile.createNewFile());
+
+    pidFile.deleteOnExit();
+    writePid(pidFile, expectedPid);
+
+    final int actualPid = getLauncherLifecycleCommands().readPid(pidFile);
+
+    assertEquals(expectedPid, actualPid);
+  }
+
+  private LauncherLifecycleCommands getLauncherLifecycleCommands() {
+    return launcherCommands;
+  }
+
+  private void writePid(final File pidFile, final int pid) throws IOException {
+    final FileWriter fileWriter = new FileWriter(pidFile, false);
+    fileWriter.write(String.valueOf(pid));
+    fileWriter.write("\n");
+    fileWriter.flush();
+    IOUtils.close(fileWriter);
+  }
+
+  private void assertJarFileManifestClassPath(final File dependenciesJar,
+                                              final Collection<String> expectedJarDependencies)
throws IOException {
+    JarFile dependenciesJarFile = new JarFile(dependenciesJar);
+    Manifest manifest = dependenciesJarFile.getManifest();
+
+    assertNotNull(manifest);
+
+    Attributes attributes = manifest.getMainAttributes();
+
+    assertNotNull(attributes);
+    assertTrue(attributes.containsKey(Name.CLASS_PATH));
+
+    String[] actualJarDependencies = attributes.getValue(Name.CLASS_PATH).split(" ");
+
+    assertNotNull(actualJarDependencies);
+    assertTrue(String.format("Expected the actual number of JAR dependencies to be (%1$d);
but was (%2$d)!",
+            expectedJarDependencies.size(), actualJarDependencies.length),
+            actualJarDependencies.length >= expectedJarDependencies.size());
+    //assertTrue(Arrays.asList(actualJarDependencies).containsAll(expectedJarDependencies));
+
+    List<String> actualJarDependenciesList = new ArrayList<>(Arrays.asList(actualJarDependencies));
+    List<String> missingExpectedJarDependenciesList = new ArrayList<>(expectedJarDependencies.size());
+
+    for (String expectedJarDependency : expectedJarDependencies) {
+      boolean containsExpectedJar = false;
+
+      for (int index = 0, size = actualJarDependenciesList.size(); index < size; index++)
{
+        if (actualJarDependenciesList.get(index).toLowerCase().contains(expectedJarDependency.toLowerCase()))
{
+          actualJarDependenciesList.remove(index);
+          containsExpectedJar = true;
+          break;
+        }
+      }
+
+      if (!containsExpectedJar) {
+        missingExpectedJarDependenciesList.add(expectedJarDependency);
+      }
+    }
+
+    assertTrue(String.format(
+            "GemFire dependencies JAR file (%1$s) does not contain the expected dependencies
(%2$s) in the Manifest Class-Path attribute (%3$s)!",
+            dependenciesJar, missingExpectedJarDependenciesList, attributes.getValue(Name.CLASS_PATH)),
+            missingExpectedJarDependenciesList.isEmpty());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/74c625ed/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/LocatorLoadSnapshotIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/LocatorLoadSnapshotIntegrationTest.java
b/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/LocatorLoadSnapshotIntegrationTest.java
new file mode 100644
index 0000000..08648d9
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/LocatorLoadSnapshotIntegrationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package com.gemstone.gemfire.distributed.internal;
+
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.cache.server.ServerLoad;
+import com.gemstone.gemfire.test.junit.categories.FlakyTest;
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+
+/**
+ * Integration tests extracted from LocatorLoadSnapshotJUnitTest
+ */
+@Category(IntegrationTest.class)
+public class LocatorLoadSnapshotIntegrationTest {
+
+  /**
+   * A basic test of concurrent functionality. Starts a number of
+   * threads making requests and expects the load to be balanced between
+   * three servers.
+   * @throws InterruptedException
+   */
+  @Category(FlakyTest.class) // GEODE-613: lots of threads, async action, IntegrationTest-not-UnitTest,
thread joins, time sensitive
+  @Test
+  public void testConcurrentBalancing() throws InterruptedException {
+    int NUM_THREADS = 50;
+    final int NUM_REQUESTS = 10000;
+    int ALLOWED_THRESHOLD = 50; //We should never be off by more than
+    //the number of concurrent threads.
+
+    final LocatorLoadSnapshot sn = new LocatorLoadSnapshot();
+    final ServerLocation l1 = new ServerLocation("localhost", 1);
+    final ServerLocation l2 = new ServerLocation("localhost", 2);
+    final ServerLocation l3 = new ServerLocation("localhost", 3);
+
+    int initialLoad1 = (int) (Math.random() * (NUM_REQUESTS / 2));
+    int initialLoad2 = (int) (Math.random() * (NUM_REQUESTS / 2));
+    int initialLoad3 = (int) (Math.random() * (NUM_REQUESTS / 2));
+
+    sn.addServer(l1, new String[0], new ServerLoad(initialLoad1, 1, 0, 1));
+    sn.addServer(l2, new String[0], new ServerLoad(initialLoad2, 1, 0, 1));
+    sn.addServer(l3, new String[0], new ServerLoad(initialLoad3, 1, 0, 1));
+
+    final Map loadCounts = new HashMap();
+    loadCounts.put(l1, new AtomicInteger(initialLoad1));
+    loadCounts.put(l2, new AtomicInteger(initialLoad2));
+    loadCounts.put(l3, new AtomicInteger(initialLoad3));
+
+    Thread[] threads = new Thread[NUM_THREADS];
+//    final Object lock = new Object();
+    for(int i =0; i < NUM_THREADS; i++) {
+      threads[i] = new Thread("Thread-" + i) {
+        public void run() {
+          for(int ii = 0; ii < NUM_REQUESTS; ii++) {
+            ServerLocation location;
+//            synchronized(lock) {
+            location = sn.getServerForConnection(null, Collections.EMPTY_SET);
+//            }
+            AtomicInteger count = (AtomicInteger) loadCounts.get(location);
+            count.incrementAndGet();
+          }
+        }
+      };
+    }
+
+    for(int i =0; i < NUM_THREADS; i++) {
+      threads[i].start();
+    }
+
+    for(int i =0; i < NUM_THREADS; i++) {
+      Thread t = threads[i];
+      long ms = 30 * 1000;
+      t.join(30 * 1000);
+      if (t.isAlive()) {
+        for(int j =0; j < NUM_THREADS; j++) {
+          threads[j].interrupt();
+        }
+        fail("Thread did not terminate after " + ms + " ms: " + t);
+      }
+    }
+
+    double expectedPerServer = ( initialLoad1 + initialLoad2 + initialLoad3 +
+            NUM_REQUESTS * NUM_THREADS) / (double) loadCounts.size();
+//    for(Iterator itr = loadCounts.entrySet().iterator(); itr.hasNext(); ) {
+//      Map.Entry entry = (Entry) itr.next();
+//      ServerLocation location = (ServerLocation) entry.getKey();
+//      AI count= (AI) entry.getValue();
+//    }
+
+    for(Iterator itr = loadCounts.entrySet().iterator(); itr.hasNext(); ) {
+      Map.Entry entry = (Map.Entry) itr.next();
+      ServerLocation location = (ServerLocation) entry.getKey();
+      AtomicInteger count= (AtomicInteger) entry.getValue();
+      int difference = (int) Math.abs(count.get() - expectedPerServer);
+      assertTrue("Count " + count + " for server " + location + " is not within " + ALLOWED_THRESHOLD
+ " of " + expectedPerServer, difference < ALLOWED_THRESHOLD);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/74c625ed/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/deadlock/DeadlockDetectorIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/deadlock/DeadlockDetectorIntegrationTest.java
b/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/deadlock/DeadlockDetectorIntegrationTest.java
new file mode 100644
index 0000000..4eaf9cb
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/distributed/internal/deadlock/DeadlockDetectorIntegrationTest.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package com.gemstone.gemfire.distributed.internal.deadlock;
+
+import static org.junit.Assert.*;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+
+/**
+ * TODO: can we get rid of the Thread.sleep calls?
+ */
+@Category(IntegrationTest.class)
+public class DeadlockDetectorIntegrationTest {
+
+  private volatile Set<Thread> stuckThreads;
+
+  @Before
+  public void setUp() throws Exception {
+    stuckThreads = Collections.synchronizedSet(new HashSet<Thread>());
+  }
+
+  /**
+   * IntegrationTests are forkEvery 1 so cleanup is not necessary
+   */
+  @After
+  public void tearDown() throws Exception {
+    for (Thread thread: stuckThreads) {
+      thread.interrupt();
+    }
+
+    stuckThreads.clear();
+  }
+
+  /**
+   * must be IntegrationTest because: "we can't clean up the threads deadlocked on monitors"
+   */
+  @Test
+  public void testMonitorDeadlock() throws Exception {
+    final Object lock1 = new Object();
+    final Object lock2 = new Object();
+
+    Thread thread1 =  new Thread() {
+      public void run() {
+        stuckThreads.add(Thread.currentThread());
+
+        synchronized(lock1) {
+          Thread thread2 = new Thread() {
+            public void run() {
+              stuckThreads.add(Thread.currentThread());
+              synchronized(lock2) {
+                synchronized(lock1) {
+                  System.out.println("we won't get here");
+                }
+              }
+            }
+          };
+
+          thread2.start();
+
+          try {
+            Thread.sleep(1000);
+            synchronized(lock2) {
+              System.out.println("We won't get here");
+            }
+          } catch (InterruptedException ignore) {
+          }
+        }
+      }
+    };
+
+    thread1.start();
+
+    Thread.sleep(2000);
+
+    DeadlockDetector detector = new DeadlockDetector();
+    detector.addDependencies(DeadlockDetector.collectAllDependencies("here"));
+    LinkedList<Dependency> deadlocks = detector.findDeadlock();
+
+    System.out.println("deadlocks=" +  DeadlockDetector.prettyFormat(deadlocks));
+
+    assertEquals(4, detector.findDeadlock().size());
+  }
+
+  /**
+   * Make sure that we can detect a deadlock between two threads
+   * that are trying to acquire a two different syncs in the different orders.
+   */
+  @Test
+  public void testSyncDeadlock() throws Exception {
+    final Lock lock1 = new ReentrantLock();
+    final Lock lock2 = new ReentrantLock();
+
+    Thread thread1 =  new Thread() {
+      public void run() {
+        stuckThreads.add(Thread.currentThread());
+
+        lock1.lock();
+
+        Thread thread2 = new Thread() {
+          public void run() {
+            stuckThreads.add(Thread.currentThread());
+            lock2.lock();
+            try {
+              lock1.tryLock(10, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+              //ignore
+            }
+            lock2.unlock();
+          }
+        };
+
+        thread2.start();
+
+        try {
+          Thread.sleep(1000);
+          lock2.tryLock(10, TimeUnit.SECONDS);
+        } catch (InterruptedException ignore) {
+        }
+
+        lock1.unlock();
+      }
+    };
+
+    thread1.start();
+
+    Thread.sleep(2000);
+
+    DeadlockDetector detector = new DeadlockDetector();
+    detector.addDependencies(DeadlockDetector.collectAllDependencies("here"));
+    LinkedList<Dependency> deadlocks = detector.findDeadlock();
+
+    System.out.println("deadlocks=" +  DeadlockDetector.prettyFormat(deadlocks));
+
+    assertEquals(4, detector.findDeadlock().size());
+  }
+
+  @Ignore("Semaphore deadlock detection is not supported by the JDK")
+  @Test
+  public void testSemaphoreDeadlock() throws Exception {
+    final Semaphore lock1 = new Semaphore(1);
+    final Semaphore lock2 = new Semaphore(1);
+
+    Thread thread1 =  new Thread() {
+      public void run() {
+        stuckThreads.add(Thread.currentThread());
+
+        try {
+          lock1.acquire();
+        } catch (InterruptedException e1) {
+          e1.printStackTrace();
+        }
+
+        Thread thread2 = new Thread() {
+          public void run() {
+            stuckThreads.add(Thread.currentThread());
+            try {
+              lock2.acquire();
+              lock1.tryAcquire(10, TimeUnit.SECONDS);
+            } catch (InterruptedException ignore) {
+            }
+            lock2.release();
+          }
+        };
+
+        thread2.start();
+
+        try {
+          Thread.sleep(1000);
+          lock2.tryAcquire(10, TimeUnit.SECONDS);
+        } catch (InterruptedException ignore) {
+        }
+
+        lock1.release();
+      }
+    };
+
+    thread1.start();
+
+    Thread.sleep(2000);
+
+    DeadlockDetector detector = new DeadlockDetector();
+    detector.addDependencies(DeadlockDetector.collectAllDependencies("here"));
+    LinkedList<Dependency> deadlocks = detector.findDeadlock();
+
+    System.out.println("deadlocks=" +  DeadlockDetector.prettyFormat(deadlocks));
+
+    assertEquals(4, detector.findDeadlock().size());
+  }
+
+  @Ignore("ReadWriteLock deadlock detection is not currently supported by DeadlockDetector")
+  @Test
+  public void testReadLockDeadlock() throws Exception {
+    final ReadWriteLock lock1 = new ReentrantReadWriteLock();
+    final ReadWriteLock lock2 = new ReentrantReadWriteLock();
+
+    Thread thread1 =  new Thread() {
+      @Override
+      public void run() {
+        stuckThreads.add(Thread.currentThread());
+
+        lock1.readLock().lock();
+
+        Thread thread2 = new Thread() {
+          @Override
+          public void run() {
+            stuckThreads.add(Thread.currentThread());
+            lock2.readLock().lock();
+            try {
+              lock1.writeLock().tryLock(10, TimeUnit.SECONDS);
+            } catch (InterruptedException ignore) {
+            }
+            lock2.readLock().unlock();
+          }
+        };
+
+        thread2.start();
+
+        try {
+          Thread.sleep(1000);
+          lock2.writeLock().tryLock(10, TimeUnit.SECONDS);
+        } catch (InterruptedException ignore) {
+        }
+
+        lock1.readLock().unlock();
+      }
+    };
+
+    thread1.start();
+
+    Thread.sleep(2000);
+
+    DeadlockDetector detector = new DeadlockDetector();
+    detector.addDependencies(DeadlockDetector.collectAllDependencies("here"));
+    LinkedList<Dependency> deadlocks = detector.findDeadlock();
+
+    System.out.println("deadlocks=" +  deadlocks);
+
+    assertEquals(4, detector.findDeadlock().size());
+  }
+
+  /**
+   * A fake dependency monitor.
+   */
+  private static class MockDependencyMonitor implements DependencyMonitor {
+
+    Set<Dependency<Thread, Serializable>> blockedThreads = new HashSet<>();
+    Set<Dependency<Serializable, Thread>> held = new HashSet<>();
+
+    @Override
+    public Set<Dependency<Thread, Serializable>> getBlockedThreads(Thread[] allThreads)
{
+      return blockedThreads;
+    }
+
+    public void addDependency(String resource, Thread thread) {
+      held.add(new Dependency<>(resource, thread));
+    }
+
+    public void addDependency(Thread thread, String resource) {
+      blockedThreads.add(new Dependency<>(thread, resource));
+    }
+
+    @Override
+    public Set<Dependency<Serializable, Thread>> getHeldResources(Thread[] allThreads)
{
+      return held;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/74c625ed/geode-core/src/test/java/com/gemstone/gemfire/internal/ClassPathLoaderIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/internal/ClassPathLoaderIntegrationTest.java
b/geode-core/src/test/java/com/gemstone/gemfire/internal/ClassPathLoaderIntegrationTest.java
new file mode 100644
index 0000000..4c836bd
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/internal/ClassPathLoaderIntegrationTest.java
@@ -0,0 +1,503 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package com.gemstone.gemfire.internal;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.generic.ClassGen;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+
+import com.gemstone.gemfire.internal.ClassPathLoaderTest.BrokenClassLoader;
+import com.gemstone.gemfire.internal.ClassPathLoaderTest.NullClassLoader;
+import com.gemstone.gemfire.internal.ClassPathLoaderTest.SimpleClassLoader;
+
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+
+/**
+ * Integration tests for {@link ClassPathLoader}.
+ *
+ * Extracted from ClassPathLoaderTest.
+ */
+@Category(IntegrationTest.class)
+public class ClassPathLoaderIntegrationTest {
+
+  private static final int TEMP_FILE_BYTES_COUNT = 256;
+
+  private volatile File tempFile;
+  private volatile File tempFile2;
+
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @Before
+  public void setUp() throws Exception {
+    System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
+    System.setProperty(ClassPathLoader.EXT_LIB_DIR_PARENT_PROPERTY, this.temporaryFolder.getRoot().getAbsolutePath());
+
+    this.tempFile = this.temporaryFolder.newFile("tempFile1.tmp");
+    FileOutputStream fos = new FileOutputStream(this.tempFile);
+    fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
+    fos.close();
+
+    this.tempFile2 = this.temporaryFolder.newFile("tempFile2.tmp");
+    fos = new FileOutputStream(this.tempFile2);
+    fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
+    fos.close();
+  }
+
+  /**
+   * Verifies that <tt>getResource</tt> works with custom loader from {@link
ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourceWithCustomLoader() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithCustomLoader");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
+    URL url = dcl.getResource(resourceToGet);
+    assertNotNull(url);
+
+    InputStream is = url != null ? url.openStream() : null;
+    assertNotNull(is);
+
+    int totalBytesRead = 0;
+    byte[] input = new byte[128];
+
+    BufferedInputStream bis = new BufferedInputStream(is);
+    for (int bytesRead = bis.read(input); bytesRead > -1;) {
+      totalBytesRead += bytesRead;
+      bytesRead = bis.read(input);
+    }
+    bis.close();
+
+    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+  }
+
+  /**
+   * Verifies that <tt>getResources</tt> works with custom loader from {@link
ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourcesWithCustomLoader() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithCustomLoader");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
+    Enumeration<URL> urls = dcl.getResources(resourceToGet);
+    assertNotNull(urls);
+    assertTrue(urls.hasMoreElements());
+
+    URL url = urls.nextElement();
+    InputStream is = url != null ? url.openStream() : null;
+    assertNotNull(is);
+
+    int totalBytesRead = 0;
+    byte[] input = new byte[128];
+
+    BufferedInputStream bis = new BufferedInputStream(is);
+    for (int bytesRead = bis.read(input); bytesRead > -1;) {
+      totalBytesRead += bytesRead;
+      bytesRead = bis.read(input);
+    }
+    bis.close();
+
+    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+  }
+
+  /**
+   * Verifies that <tt>getResourceAsStream</tt> works with custom loader from
{@link ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourceAsStreamWithCustomLoader() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamWithCustomLoader");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithCustomLoader.rsc";
+    InputStream is = dcl.getResourceAsStream(resourceToGet);
+    assertNotNull(is);
+
+    int totalBytesRead = 0;
+    byte[] input = new byte[128];
+
+    BufferedInputStream bis = new BufferedInputStream(is);
+    for (int bytesRead = bis.read(input); bytesRead > -1;) {
+      totalBytesRead += bytesRead;
+      bytesRead = bis.read(input);
+    }
+    bis.close();
+
+    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+  }
+
+  /**
+   * Verifies that <tt>getResource</tt> works with TCCL from {@link ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourceWithTCCL() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithTCCL");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+
+    String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
+    assertNull(dcl.getResource(resourceToGet));
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
+      URL url = dcl.getResource(resourceToGet);
+      assertNotNull(url);
+
+      InputStream is = url.openStream();
+      assertNotNull(is);
+
+      int totalBytesRead = 0;
+      byte[] input = new byte[128];
+
+      BufferedInputStream bis = new BufferedInputStream(is);
+      for (int bytesRead = bis.read(input); bytesRead > -1;) {
+        totalBytesRead += bytesRead;
+        bytesRead = bis.read(input);
+      }
+      bis.close();
+
+      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  /**
+   * Verifies that <tt>getResources</tt> works with TCCL from {@link ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourcesWithTCCL() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithTCCL");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+
+    String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
+    Enumeration<URL> urls = dcl.getResources(resourceToGet);
+    assertNotNull(urls);
+    assertFalse(urls.hasMoreElements());
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
+      urls = dcl.getResources(resourceToGet);
+      assertNotNull(urls);
+
+      URL url = urls.nextElement();
+      InputStream is = url.openStream();
+      assertNotNull(is);
+
+      int totalBytesRead = 0;
+      byte[] input = new byte[128];
+
+      BufferedInputStream bis = new BufferedInputStream(is);
+      for (int bytesRead = bis.read(input); bytesRead > -1;) {
+        totalBytesRead += bytesRead;
+        bytesRead = bis.read(input);
+      }
+      bis.close();
+
+      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  /**
+   * Verifies that <tt>getResourceAsStream</tt> works with TCCL from {@link ClassPathLoader}.
+   */
+  @Test
+  public void testGetResourceAsStreamWithTCCL() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamWithTCCL");
+
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+
+    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithTCCL.rsc";
+    assertNull(dcl.getResourceAsStream(resourceToGet));
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      // ensure that TCCL is only CL that can find this resource
+      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
+      InputStream is = dcl.getResourceAsStream(resourceToGet);
+      assertNotNull(is);
+
+      int totalBytesRead = 0;
+      byte[] input = new byte[128];
+
+      BufferedInputStream bis = new BufferedInputStream(is);
+      for (int bytesRead = bis.read(input); bytesRead > -1;) {
+        totalBytesRead += bytesRead;
+        bytesRead = bis.read(input);
+      }
+      bis.close();
+
+      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  /**
+   * Verifies that JAR files found in the extlib directory will be correctly
+   * added to the {@link ClassPathLoader}.
+   */
+  @Test
+  public void testJarsInExtLib() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testJarsInExtLib");
+
+    File EXT_LIB_DIR = ClassPathLoader.defineEXT_LIB_DIR();
+    EXT_LIB_DIR.mkdir();
+
+    File subdir = new File(EXT_LIB_DIR, "cplju");
+    subdir.mkdir();
+
+    final ClassBuilder classBuilder = new ClassBuilder();
+
+    writeJarBytesToFile(new File(EXT_LIB_DIR, "ClassPathLoaderJUnit1.jar"),
+            classBuilder.createJarFromClassContent("com/cpljunit1/ClassPathLoaderJUnit1",
"package com.cpljunit1; public class ClassPathLoaderJUnit1 {}"));
+    writeJarBytesToFile(new File(subdir, "ClassPathLoaderJUnit2.jar"),
+            classBuilder.createJarFromClassContent("com/cpljunit2/ClassPathLoaderJUnit2",
"package com.cpljunit2; public class ClassPathLoaderJUnit2 {}"));
+
+    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
+    try {
+      classPathLoader.forName("com.cpljunit1.ClassPathLoaderJUnit1");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    try {
+      classPathLoader.forName("com.cpljunit2.ClassPathLoaderJUnit2");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    assertNotNull(classPathLoader.getResource("com/cpljunit2/ClassPathLoaderJUnit2.class"));
+
+    Enumeration<URL> urls = classPathLoader.getResources("com/cpljunit1");
+    if  (!urls.hasMoreElements()) {
+      fail("Resources should return one element");
+    }
+  }
+
+  /**
+   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and
TCCL is broken. This verifies
+   * that all custom loaders are checked and that the custom loaders are all checked before
TCCL.
+   */
+  @Test
+  public void testGetResourceAsStreamWithMultipleCustomLoaders() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamWithMultipleCustomLoaders");
+
+    // create DCL such that the 3rd loader should find the resource
+    // first custom loader becomes parent which won't find anything
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
+    dcl = dcl.addOrReplace(new NullClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithMultipleCustomLoaders.rsc";
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      // set TCCL to throw errors which makes sure we find before checking TCCL
+      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
+
+      InputStream is = dcl.getResourceAsStream(resourceToGet);
+      assertNotNull(is);
+      is.close();
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  /**
+   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and
TCCL is broken. This verifies
+   * that all custom loaders are checked and that the custom loaders are all checked before
TCCL.
+   */
+  @Test
+  public void testGetResourceWithMultipleCustomLoaders() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithMultipleCustomLoaders");
+
+    // create DCL such that the 3rd loader should find the resource
+    // first custom loader becomes parent which won't find anything
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
+    dcl = dcl.addOrReplace(new NullClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      // set TCCL to throw errors which makes sure we find before checking TCCL
+      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
+
+      URL url = dcl.getResource(resourceToGet);
+      assertNotNull(url);
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  /**
+   * Verifies that the 3rd custom loader will get the resources. Parent cannot find it and
TCCL is broken. This verifies
+   * that all custom loaders are checked and that the custom loaders are all checked before
TCCL.
+   */
+  @Test
+  public void testGetResourcesWithMultipleCustomLoaders() throws Exception {
+    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithMultipleCustomLoaders");
+
+    // create DCL such that the 3rd loader should find the resource
+    // first custom loader becomes parent which won't find anything
+    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
+    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    dcl = dcl.addOrReplace(new GeneratingClassLoader2());
+    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
+    dcl = dcl.addOrReplace(new NullClassLoader());
+
+    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
+
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    try {
+      // set TCCL to throw errors which makes sure we find before checking TCCL
+      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
+
+      Enumeration<URL> urls = dcl.getResources(resourceToGet);
+      assertNotNull(urls);
+      assertTrue(urls.hasMoreElements());
+
+      URL url = urls.nextElement();
+      assertNotNull(url);
+
+      // Should find two with unique URLs
+      assertTrue("Did not find all resources.", urls.hasMoreElements());
+      URL url2 = urls.nextElement();
+      assertNotNull(url2);
+      assertTrue("Resource URLs should be unique.", !url.equals(url2));
+
+    } finally {
+      Thread.currentThread().setContextClassLoader(cl);
+    }
+  }
+
+  private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
+    final OutputStream outStream = new FileOutputStream(jarFile);
+    outStream.write(jarBytes);
+    outStream.close();
+  }
+
+  /**
+   * Custom class loader which uses BCEL to always dynamically generate a class for any class
name it tries to load.
+   */
+  private class GeneratingClassLoader extends ClassLoader {
+
+    /**
+     * Currently unused but potentially useful for some future test. This causes this loader
to only generate a class
+     * that the parent could not find.
+     *
+     * @param parent
+     *          the parent class loader to check with first
+     */
+    @SuppressWarnings("unused")
+    public GeneratingClassLoader(ClassLoader parent) {
+      super(parent);
+    }
+
+    /**
+     * Specifies no parent to ensure that this loader generates the named class.
+     */
+    public GeneratingClassLoader() {
+      super(null); // no parent!!
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+      ClassGen cg = new ClassGen(name, "java.lang.Object", "<generated>", Constants.ACC_PUBLIC
| Constants.ACC_SUPER, null);
+      cg.addEmptyConstructor(Constants.ACC_PUBLIC);
+      JavaClass jClazz = cg.getJavaClass();
+      byte[] bytes = jClazz.getBytes();
+      return defineClass(jClazz.getClassName(), bytes, 0, bytes.length);
+    }
+
+    @Override
+    protected URL findResource(String name) {
+      URL url = null;
+      try {
+        url = getTempFile().getAbsoluteFile().toURI().toURL();
+        System.out.println("GeneratingClassLoader#findResource returning " + url);
+      } catch (IOException e) {
+      }
+      return url;
+    }
+
+    @Override
+    protected Enumeration<URL> findResources(String name) throws IOException {
+      URL url = null;
+      try {
+        url = getTempFile().getAbsoluteFile().toURI().toURL();
+        System.out.println("GeneratingClassLoader#findResources returning " + url);
+      } catch (IOException e) {
+      }
+      Vector<URL> urls = new Vector<URL>();
+      urls.add(url);
+      return urls.elements();
+    }
+
+    protected File getTempFile() {
+      return tempFile;
+    }
+  }
+
+  /**
+   * Custom class loader which uses BCEL to always dynamically generate a class for any class
name it tries to load.
+   */
+  private class GeneratingClassLoader2 extends GeneratingClassLoader {
+    @Override
+    protected File getTempFile() {
+      return tempFile2;
+    }
+  }
+}



Mime
View raw message