bookkeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zhai...@apache.org
Subject [bookkeeper] branch master updated: ISSUE #466: Move backward compatibility tests into a separate module
Date Sun, 27 Aug 2017 00:54:53 GMT
This is an automated email from the ASF dual-hosted git repository.

zhaijia pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new 160e975  ISSUE #466: Move backward compatibility tests into a separate module
160e975 is described below

commit 160e975a98abfa18a8de9423137763e4a1125b47
Author: Sijie Guo <sijie@apache.org>
AuthorDate: Sun Aug 27 08:54:41 2017 +0800

    ISSUE #466: Move backward compatibility tests into a separate module
    
    Descriptions of the changes in this PR:
    
    - move backward compatibility test related stuffs to a separate module `test/backward`.
    - fix checkstyle for backward compatibility related tests
    - other pom file changes: move common dependencies to root pom, use variables
    - for bookkeeper-server module, produce a non-shaded jar and a shaded jar. so applications can choose what jar to use.
    
    Author: Sijie Guo <sijie@apache.org>
    
    Reviewers: Jia Zhai <None>
    
    This closes #467 from sijie/integration_tests, closes #466
---
 bookkeeper-benchmark/pom.xml                       |  39 ---
 bookkeeper-http/http-server/pom.xml                |   5 -
 bookkeeper-http/pom.xml                            |   2 +-
 bookkeeper-http/twitter-http-server/pom.xml        |   5 -
 bookkeeper-http/vertx-http-server/pom.xml          |   7 +-
 bookkeeper-server/pom.xml                          |  68 +---
 .../org/apache/bookkeeper/client/BookKeeper.java   |   3 +-
 .../bookkeeper/client/BookKeeperTestClient.java    |   5 +
 .../bookkeeper/client/BookieRecoveryTest.java      | 123 -------
 bookkeeper-stats/pom.xml                           |   5 -
 pom.xml                                            |  38 ++-
 {bookkeeper-benchmark => tests/backward}/pom.xml   | 246 ++++++--------
 .../tests/backward}/TestBackwardCompat.java        | 115 +++----
 .../tests/backward/TestBookieRecovery.java         | 364 +++++++++++++++++++++
 .../bookkeeper-server-compat-4.0.0/pom.xml         |  14 +-
 .../bookkeeper-server-compat-4.1.0/pom.xml         |  16 +-
 .../bookkeeper-server-compat-4.2.0/pom.xml         |  16 +-
 {compat-deps => tests}/pom.xml                     |  35 +-
 18 files changed, 602 insertions(+), 504 deletions(-)

diff --git a/bookkeeper-benchmark/pom.xml b/bookkeeper-benchmark/pom.xml
index 2f5c600..af3499b 100644
--- a/bookkeeper-benchmark/pom.xml
+++ b/bookkeeper-benchmark/pom.xml
@@ -85,11 +85,6 @@
   <dependencies>
     <dependency>
       <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
       <version>${slf4j.version}</version>
     </dependency>
@@ -151,40 +146,6 @@
       <scope>test</scope>
       <type>test-jar</type>
     </dependency>
-    <!--
-	Annoying dependency we need to include because
-	zookeeper uses log4j and so we transatively do, but
-	log4j has some dependencies which aren't in the 
-	default maven repositories
-    //-->
-    <dependency>
-      <groupId>log4j</groupId>
-      <artifactId>log4j</artifactId>
-      <version>1.2.15</version>
-      <exclusions>
-	<exclusion>
-	  <groupId>javax.mail</groupId>
-	  <artifactId>mail</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>javax.jms</groupId>
-	  <artifactId>jms</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>com.sun.jdmk</groupId>
-	  <artifactId>jmxtools</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>com.sun.jmx</groupId>
-	  <artifactId>jmxri</artifactId>
-	</exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>commons-cli</groupId>
-      <artifactId>commons-cli</artifactId>
-      <version>1.2</version>
-    </dependency>
     <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
diff --git a/bookkeeper-http/http-server/pom.xml b/bookkeeper-http/http-server/pom.xml
index 2825d37..194bd4a 100644
--- a/bookkeeper-http/http-server/pom.xml
+++ b/bookkeeper-http/http-server/pom.xml
@@ -77,11 +77,6 @@
             <version>1.6</version>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <version>${slf4j.version}</version>
-        </dependency>
-        <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-core</artifactId>
             <version>2.8.9</version>
diff --git a/bookkeeper-http/pom.xml b/bookkeeper-http/pom.xml
index 74a13bd..5c05d88 100644
--- a/bookkeeper-http/pom.xml
+++ b/bookkeeper-http/pom.xml
@@ -27,7 +27,7 @@
     <groupId>org.apache.bookkeeper.http</groupId>
     <artifactId>bookkeeper-http</artifactId>
     <packaging>pom</packaging>
-    <name>Apache BookKeeper :: Bookkeeper-Http</name>
+    <name>Apache BookKeeper :: Bookkeeper Http</name>
     <modules>
         <module>http-server</module>
         <module>twitter-http-server</module>
diff --git a/bookkeeper-http/twitter-http-server/pom.xml b/bookkeeper-http/twitter-http-server/pom.xml
index a392cc3..db874b1 100644
--- a/bookkeeper-http/twitter-http-server/pom.xml
+++ b/bookkeeper-http/twitter-http-server/pom.xml
@@ -87,11 +87,6 @@
             <artifactId>http-server</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-        </dependency>
     </dependencies>
 
 </project>
diff --git a/bookkeeper-http/vertx-http-server/pom.xml b/bookkeeper-http/vertx-http-server/pom.xml
index dc6dd35..ea5b26d 100644
--- a/bookkeeper-http/vertx-http-server/pom.xml
+++ b/bookkeeper-http/vertx-http-server/pom.xml
@@ -98,17 +98,12 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>20.0</version>
+            <version>${guava.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.bookkeeper.http</groupId>
             <artifactId>http-server</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-        </dependency>
     </dependencies>
 </project>
diff --git a/bookkeeper-server/pom.xml b/bookkeeper-server/pom.xml
index afdcd4f..0a5429f 100644
--- a/bookkeeper-server/pom.xml
+++ b/bookkeeper-server/pom.xml
@@ -35,20 +35,15 @@
       <version>${project.parent.version}</version>
     </dependency>
     <dependency>
-      <groupId>com.google.protobuf</groupId>
-      <artifactId>protobuf-java</artifactId>
-      <version>${protobuf.version}</version>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <version>${guava.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j.version}</version>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+      <version>${protobuf.version}</version>
+      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -173,42 +168,6 @@
       </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.apache.bookkeeper</groupId>
-      <artifactId>bookkeeper-server-compat400</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>org.apache.bookkeeper</groupId>
-          <artifactId>bookkeeper-server</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.bookkeeper</groupId>
-      <artifactId>bookkeeper-server-compat410</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>org.apache.bookkeeper</groupId>
-          <artifactId>bookkeeper-server</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.bookkeeper</groupId>
-      <artifactId>bookkeeper-server-compat420</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>org.apache.bookkeeper</groupId>
-          <artifactId>bookkeeper-server</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
       <groupId>io.netty</groupId>
       <artifactId>netty-all</artifactId>
       <version>${netty.version}</version>
@@ -233,12 +192,12 @@
   <build>
     <plugins>
       <plugin>
-            <!-- for mini-kdc -->
-            <groupId>org.apache.felix</groupId>
-            <artifactId>maven-bundle-plugin</artifactId>
-            <version>3.2.0</version>
-            <inherited>true</inherited>
-            <extensions>true</extensions>
+        <!-- for mini-kdc -->
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>3.2.0</version>
+        <inherited>true</inherited>
+        <extensions>true</extensions>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
@@ -255,13 +214,16 @@
             </goals>
             <configuration>
               <createDependencyReducedPom>true</createDependencyReducedPom>
+              <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+              <shadedArtifactAttached>true</shadedArtifactAttached>
+              <shadedClassifierName>shaded</shadedClassifierName>
+              <minimizeJar>true</minimizeJar>
               <artifactSet>
                 <includes>
                   <include>com.google.protobuf:protobuf-java</include>
                   <include>com.google.guava:guava</include>
                 </includes>
               </artifactSet>
-              <minimizeJar>true</minimizeJar>
               <relocations>
                 <relocation>
                   <pattern>com.google.protobuf</pattern>
@@ -303,7 +265,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
-        <version>2.2</version>
+        <version>${maven-jar-plugin.version}</version>
         <executions>
           <execution>
             <goals>
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
index e5ccba3..8a96ebe 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java
@@ -562,7 +562,8 @@ public class BookKeeper implements AutoCloseable {
         }
     }
 
-    LedgerManager getLedgerManager() {
+    @VisibleForTesting
+    public LedgerManager getLedgerManager() {
         return ledgerManager;
     }
 
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperTestClient.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperTestClient.java
index 3fc86ad..5401fef 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperTestClient.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperTestClient.java
@@ -24,6 +24,7 @@ package org.apache.bookkeeper.client;
 import java.io.IOException;
 
 import org.apache.bookkeeper.conf.ClientConfiguration;
+import org.apache.bookkeeper.proto.BookieClient;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooKeeper;
 
@@ -45,6 +46,10 @@ public class BookKeeperTestClient extends BookKeeper {
         return super.getConf();
     }
 
+    public BookieClient getBookieClient() {
+        return bookieClient;
+    }
+
     /**
      * Force a read to zookeeper to get list of bookies.
      *
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookieRecoveryTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookieRecoveryTest.java
index c707c46..6718860 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookieRecoveryTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookieRecoveryTest.java
@@ -895,127 +895,4 @@ public class BookieRecoveryTest extends BookKeeperClusterTestCase {
         lh.close();
     }
 
-    /**
-     * Test that when we try to recover a ledger which doesn't have
-     * the password stored in the configuration, we don't succeed
-     */
-    @Test(timeout=60000)
-    public void ensurePasswordUsedForOldLedgers() throws Exception {
-        // This test bases on creating old ledgers in version 4.1.0, which only
-        // supports ZooKeeper based flat and hierarchical LedgerManagerFactory.
-        // So we ignore it for MSLedgerManagerFactory and LongHierarchicalLedgerManagerFactory.
-        if (MSLedgerManagerFactory.class.getName().equals(ledgerManagerFactory)) {
-            return;
-        }
-        if (LongHierarchicalLedgerManagerFactory.class.getName().equals(ledgerManagerFactory)) {
-            return;
-        }
-
-        // stop all bookies
-        // and wipe the ledger layout so we can use an old client
-        zkUtil.getZooKeeperClient().delete("/ledgers/LAYOUT", -1);
-
-        byte[] passwdCorrect = "AAAAAA".getBytes();
-        byte[] passwdBad = "BBBBBB".getBytes();
-        DigestType digestCorrect = digestType;
-        DigestType digestBad = digestCorrect == DigestType.MAC ? DigestType.CRC32 : DigestType.MAC;
-
-        org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType digestCorrect410
-            = org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType.valueOf(digestType.toString());
-
-        org.apache.bk_v4_1_0.bookkeeper.conf.ClientConfiguration c
-            = new org.apache.bk_v4_1_0.bookkeeper.conf.ClientConfiguration();
-        c.setZkServers(zkUtil.getZooKeeperConnectString())
-            .setLedgerManagerType(
-                    ledgerManagerFactory.equals("org.apache.bookkeeper.meta.FlatLedgerManagerFactory") ?
-                    "flat" : "hierarchical");
-
-        // create client to set up layout, close it, restart bookies, and open a new client.
-        // the new client is necessary to ensure that it has all the restarted bookies in the
-        // its available bookie list
-        org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper bkc41
-            = new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(c);
-        bkc41.close();
-        restartBookies();
-        bkc41 = new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(c);
-
-        org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle lh41
-            = bkc41.createLedger(3, 2, digestCorrect410, passwdCorrect);
-        long ledgerId = lh41.getId();
-        for (int i = 0; i < 100; i++) {
-            lh41.addEntry("foobar".getBytes());
-        }
-        lh41.close();
-        bkc41.close();
-
-        // Startup a new bookie server
-        startNewBookie();
-        int removeIndex = 0;
-        BookieSocketAddress bookieSrc = bs.get(removeIndex).getLocalAddress();
-        bs.get(removeIndex).shutdown();
-        bs.remove(removeIndex);
-
-        // Check that entries are missing
-        LedgerHandle lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
-        assertFalse("Should be entries missing", verifyFullyReplicated(lh, 100));
-        lh.close();
-
-        // Try to recover with bad password in conf
-        // if the digest type is MAC
-        // for CRC32, the password is only checked
-        // when adding new entries, which recovery will
-        // never do
-        ClientConfiguration adminConf;
-        BookKeeperAdmin bka;
-        if (digestCorrect == DigestType.MAC) {
-            adminConf = new ClientConfiguration();
-            adminConf.setZkServers(zkUtil.getZooKeeperConnectString());
-            adminConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
-            adminConf.setBookieRecoveryDigestType(digestCorrect);
-            adminConf.setBookieRecoveryPasswd(passwdBad);
-
-            bka = new BookKeeperAdmin(adminConf);
-            try {
-                bka.recoverBookieData(bookieSrc, null);
-                fail("Shouldn't be able to recover with wrong password");
-            } catch (BKException bke) {
-                // correct behaviour
-            } finally {
-                bka.close();
-            }
-        }
-
-        // Try to recover with bad digest in conf
-        adminConf = new ClientConfiguration();
-        adminConf.setZkServers(zkUtil.getZooKeeperConnectString());
-        adminConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
-        adminConf.setBookieRecoveryDigestType(digestBad);
-        adminConf.setBookieRecoveryPasswd(passwdCorrect);
-
-        bka = new BookKeeperAdmin(adminConf);
-        try {
-            bka.recoverBookieData(bookieSrc, null);
-            fail("Shouldn't be able to recover with wrong digest");
-        } catch (BKException bke) {
-            // correct behaviour
-        } finally {
-            bka.close();
-        }
-
-        // Check that entries are still missing
-        lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
-        assertFalse("Should be entries missing", verifyFullyReplicated(lh, 100));
-        lh.close();
-
-        adminConf.setBookieRecoveryDigestType(digestCorrect);
-        adminConf.setBookieRecoveryPasswd(passwdCorrect);
-
-        bka = new BookKeeperAdmin(adminConf);
-        bka.recoverBookieData(bookieSrc, null);
-        bka.close();
-
-        lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
-        assertTrue("Should have recovered everything", verifyFullyReplicated(lh, 100));
-        lh.close();
-    }
 }
diff --git a/bookkeeper-stats/pom.xml b/bookkeeper-stats/pom.xml
index 2e78f6f..30c1632 100644
--- a/bookkeeper-stats/pom.xml
+++ b/bookkeeper-stats/pom.xml
@@ -97,10 +97,5 @@
       <artifactId>commons-configuration</artifactId>
       <version>1.6</version>
     </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j.version}</version>
-    </dependency>
   </dependencies>
 </project>
diff --git a/pom.xml b/pom.xml
index 8867b54..c85e981 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,12 +52,12 @@
   </ciManagement>
   <modules>
     <module>buildtools</module>
-    <module>compat-deps</module>
     <module>bookkeeper-stats</module>
     <module>bookkeeper-server</module>
     <module>bookkeeper-benchmark</module>
     <module>bookkeeper-stats-providers</module>
     <module>bookkeeper-http</module>
+    <module>tests</module>
   </modules>
   <mailingLists>
     <mailingList>
@@ -107,12 +107,23 @@
     <findbugs-maven-plugin.version>3.0.4</findbugs-maven-plugin.version>
     <puppycrawl.checkstyle.version>6.19</puppycrawl.checkstyle.version>
     <maven-checkstyle-plugin.version>2.17</maven-checkstyle-plugin.version>
+    <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>
+    <maven-deploy-plugin.version>2.7</maven-deploy-plugin.version>
+    <maven-jar-plugin.version>2.2</maven-jar-plugin.version>
     <maven-javadoc-plugin.version>2.10.4</maven-javadoc-plugin.version>
     <maven-source-plugin.version>2.2.1</maven-source-plugin.version>
+    <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
   </properties>
 
   <!-- dependencies for all modules -->
   <dependencies>
+    <!-- compilation dependencies -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+    <!-- test dependencies -->
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
@@ -126,6 +137,12 @@
       <version>${hamcrest.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -157,7 +174,7 @@
       </plugin>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.5.1</version>
+        <version>${maven-compiler-plugin.version}</version>
         <configuration>
           <source>1.8</source>
           <target>1.8</target>
@@ -171,7 +188,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>2.19.1</version>
+        <version>${maven-surefire-plugin.version}</version>
         <configuration>
           <!-- @{argLine} is a variable injected by JaCoCo-->
           <argLine>@{argLine} -Xmx2G -Djava.net.preferIPv4Stack=true</argLine>
@@ -259,6 +276,7 @@
             <exclude>**/.gitignore</exclude>
             <exclude>**/.project</exclude>
             <exclude>**/.settings/*</exclude>
+            <exclude>**/*.iml</exclude>
             <exclude>site/**</exclude>
             <exclude>.repository/**</exclude>
           </excludes>
@@ -320,19 +338,5 @@
         <skipTests>true</skipTests>
       </properties>
     </profile>
-    <profile>
-      <id>consoleTestOutput</id>
-      <build>
-	<plugins>
-	  <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-surefire-plugin</artifactId>
-            <configuration>
-	      <redirectTestOutputToFile>true</redirectTestOutputToFile>
-            </configuration>
-	  </plugin>
-	</plugins>
-      </build>
-    </profile>
   </profiles>
 </project>
diff --git a/bookkeeper-benchmark/pom.xml b/tests/backward/pom.xml
similarity index 50%
copy from bookkeeper-benchmark/pom.xml
copy to tests/backward/pom.xml
index 2f5c600..8a3dcd0 100644
--- a/bookkeeper-benchmark/pom.xml
+++ b/tests/backward/pom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,98 +15,71 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>bookkeeper</artifactId>
-    <groupId>org.apache.bookkeeper</groupId>
+    <groupId>org.apache.bookkeeper.tests</groupId>
+    <artifactId>tests-parent</artifactId>
     <version>4.6.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
   </parent>
-  <groupId>org.apache.bookkeeper</groupId>
-  <artifactId>bookkeeper-benchmark</artifactId>
-  <name>Apache BookKeeper :: Benchmark</name>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-  </properties>
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <version>2.2.1</version>
-        <configuration>
-	  <skipAssembly>true</skipAssembly>
-        </configuration>
-      </plugin>
-      <plugin>
-      	<groupId>org.apache.maven.plugins</groupId>
-	<artifactId>maven-surefire-plugin</artifactId>
-	<configuration>
-          <systemPropertyVariables>
-            <test.latency.file>target/latencyDump.dat</test.latency.file>
-          </systemPropertyVariables>
-	</configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <version>${maven-checkstyle-plugin.version}</version>
-        <dependencies>
-          <dependency>
-            <groupId>com.puppycrawl.tools</groupId>
-            <artifactId>checkstyle</artifactId>
-            <version>${puppycrawl.checkstyle.version}</version>
-          </dependency>
-          <dependency>
-            <groupId>org.apache.bookkeeper</groupId>
-            <artifactId>buildtools</artifactId>
-            <version>${project.parent.version}</version>
-          </dependency>
-        </dependencies>
-        <configuration>
-          <configLocation>bookkeeper/checkstyle.xml</configLocation>
-          <suppressionsLocation>bookkeeper/suppressions.xml</suppressionsLocation>
-          <encoding>UTF-8</encoding>
-          <consoleOutput>true</consoleOutput>
-          <failsOnError>true</failsOnError>
-        </configuration>
-        <executions>
-          <execution>
-            <id>checkstyle</id>
-            <phase>validate</phase>
-            <goals>
-              <goal>check</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
+  <artifactId>backward-test</artifactId>
+  <name>Apache BookKeeper :: Tests :: Backward</name>
   <dependencies>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j.version}</version>
+      <groupId>org.apache.bookkeeper.tests</groupId>
+      <artifactId>bookkeeper-server-compat400</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.bookkeeper</groupId>
+          <artifactId>bookkeeper-server</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.google.protobuf</groupId>
+          <artifactId>protobuf-java</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-log4j12</artifactId>
-      <version>${slf4j.version}</version>
+      <groupId>org.apache.bookkeeper.tests</groupId>
+      <artifactId>bookkeeper-server-compat410</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.bookkeeper</groupId>
+          <artifactId>bookkeeper-server</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
-      <groupId>org.apache.zookeeper</groupId>
-      <artifactId>zookeeper</artifactId>
-      <version>${zookeeper.version}</version>
-      <type>jar</type>
-      <scope>compile</scope>
+      <groupId>org.apache.bookkeeper.tests</groupId>
+      <artifactId>bookkeeper-server-compat420</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
       <exclusions>
         <exclusion>
-          <groupId>net.java.dev.javacc</groupId>
-          <artifactId>javacc</artifactId>
+          <groupId>org.apache.bookkeeper</groupId>
+          <artifactId>bookkeeper-server</artifactId>
         </exclusion>
       </exclusions>
     </dependency>
     <dependency>
+      <groupId>org.apache.bookkeeper</groupId>
+      <artifactId>bookkeeper-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.bookkeeper</groupId>
+      <artifactId>bookkeeper-server</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.zookeeper</groupId>
       <artifactId>zookeeper</artifactId>
       <version>${zookeeper.version}</version>
@@ -131,79 +104,50 @@
         </exclusion>
       </exclusions>
     </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-all</artifactId>
-      <version>${netty.version}</version>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.bookkeeper</groupId>
-      <artifactId>bookkeeper-server</artifactId>
-      <version>${project.parent.version}</version>
-      <scope>compile</scope>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.bookkeeper</groupId>
-      <artifactId>bookkeeper-server</artifactId>
-      <version>${project.parent.version}</version>
-      <scope>test</scope>
-      <type>test-jar</type>
-    </dependency>
-    <!--
-	Annoying dependency we need to include because
-	zookeeper uses log4j and so we transatively do, but
-	log4j has some dependencies which aren't in the 
-	default maven repositories
-    //-->
-    <dependency>
-      <groupId>log4j</groupId>
-      <artifactId>log4j</artifactId>
-      <version>1.2.15</version>
-      <exclusions>
-	<exclusion>
-	  <groupId>javax.mail</groupId>
-	  <artifactId>mail</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>javax.jms</groupId>
-	  <artifactId>jms</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>com.sun.jdmk</groupId>
-	  <artifactId>jmxtools</artifactId>
-	</exclusion>
-	<exclusion>
-	  <groupId>com.sun.jmx</groupId>
-	  <artifactId>jmxri</artifactId>
-	</exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>commons-cli</groupId>
-      <artifactId>commons-cli</artifactId>
-      <version>1.2</version>
-    </dependency>
-    <dependency>
-       <groupId>org.apache.hadoop</groupId>
-       <artifactId>hadoop-common</artifactId>
-       <version>0.23.1</version>
-       <scope>compile</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.hadoop</groupId>
-      <artifactId>hadoop-hdfs</artifactId>
-      <version>0.23.1</version>
-      <scope>compile</scope>
-      <!-- commons-daemon has a malformed pom, which can cause the build to fail in an
-           environment which uses an artifactory cache. -->
-      <exclusions>
-        <exclusion>
-          <groupId>commons-daemon</groupId>
-          <artifactId>commons-daemon</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
   </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>${maven-compiler-plugin.version}</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${maven-checkstyle-plugin.version}</version>
+        <dependencies>
+          <dependency>
+            <groupId>com.puppycrawl.tools</groupId>
+            <artifactId>checkstyle</artifactId>
+            <version>${puppycrawl.checkstyle.version}</version>
+          </dependency>
+          <dependency>
+            <groupId>org.apache.bookkeeper</groupId>
+            <artifactId>buildtools</artifactId>
+            <version>${project.parent.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <configLocation>bookkeeper/checkstyle.xml</configLocation>
+          <suppressionsLocation>bookkeeper/suppressions.xml</suppressionsLocation>
+          <consoleOutput>true</consoleOutput>
+          <failOnViolation>true</failOnViolation>
+          <includeResources>false</includeResources>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>test-compile</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
 </project>
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/TestBackwardCompat.java b/tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBackwardCompat.java
similarity index 90%
rename from bookkeeper-server/src/test/java/org/apache/bookkeeper/test/TestBackwardCompat.java
rename to tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBackwardCompat.java
index 951d4d8..92ce880 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/TestBackwardCompat.java
+++ b/tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBackwardCompat.java
@@ -18,41 +18,43 @@
  * under the License.
  *
  */
-package org.apache.bookkeeper.test;
+package org.apache.bookkeeper.tests.backward;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.io.IOException;
 import java.io.File;
+import java.io.IOException;
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
-import java.util.ArrayList;
 import java.util.List;
-import org.apache.commons.io.FileUtils;
 import org.apache.bookkeeper.bookie.Bookie;
 import org.apache.bookkeeper.bookie.BookieException;
 import org.apache.bookkeeper.client.BookKeeperAdmin;
 import org.apache.bookkeeper.conf.AbstractConfiguration;
 import org.apache.bookkeeper.conf.ClientConfiguration;
 import org.apache.bookkeeper.conf.TestBKConfiguration;
+import org.apache.bookkeeper.test.PortManager;
+import org.apache.bookkeeper.test.ZooKeeperUtil;
 import org.apache.bookkeeper.util.IOUtils;
+import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+/**
+ * Test backward compat between versions.
+ */
 public class TestBackwardCompat {
-    private final static Logger LOG = LoggerFactory.getLogger(TestBackwardCompat.class);
 
-    private static ZooKeeperUtil zkUtil = new ZooKeeperUtil();
-    private static byte[] ENTRY_DATA = "ThisIsAnEntry".getBytes();
+    private static final ZooKeeperUtil zkUtil = new ZooKeeperUtil();
+    private static final byte[] ENTRYDATA = "ThisIsAnEntry".getBytes();
 
     static void waitUp(int port) throws Exception {
-        while(zkUtil.getZooKeeperClient().exists(
+        while (zkUtil.getZooKeeperClient().exists(
                       "/ledgers/available/" + InetAddress.getLocalHost().getHostAddress() + ":" + port,
                       false) == null) {
             Thread.sleep(500);
@@ -82,7 +84,7 @@ public class TestBackwardCompat {
     }
 
     /**
-     * Version 4.1.0 classes
+     * Version 4.1.0 classes.
      */
     static class Server410 {
         org.apache.bk_v4_1_0.bookkeeper.conf.ServerConfiguration conf;
@@ -124,20 +126,20 @@ public class TestBackwardCompat {
         }
 
         static Ledger410 newLedger() throws Exception {
-            org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle newlh
-                = newbk.createLedger(1, 1,
+            org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle newlh =
+                    newbk.createLedger(1, 1,
                                   org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                   "foobar".getBytes());
             return new Ledger410(newbk, newlh);
         }
 
         static Ledger410 openLedger(long id) throws Exception {
-            org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle newlh
-                = newbk.openLedger(id,
+            org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle newlh =
+                    newbk.openLedger(id,
                                 org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                 "foobar".getBytes());
             return new Ledger410(newbk, newlh);
@@ -149,17 +151,17 @@ public class TestBackwardCompat {
 
         void write100() throws Exception {
             for (int i = 0; i < 100; i++) {
-                lh.addEntry(ENTRY_DATA);
+                lh.addEntry(ENTRYDATA);
             }
         }
 
         long readAll() throws Exception {
             long count = 0;
-            Enumeration<org.apache.bk_v4_1_0.bookkeeper.client.LedgerEntry> entries
-                = lh.readEntries(0, lh.getLastAddConfirmed());
+            Enumeration<org.apache.bk_v4_1_0.bookkeeper.client.LedgerEntry> entries =
+                    lh.readEntries(0, lh.getLastAddConfirmed());
             while (entries.hasMoreElements()) {
                 assertTrue("entry data doesn't match",
-                           Arrays.equals(entries.nextElement().getEntry(), ENTRY_DATA));
+                           Arrays.equals(entries.nextElement().getEntry(), ENTRYDATA));
                 count++;
             }
             return count;
@@ -179,7 +181,7 @@ public class TestBackwardCompat {
     }
 
     /**
-     * Version 4.2.0 classes
+     * Version 4.2.0 classes.
      */
     static class Server420 {
         org.apache.bk_v4_2_0.bookkeeper.conf.ServerConfiguration conf;
@@ -222,20 +224,20 @@ public class TestBackwardCompat {
         }
 
         static Ledger420 newLedger() throws Exception {
-            org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bk_v4_2_0.bookkeeper.client.LedgerHandle newlh
-                = newbk.createLedger(1, 1,
+            org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bk_v4_2_0.bookkeeper.client.LedgerHandle newlh =
+                    newbk.createLedger(1, 1,
                                   org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                   "foobar".getBytes());
             return new Ledger420(newbk, newlh);
         }
 
         static Ledger420 openLedger(long id) throws Exception {
-            org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bk_v4_2_0.bookkeeper.client.LedgerHandle newlh
-                = newbk.openLedger(id,
+            org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bk_v4_2_0.bookkeeper.client.LedgerHandle newlh =
+                    newbk.openLedger(id,
                                 org.apache.bk_v4_2_0.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                 "foobar".getBytes());
             return new Ledger420(newbk, newlh);
@@ -247,17 +249,17 @@ public class TestBackwardCompat {
 
         void write100() throws Exception {
             for (int i = 0; i < 100; i++) {
-                lh.addEntry(ENTRY_DATA);
+                lh.addEntry(ENTRYDATA);
             }
         }
 
         long readAll() throws Exception {
             long count = 0;
-            Enumeration<org.apache.bk_v4_2_0.bookkeeper.client.LedgerEntry> entries
-                = lh.readEntries(0, lh.getLastAddConfirmed());
+            Enumeration<org.apache.bk_v4_2_0.bookkeeper.client.LedgerEntry> entries =
+                    lh.readEntries(0, lh.getLastAddConfirmed());
             while (entries.hasMoreElements()) {
                 assertTrue("entry data doesn't match",
-                           Arrays.equals(entries.nextElement().getEntry(), ENTRY_DATA));
+                           Arrays.equals(entries.nextElement().getEntry(), ENTRYDATA));
                 count++;
             }
             return count;
@@ -277,7 +279,7 @@ public class TestBackwardCompat {
     }
 
     /**
-     * Current verion classes
+     * Current verion classes.
      */
     static class ServerCurrent {
         org.apache.bookkeeper.conf.ServerConfiguration conf;
@@ -322,20 +324,20 @@ public class TestBackwardCompat {
         }
 
         static LedgerCurrent newLedger() throws Exception {
-            org.apache.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bookkeeper.client.LedgerHandle newlh
-                = newbk.createLedger(1, 1,
+            org.apache.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bookkeeper.client.LedgerHandle newlh =
+                    newbk.createLedger(1, 1,
                                      org.apache.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                      "foobar".getBytes());
             return new LedgerCurrent(newbk, newlh);
         }
 
         static LedgerCurrent openLedger(long id) throws Exception {
-            org.apache.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
-            org.apache.bookkeeper.client.LedgerHandle newlh
-                = newbk.openLedger(id,
+            org.apache.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bookkeeper.client.BookKeeper(zkUtil.getZooKeeperConnectString());
+            org.apache.bookkeeper.client.LedgerHandle newlh =
+                    newbk.openLedger(id,
                                 org.apache.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                 "foobar".getBytes());
             return new LedgerCurrent(newbk, newlh);
@@ -343,10 +345,10 @@ public class TestBackwardCompat {
 
         static LedgerCurrent openLedger(long id, ClientConfiguration conf) throws Exception {
             conf.setZkServers(zkUtil.getZooKeeperConnectString());
-            org.apache.bookkeeper.client.BookKeeper newbk
-                = new org.apache.bookkeeper.client.BookKeeper(conf);
-            org.apache.bookkeeper.client.LedgerHandle newlh
-                = newbk.openLedger(id,
+            org.apache.bookkeeper.client.BookKeeper newbk =
+                    new org.apache.bookkeeper.client.BookKeeper(conf);
+            org.apache.bookkeeper.client.LedgerHandle newlh =
+                    newbk.openLedger(id,
                                    org.apache.bookkeeper.client.BookKeeper.DigestType.CRC32,
                                 "foobar".getBytes());
             return new LedgerCurrent(newbk, newlh);
@@ -358,17 +360,17 @@ public class TestBackwardCompat {
 
         void write100() throws Exception {
             for (int i = 0; i < 100; i++) {
-                lh.addEntry(ENTRY_DATA);
+                lh.addEntry(ENTRYDATA);
             }
         }
 
         long readAll() throws Exception {
             long count = 0;
-            Enumeration<org.apache.bookkeeper.client.LedgerEntry> entries
-                = lh.readEntries(0, lh.getLastAddConfirmed());
+            Enumeration<org.apache.bookkeeper.client.LedgerEntry> entries =
+                    lh.readEntries(0, lh.getLastAddConfirmed());
             while (entries.hasMoreElements()) {
                 assertTrue("entry data doesn't match",
-                           Arrays.equals(entries.nextElement().getEntry(), ENTRY_DATA));
+                           Arrays.equals(entries.nextElement().getEntry(), ENTRYDATA));
                 count++;
             }
             return count;
@@ -390,7 +392,7 @@ public class TestBackwardCompat {
     /*
      * Test old cookie accessing the new version formatted cluster.
      */
-    @Test(timeout=60000)
+    @Test(timeout = 60000)
     public void testOldCookieAccessingNewCluster() throws Exception {
         File journalDir = createTempDir("bookie", "journal");
         File ledgerDir = createTempDir("bookie", "ledger");
@@ -443,7 +445,7 @@ public class TestBackwardCompat {
      *    version due to a change in the ledger metadata format.
      *  - Otherwise, they should be compatible.
      */
-    @Test(timeout=60000)
+    @Test(timeout = 60000)
     public void testCompat410() throws Exception {
         File journalDir = createTempDir("bookie", "journal");
         File ledgerDir = createTempDir("bookie", "ledger");
@@ -673,7 +675,8 @@ public class TestBackwardCompat {
         int port = PortManager.nextFreePort();
         // start server, upgrade
         Server420 s420 = new Server420(journalDir, ledgerDir, port);
-        s420.getConf().setLedgerManagerFactoryClassName("org.apache.bk_v4_2_0.bookkeeper.meta.HierarchicalLedgerManagerFactory");
+        s420.getConf().setLedgerManagerFactoryClassName(
+                "org.apache.bk_v4_2_0.bookkeeper.meta.HierarchicalLedgerManagerFactory");
         s420.start();
 
         Ledger420 l420 = Ledger420.newLedger();
diff --git a/tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBookieRecovery.java b/tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBookieRecovery.java
new file mode 100644
index 0000000..6297657
--- /dev/null
+++ b/tests/backward/src/test/java/org/apache/bookkeeper/tests/backward/TestBookieRecovery.java
@@ -0,0 +1,364 @@
+/**
+ *
+ * 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 org.apache.bookkeeper.tests.backward;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import io.netty.buffer.ByteBuf;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.bookkeeper.client.AsyncCallback.RecoverCallback;
+import org.apache.bookkeeper.client.BKException;
+import org.apache.bookkeeper.client.BookKeeper.DigestType;
+import org.apache.bookkeeper.client.BookKeeperAdmin;
+import org.apache.bookkeeper.client.LedgerHandle;
+import org.apache.bookkeeper.client.LedgerMetadata;
+import org.apache.bookkeeper.conf.ClientConfiguration;
+import org.apache.bookkeeper.meta.LongHierarchicalLedgerManagerFactory;
+import org.apache.bookkeeper.meta.MSLedgerManagerFactory;
+import org.apache.bookkeeper.net.BookieSocketAddress;
+import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
+import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback;
+import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test backward compat issue on bookie recovery.
+ */
+public class TestBookieRecovery extends BookKeeperClusterTestCase {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TestBookieRecovery.class);
+
+    // Object used for synchronizing async method calls
+    class SyncObject {
+        boolean value;
+
+        public SyncObject() {
+            value = false;
+        }
+    }
+
+    // Object used for implementing the Bookie RecoverCallback for this jUnit
+    // test. This verifies that the operation completed successfully.
+    class BookieRecoverCallback implements RecoverCallback {
+        boolean success = false;
+        @Override
+        public void recoverComplete(int rc, Object ctx) {
+            LOG.info("Recovered bookie operation completed with rc: " + rc);
+            success = rc == BKException.Code.OK;
+            SyncObject sync = (SyncObject) ctx;
+            synchronized (sync) {
+                sync.value = true;
+                sync.notify();
+            }
+        }
+    }
+
+    // Objects to use for this jUnit test.
+    DigestType digestType;
+    String ledgerManagerFactory;
+    SyncObject sync;
+    BookieRecoverCallback bookieRecoverCb;
+    BookKeeperAdmin bkAdmin;
+
+    // Constructor
+    public TestBookieRecovery() {
+        super(3);
+
+        this.digestType = DigestType.CRC32;
+        this.ledgerManagerFactory = "org.apache.bookkeeper.meta.HierarchicalLedgerManagerFactory";
+        LOG.info("Using ledger manager " + ledgerManagerFactory);
+        // set ledger manager
+        baseConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
+        baseConf.setOpenFileLimit(200); // Limit the number of open files to avoid reaching the proc max
+        baseClientConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        // Set up the configuration properties needed.
+        baseClientConf.setBookieRecoveryDigestType(digestType);
+        baseClientConf.setBookieRecoveryPasswd("".getBytes());
+        super.setUp();
+
+        sync = new SyncObject();
+        bookieRecoverCb = new BookieRecoverCallback();
+        ClientConfiguration adminConf = new ClientConfiguration(baseClientConf);
+        adminConf.setZkServers(zkUtil.getZooKeeperConnectString());
+        bkAdmin = new BookKeeperAdmin(adminConf);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        // Release any resources used by the BookKeeperTools instance.
+        if (bkAdmin != null) {
+            bkAdmin.close();
+        }
+        super.tearDown();
+    }
+
+    // Object used for synchronizing async method calls
+    class SyncLedgerMetaObject {
+        boolean value;
+        int rc;
+        LedgerMetadata meta;
+
+        public SyncLedgerMetaObject() {
+            value = false;
+            meta = null;
+        }
+    }
+
+    private LedgerMetadata getLedgerMetadata(LedgerHandle lh) throws Exception {
+        final SyncLedgerMetaObject syncObj = new SyncLedgerMetaObject();
+        bkc.getLedgerManager().readLedgerMetadata(lh.getId(), new GenericCallback<LedgerMetadata>() {
+
+            @Override
+            public void operationComplete(int rc, LedgerMetadata result) {
+                synchronized (syncObj) {
+                    syncObj.rc = rc;
+                    syncObj.meta = result;
+                    syncObj.value = true;
+                    syncObj.notify();
+                }
+            }
+
+        });
+
+        synchronized (syncObj) {
+            while (!syncObj.value) {
+                syncObj.wait();
+            }
+        }
+        assertEquals(BKException.Code.OK, syncObj.rc);
+        return syncObj.meta;
+    }
+
+    private static class ReplicationVerificationCallback implements ReadEntryCallback {
+        final CountDownLatch latch;
+        final AtomicLong numSuccess;
+
+        ReplicationVerificationCallback(int numRequests) {
+            latch = new CountDownLatch(numRequests);
+            numSuccess = new AtomicLong(0);
+        }
+
+        @Override
+        public void readEntryComplete(int rc, long ledgerId, long entryId, ByteBuf buffer, Object ctx) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Got " + rc + " for ledger " + ledgerId + " entry " + entryId + " from " + ctx);
+            }
+            if (rc == BKException.Code.OK) {
+                numSuccess.incrementAndGet();
+            }
+            latch.countDown();
+        }
+
+        long await() throws InterruptedException {
+            if (!latch.await(60, TimeUnit.SECONDS)) {
+                LOG.warn("Didn't get all responses in verification");
+                return 0;
+            } else {
+                return numSuccess.get();
+            }
+        }
+    }
+
+    private boolean verifyFullyReplicated(LedgerHandle lh, long untilEntry) throws Exception {
+        LedgerMetadata md = getLedgerMetadata(lh);
+
+        Map<Long, ArrayList<BookieSocketAddress>> ensembles = md.getEnsembles();
+
+        HashMap<Long, Long> ranges = new HashMap<Long, Long>();
+        ArrayList<Long> keyList = Collections.list(
+                Collections.enumeration(ensembles.keySet()));
+        Collections.sort(keyList);
+        for (int i = 0; i < keyList.size() - 1; i++) {
+            ranges.put(keyList.get(i), keyList.get(i + 1));
+        }
+        ranges.put(keyList.get(keyList.size() - 1), untilEntry);
+
+        for (Map.Entry<Long, ArrayList<BookieSocketAddress>> e : ensembles.entrySet()) {
+            int quorum = md.getAckQuorumSize();
+            long startEntryId = e.getKey();
+            long endEntryId = ranges.get(startEntryId);
+            long expectedSuccess = quorum * (endEntryId - startEntryId);
+            int numRequests = e.getValue().size() * ((int) (endEntryId - startEntryId));
+
+            ReplicationVerificationCallback cb = new ReplicationVerificationCallback(numRequests);
+            for (long i = startEntryId; i < endEntryId; i++) {
+                for (BookieSocketAddress addr : e.getValue()) {
+                    bkc.getBookieClient().readEntry(addr, lh.getId(), i, cb, addr);
+                }
+            }
+
+            long numSuccess = cb.await();
+            if (numSuccess < expectedSuccess) {
+                LOG.warn("Fragment not fully replicated ledgerId = " + lh.getId()
+                         + " startEntryId = " + startEntryId
+                         + " endEntryId = " + endEntryId
+                         + " expectedSuccess = " + expectedSuccess
+                         + " gotSuccess = " + numSuccess);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Test that when we try to recover a ledger which doesn't have
+     * the password stored in the configuration, we don't succeed.
+     */
+    @Test(timeout = 60000)
+    public void ensurePasswordUsedForOldLedgers() throws Exception {
+        // This test bases on creating old ledgers in version 4.1.0, which only
+        // supports ZooKeeper based flat and hierarchical LedgerManagerFactory.
+        // So we ignore it for MSLedgerManagerFactory and LongHierarchicalLedgerManagerFactory.
+        if (MSLedgerManagerFactory.class.getName().equals(ledgerManagerFactory)) {
+            return;
+        }
+        if (LongHierarchicalLedgerManagerFactory.class.getName().equals(ledgerManagerFactory)) {
+            return;
+        }
+
+        // stop all bookies
+        // and wipe the ledger layout so we can use an old client
+        zkUtil.getZooKeeperClient().delete("/ledgers/LAYOUT", -1);
+
+        byte[] passwdCorrect = "AAAAAA".getBytes();
+        byte[] passwdBad = "BBBBBB".getBytes();
+        DigestType digestCorrect = digestType;
+        DigestType digestBad = digestCorrect == DigestType.MAC ? DigestType.CRC32 : DigestType.MAC;
+
+        org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType digestCorrect410 =
+                org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper.DigestType.valueOf(digestType.toString());
+
+        org.apache.bk_v4_1_0.bookkeeper.conf.ClientConfiguration c =
+                new org.apache.bk_v4_1_0.bookkeeper.conf.ClientConfiguration();
+        c.setZkServers(zkUtil.getZooKeeperConnectString())
+            .setLedgerManagerType(
+                    ledgerManagerFactory.equals("org.apache.bookkeeper.meta.FlatLedgerManagerFactory")
+                            ? "flat" : "hierarchical");
+
+        // create client to set up layout, close it, restart bookies, and open a new client.
+        // the new client is necessary to ensure that it has all the restarted bookies in the
+        // its available bookie list
+        org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper bkc41 =
+                new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(c);
+        bkc41.close();
+        restartBookies();
+        bkc41 = new org.apache.bk_v4_1_0.bookkeeper.client.BookKeeper(c);
+
+        org.apache.bk_v4_1_0.bookkeeper.client.LedgerHandle lh41 =
+                bkc41.createLedger(3, 2, digestCorrect410, passwdCorrect);
+        long ledgerId = lh41.getId();
+        for (int i = 0; i < 100; i++) {
+            lh41.addEntry("foobar".getBytes());
+        }
+        lh41.close();
+        bkc41.close();
+
+        // Startup a new bookie server
+        startNewBookie();
+        int removeIndex = 0;
+        BookieSocketAddress bookieSrc = bs.get(removeIndex).getLocalAddress();
+        bs.get(removeIndex).shutdown();
+        bs.remove(removeIndex);
+
+        // Check that entries are missing
+        LedgerHandle lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
+        assertFalse("Should be entries missing", verifyFullyReplicated(lh, 100));
+        lh.close();
+
+        // Try to recover with bad password in conf
+        // if the digest type is MAC
+        // for CRC32, the password is only checked
+        // when adding new entries, which recovery will
+        // never do
+        ClientConfiguration adminConf;
+        BookKeeperAdmin bka;
+        if (digestCorrect == DigestType.MAC) {
+            adminConf = new ClientConfiguration();
+            adminConf.setZkServers(zkUtil.getZooKeeperConnectString());
+            adminConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
+            adminConf.setBookieRecoveryDigestType(digestCorrect);
+            adminConf.setBookieRecoveryPasswd(passwdBad);
+
+            bka = new BookKeeperAdmin(adminConf);
+            try {
+                bka.recoverBookieData(bookieSrc, null);
+                fail("Shouldn't be able to recover with wrong password");
+            } catch (BKException bke) {
+                // correct behaviour
+            } finally {
+                bka.close();
+            }
+        }
+
+        // Try to recover with bad digest in conf
+        adminConf = new ClientConfiguration();
+        adminConf.setZkServers(zkUtil.getZooKeeperConnectString());
+        adminConf.setLedgerManagerFactoryClassName(ledgerManagerFactory);
+        adminConf.setBookieRecoveryDigestType(digestBad);
+        adminConf.setBookieRecoveryPasswd(passwdCorrect);
+
+        bka = new BookKeeperAdmin(adminConf);
+        try {
+            bka.recoverBookieData(bookieSrc, null);
+            fail("Shouldn't be able to recover with wrong digest");
+        } catch (BKException bke) {
+            // correct behaviour
+        } finally {
+            bka.close();
+        }
+
+        // Check that entries are still missing
+        lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
+        assertFalse("Should be entries missing", verifyFullyReplicated(lh, 100));
+        lh.close();
+
+        adminConf.setBookieRecoveryDigestType(digestCorrect);
+        adminConf.setBookieRecoveryPasswd(passwdCorrect);
+
+        bka = new BookKeeperAdmin(adminConf);
+        bka.recoverBookieData(bookieSrc, null);
+        bka.close();
+
+        lh = bkc.openLedgerNoRecovery(ledgerId, digestCorrect, passwdCorrect);
+        assertTrue("Should have recovered everything", verifyFullyReplicated(lh, 100));
+        lh.close();
+    }
+
+}
diff --git a/compat-deps/bookkeeper-server-compat-4.0.0/pom.xml b/tests/bookkeeper-server-compat-4.0.0/pom.xml
similarity index 90%
rename from compat-deps/bookkeeper-server-compat-4.0.0/pom.xml
rename to tests/bookkeeper-server-compat-4.0.0/pom.xml
index c1963cb..261ed08 100644
--- a/compat-deps/bookkeeper-server-compat-4.0.0/pom.xml
+++ b/tests/bookkeeper-server-compat-4.0.0/pom.xml
@@ -18,23 +18,19 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>compat-deps</artifactId>
-    <groupId>org.apache.bookkeeper</groupId>
+    <groupId>org.apache.bookkeeper.tests</groupId>
+    <artifactId>tests-parent</artifactId>
     <version>4.6.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
   </parent>
-  <groupId>org.apache.bookkeeper</groupId>
+  <groupId>org.apache.bookkeeper.tests</groupId>
   <artifactId>bookkeeper-server-compat400</artifactId>
-  <name>Apache BookKeeper :: Compability Dependencies :: 4.0.0</name>
+  <name>Apache BookKeeper :: Tests :: Compability Dependencies :: 4.0.0</name>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <dependencies>
     <dependency>
-      <groupId>com.google.protobuf</groupId>
-      <artifactId>protobuf-java</artifactId>
-      <version>2.4.1</version>
-    </dependency>
-    <dependency>
       <groupId>org.apache.bookkeeper</groupId>
       <artifactId>bookkeeper-server</artifactId>
       <version>4.0.0</version>
diff --git a/compat-deps/bookkeeper-server-compat-4.1.0/pom.xml b/tests/bookkeeper-server-compat-4.1.0/pom.xml
similarity index 89%
rename from compat-deps/bookkeeper-server-compat-4.1.0/pom.xml
rename to tests/bookkeeper-server-compat-4.1.0/pom.xml
index add7879..5462017 100644
--- a/compat-deps/bookkeeper-server-compat-4.1.0/pom.xml
+++ b/tests/bookkeeper-server-compat-4.1.0/pom.xml
@@ -18,23 +18,19 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>compat-deps</artifactId>
-    <groupId>org.apache.bookkeeper</groupId>
+    <groupId>org.apache.bookkeeper.tests</groupId>
+    <artifactId>tests-parent</artifactId>
     <version>4.6.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
   </parent>
-  <groupId>org.apache.bookkeeper</groupId>
+  <groupId>org.apache.bookkeeper.tests</groupId>
   <artifactId>bookkeeper-server-compat410</artifactId>
-  <name>Apache BookKeeper :: Compability Dependencies :: 4.1.0</name>
+  <name>Apache BookKeeper :: Tests :: Compability Dependencies :: 4.1.0</name>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <dependencies>
     <dependency>
-      <groupId>com.google.protobuf</groupId>
-      <artifactId>protobuf-java</artifactId>
-      <version>2.4.1</version>
-    </dependency>
-    <dependency>
       <groupId>org.apache.bookkeeper</groupId>
       <artifactId>bookkeeper-server</artifactId>
       <version>4.1.0</version>
@@ -81,7 +77,7 @@
                 </relocation>
                 <relocation>
                   <pattern>org.apache.zookeeper</pattern>
-                  <shadedPattern>org.apache.bk_v4_1_0.bookkeeper</shadedPattern>
+                  <shadedPattern>org.apache.bk_v4_1_0.zookeeper</shadedPattern>
                 </relocation>
                 <relocation>
                   <pattern>org.apache.jute</pattern>
diff --git a/compat-deps/bookkeeper-server-compat-4.2.0/pom.xml b/tests/bookkeeper-server-compat-4.2.0/pom.xml
similarity index 89%
rename from compat-deps/bookkeeper-server-compat-4.2.0/pom.xml
rename to tests/bookkeeper-server-compat-4.2.0/pom.xml
index aa79106..d1eef13 100644
--- a/compat-deps/bookkeeper-server-compat-4.2.0/pom.xml
+++ b/tests/bookkeeper-server-compat-4.2.0/pom.xml
@@ -18,23 +18,19 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>compat-deps</artifactId>
-    <groupId>org.apache.bookkeeper</groupId>
+    <groupId>org.apache.bookkeeper.tests</groupId>
+    <artifactId>tests-parent</artifactId>
     <version>4.6.0-SNAPSHOT</version>
+    <relativePath>..</relativePath>
   </parent>
-  <groupId>org.apache.bookkeeper</groupId>
+  <groupId>org.apache.bookkeeper.tests</groupId>
   <artifactId>bookkeeper-server-compat420</artifactId>
-  <name>Apache BookKeeper :: Compability Dependencies :: 4.2.0</name>
+  <name>Apache BookKeeper :: Tests :: Compability Dependencies :: 4.2.0</name>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <dependencies>
     <dependency>
-      <groupId>com.google.protobuf</groupId>
-      <artifactId>protobuf-java</artifactId>
-      <version>2.4.1</version>
-    </dependency>
-    <dependency>
       <groupId>org.apache.bookkeeper</groupId>
       <artifactId>bookkeeper-server</artifactId>
       <version>4.2.0</version>
@@ -81,7 +77,7 @@
                 </relocation>
                 <relocation>
                   <pattern>org.apache.zookeeper</pattern>
-                  <shadedPattern>org.apache.bk_v4_2_0.bookkeeper</shadedPattern>
+                  <shadedPattern>org.apache.bk_v4_2_0.zookeeper</shadedPattern>
                 </relocation>
                 <relocation>
                   <pattern>org.apache.jute</pattern>
diff --git a/compat-deps/pom.xml b/tests/pom.xml
similarity index 62%
rename from compat-deps/pom.xml
rename to tests/pom.xml
index 8235c9b..b4a6153 100644
--- a/compat-deps/pom.xml
+++ b/tests/pom.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -14,26 +15,34 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <packaging>pom</packaging>
+  <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>bookkeeper</artifactId>
     <groupId>org.apache.bookkeeper</groupId>
+    <artifactId>bookkeeper</artifactId>
     <version>4.6.0-SNAPSHOT</version>
   </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.bookkeeper</groupId>
-  <version>4.6.0-SNAPSHOT</version>
-  <artifactId>compat-deps</artifactId>
-  <packaging>pom</packaging>
-  <name>Apache BookKeeper :: Compability Dependencies</name>
+  <groupId>org.apache.bookkeeper.tests</groupId>
+  <artifactId>tests-parent</artifactId>
+  <name>Apache BookKeeper :: Tests</name>
   <modules>
     <module>bookkeeper-server-compat-4.0.0</module>
     <module>bookkeeper-server-compat-4.1.0</module>
     <module>bookkeeper-server-compat-4.2.0</module>
+    <module>backward</module>
   </modules>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <version>${maven-deploy-plugin.version}</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
 </project>

-- 
To stop receiving notification emails like this one, please contact
['"commits@bookkeeper.apache.org" <commits@bookkeeper.apache.org>'].

Mime
View raw message