bookkeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From eolive...@apache.org
Subject [bookkeeper] branch master updated: ISSUE #508: Introduce lifecycle components for managing components in bookie server
Date Tue, 19 Sep 2017 09:10:58 GMT
This is an automated email from the ASF dual-hosted git repository.

eolivelli 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 3ede9c6  ISSUE #508: Introduce lifecycle components for managing components in bookie server
3ede9c6 is described below

commit 3ede9c6c507aa93432757c4f1fe47c538fa83e34
Author: Jia Zhai <zhaijia@apache.org>
AuthorDate: Tue Sep 19 11:10:28 2017 +0200

    ISSUE #508: Introduce lifecycle components for managing components in bookie server
    
    Descriptions of the changes in this PR:
    
    The motivation of creating lifecycle components is described in #508.
    
    This changes include:
    
    - create bookkeeper-common module, move some common classes (such as interface annotations) to common module.
    - add the lifecycle components in bookkeeper-common module.
    - add a package `org.apache.bookkeeper.server` for keeping all server related classes.
    - rewrite BookieServer using the lifecycle components: stats provider | bookie server | auto recovery | http endpoint
    - BookieServer is still kept for backward compatible, all the main functions are delegated to `org.apache.bookkeeper.server.Main`.
    
    Author: Jia Zhai <zhaijia@apache.org>
    
    Reviewers: Enrico Olivelli <eolivelli@apache.org>
    
    This closes #509 from zhaijack/metadata/lifecycle_management, closes #508
---
 bookkeeper-benchmark/pom.xml                       |   5 +
 bookkeeper-common/pom.xml                          | 103 +++++++
 .../common/annotation/InterfaceAudience.java       |   0
 .../common/annotation/InterfaceStability.java      |   0
 .../bookkeeper/common/annotation/package-info.java |   0
 .../common/component/AbstractComponent.java        |  26 +-
 .../component/AbstractLifecycleComponent.java      | 111 ++++++++
 .../common/component/ComponentStarter.java         |  71 +++++
 .../bookkeeper/common/component/Lifecycle.java     | 196 +++++++++++++
 .../common/component/LifecycleComponent.java       |  21 +-
 .../common/component/LifecycleComponentStack.java  | 113 ++++++++
 .../common/component/LifecycleListener.java        |  20 +-
 .../bookkeeper/common/component}/package-info.java |   4 +-
 .../common/conf/ComponentConfiguration.java        | 285 +++++++++++++++++++
 .../bookkeeper/common/conf}/package-info.java      |   7 +-
 .../org/apache/bookkeeper/common/package-info.java |   2 -
 .../common/component/TestComponentStarter.java     |  54 ++++
 .../component/TestLifecycleComponentStack.java     | 116 ++++++++
 bookkeeper-server/bin/bookkeeper                   |   2 +-
 bookkeeper-server/pom.xml                          |  19 +-
 .../apache/bookkeeper/client/BookieInfoReader.java |   3 +-
 .../client/DefaultEnsemblePlacementPolicy.java     |   2 +-
 .../RackawareEnsemblePlacementPolicyImpl.java      |   2 +-
 .../org/apache/bookkeeper/proto/BookieServer.java  | 232 +--------------
 .../org/apache/bookkeeper/replication/Auditor.java |   2 +-
 .../java/org/apache/bookkeeper/server/Main.java    | 315 +++++++++++++++++++++
 .../conf/BookieConfiguration.java}                 |  23 +-
 .../annotation => server/conf}/package-info.java   |   6 +-
 .../{common => server}/package-info.java           |  10 +-
 .../server/service/AutoRecoveryService.java        |  62 ++++
 .../bookkeeper/server/service/BookieService.java   |  66 +++++
 .../bookkeeper/server/service/HttpService.java     |  65 +++++
 .../server/service/StatsProviderService.java       |  63 +++++
 .../service}/package-info.java                     |   6 +-
 .../bookkeeper/client/BookKeeperAdminTest.java     |   2 +-
 .../apache/bookkeeper/proto/TestDeathwatcher.java  |  62 ----
 .../replication/AuditorRollingRestartTest.java     |   2 +-
 .../TestAutoRecoveryAlongWithBookieServers.java    |   8 +-
 pom.xml                                            |  21 ++
 site/community/contributing.md                     |  14 +
 40 files changed, 1781 insertions(+), 340 deletions(-)

diff --git a/bookkeeper-benchmark/pom.xml b/bookkeeper-benchmark/pom.xml
index af3499b..488fd70 100644
--- a/bookkeeper-benchmark/pom.xml
+++ b/bookkeeper-benchmark/pom.xml
@@ -133,6 +133,11 @@
       <scope>compile</scope>
     </dependency>
     <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>${guava.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.bookkeeper</groupId>
       <artifactId>bookkeeper-server</artifactId>
       <version>${project.parent.version}</version>
diff --git a/bookkeeper-common/pom.xml b/bookkeeper-common/pom.xml
new file mode 100644
index 0000000..4961e4a
--- /dev/null
+++ b/bookkeeper-common/pom.xml
@@ -0,0 +1,103 @@
+<?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
+   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.
+-->
+<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>
+    <groupId>org.apache.bookkeeper</groupId>
+    <artifactId>bookkeeper</artifactId>
+    <version>4.6.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>bookkeeper-common</artifactId>
+  <name>Apache BookKeeper :: Common</name>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.bookkeeper.stats</groupId>
+      <artifactId>bookkeeper-stats-api</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>${guava.version}</version>
+    </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-jar-plugin</artifactId>
+        <version>${maven-jar-plugin.version}</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>${maven-surefire-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>
+            <id>checkstyle</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceAudience.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceAudience.java
similarity index 100%
rename from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceAudience.java
rename to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceAudience.java
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceStability.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceStability.java
similarity index 100%
rename from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceStability.java
rename to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/InterfaceStability.java
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
similarity index 100%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractComponent.java
similarity index 57%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractComponent.java
index b0fe537..36a64e4 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractComponent.java
@@ -16,7 +16,29 @@
  * limitations under the License.
  */
 
+package org.apache.bookkeeper.common.component;
+
+import org.apache.bookkeeper.common.conf.ComponentConfiguration;
+
 /**
- * Annotations used across the whole project.
+ * The base class represents a component.
  */
-package org.apache.bookkeeper.common.annotation;
+public abstract class AbstractComponent<ConfT extends ComponentConfiguration> implements AutoCloseable {
+
+    protected final String componentName;
+    protected final ConfT conf;
+
+    protected AbstractComponent(String name, ConfT conf) {
+        this.componentName = name;
+        this.conf = conf;
+    }
+
+    public ConfT getConf() {
+        return conf;
+    }
+
+    public String getName() {
+        return componentName;
+    }
+
+}
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractLifecycleComponent.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractLifecycleComponent.java
new file mode 100644
index 0000000..38e73bf
--- /dev/null
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/AbstractLifecycleComponent.java
@@ -0,0 +1,111 @@
+/*
+ * 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.common.component;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.bookkeeper.common.conf.ComponentConfiguration;
+import org.apache.bookkeeper.stats.StatsLogger;
+
+/**
+ * A mix of {@link AbstractComponent} and {@link LifecycleComponent}.
+ */
+@Slf4j
+public abstract class AbstractLifecycleComponent<ConfT extends ComponentConfiguration>
+    extends AbstractComponent<ConfT> implements LifecycleComponent {
+
+    protected final Lifecycle lifecycle = new Lifecycle();
+    private final Set<LifecycleListener> listeners = new CopyOnWriteArraySet<>();
+    protected final StatsLogger statsLogger;
+
+    protected AbstractLifecycleComponent(String componentName,
+                                         ConfT conf,
+                                         StatsLogger statsLogger) {
+        super(componentName, conf);
+        this.statsLogger = statsLogger;
+    }
+
+    protected StatsLogger getStatsLogger() {
+        return statsLogger;
+    }
+
+    @Override
+    public Lifecycle.State lifecycleState() {
+        return this.lifecycle.state();
+    }
+
+    @Override
+    public void addLifecycleListener(LifecycleListener listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeLifecycleListener(LifecycleListener listener) {
+        listeners.remove(listener);
+    }
+
+    @Override
+    public void start() {
+        if (!lifecycle.canMoveToStarted()) {
+            return;
+        }
+        listeners.forEach(LifecycleListener::beforeStart);
+        doStart();
+        lifecycle.moveToStarted();
+        listeners.forEach(LifecycleListener::afterStart);
+    }
+
+    protected abstract void doStart();
+
+    @Override
+    public void stop() {
+        if (!lifecycle.canMoveToStopped()) {
+            return;
+        }
+        listeners.forEach(LifecycleListener::beforeStop);
+        lifecycle.moveToStopped();
+        doStop();
+        listeners.forEach(LifecycleListener::afterStop);
+    }
+
+    protected abstract void doStop();
+
+    @Override
+    public void close() {
+        if (lifecycle.started()) {
+            stop();
+        }
+        if (!lifecycle.canMoveToClosed()) {
+            return;
+        }
+        listeners.forEach(LifecycleListener::beforeClose);
+        lifecycle.moveToClosed();
+        try {
+            doClose();
+        } catch (IOException e) {
+            log.warn("failed to close {}", getClass().getName(), e);
+        }
+        listeners.forEach(LifecycleListener::afterClose);
+    }
+
+    protected abstract void doClose() throws IOException;
+
+}
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/ComponentStarter.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/ComponentStarter.java
new file mode 100644
index 0000000..8ee6f77
--- /dev/null
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/ComponentStarter.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.component;
+
+import java.util.concurrent.CountDownLatch;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Utils to start components.
+ */
+@Slf4j
+public class ComponentStarter {
+
+    static class ComponentShutdownHook implements Runnable {
+
+        private final LifecycleComponent component;
+        private final CountDownLatch aliveLatch;
+
+        ComponentShutdownHook(LifecycleComponent component,
+                              CountDownLatch aliveLatch) {
+            this.component = component;
+            this.aliveLatch = aliveLatch;
+        }
+
+        @Override
+        public void run() {
+            aliveLatch.countDown();
+            log.info("Closing component {} in shutdown hook.", component.getName());
+            try {
+                component.close();
+                log.info("Closed component {} in shutdown hook successfully. Exiting.", component.getName());
+            } catch (Exception e) {
+                log.error("Failed to close component {} in shutdown hook gracefully, Exiting anyway",
+                    component.getName(), e);
+            }
+        }
+
+    }
+
+    /**
+     * Start a component and register a shutdown hook.
+     *
+     * @param component component to start.
+     */
+    public static void startComponent(LifecycleComponent component,
+                                      CountDownLatch aliveLatch) {
+        Runtime.getRuntime().addShutdownHook(new Thread(
+            new ComponentShutdownHook(component, aliveLatch), "component-shutdown-thread"));
+
+        log.info("Starting component {}.", component.getName());
+        component.start();
+        log.info("Started component {}.", component.getName());
+    }
+
+}
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/Lifecycle.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/Lifecycle.java
new file mode 100644
index 0000000..1ce06cf
--- /dev/null
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/Lifecycle.java
@@ -0,0 +1,196 @@
+/*
+ * 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.common.component;
+
+/**
+ * Lifecycle state. Allows the following transitions:
+ *
+ * <ul>
+ * <li>INITIALIZED -&gt; STARTED, STOPPED, CLOSED</li>
+ * <li>STARTED     -&gt; STOPPED</li>
+ * <li>STOPPED     -&gt; STARTED, CLOSED</li>
+ * <li>CLOSED      -&gt; </li>
+ * </ul>
+ *
+ * <p>Also allows to stay in the same state. For example, when calling stop on a component, the
+ * following logic can be applied:
+ *
+ * <pre>
+ * public void stop() {
+ *  if (!lifecycleState.moveToStopped()) {
+ *      return;
+ *  }
+ * // continue with stop logic
+ * }
+ * </pre>
+ *
+ * <p>Note, closed is only allowed to be called when stopped, so make sure to stop the component first.
+ * Here is how the logic can be applied:
+ *
+ * <pre>
+ * public void close() {
+ *  if (lifecycleState.started()) {
+ *      stop();
+ *  }
+ *  if (!lifecycleState.moveToClosed()) {
+ *      return;
+ *  }
+ *  // perform close logic here
+ * }
+ * </pre>
+ */
+public class Lifecycle {
+
+    /**
+     * Lifecycle State.
+     */
+    public enum State {
+        INITIALIZED,
+        STOPPED,
+        STARTED,
+        CLOSED
+    }
+
+    private volatile State state = State.INITIALIZED;
+
+    public State state() {
+        return this.state;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the state is initialized.
+     */
+    public boolean initialized() {
+        return state == State.INITIALIZED;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the state is started.
+     */
+    public boolean started() {
+        return state == State.STARTED;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the state is stopped.
+     */
+    public boolean stopped() {
+        return state == State.STOPPED;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the state is closed.
+     */
+    public boolean closed() {
+        return state == State.CLOSED;
+    }
+
+    public boolean stoppedOrClosed() {
+        Lifecycle.State state = this.state;
+        return state == State.STOPPED || state == State.CLOSED;
+    }
+
+    public boolean canMoveToStarted() throws IllegalStateException {
+        State localState = this.state;
+        if (localState == State.INITIALIZED || localState == State.STOPPED) {
+            return true;
+        }
+        if (localState == State.STARTED) {
+            return false;
+        }
+        if (localState == State.CLOSED) {
+            throw new IllegalStateException("Can't move to started state when closed");
+        }
+        throw new IllegalStateException("Can't move to started with unknown state");
+    }
+
+
+    public boolean moveToStarted() throws IllegalStateException {
+        State localState = this.state;
+        if (localState == State.INITIALIZED || localState == State.STOPPED) {
+            state = State.STARTED;
+            return true;
+        }
+        if (localState == State.STARTED) {
+            return false;
+        }
+        if (localState == State.CLOSED) {
+            throw new IllegalStateException("Can't move to started state when closed");
+        }
+        throw new IllegalStateException("Can't move to started with unknown state");
+    }
+
+    public boolean canMoveToStopped() throws IllegalStateException {
+        State localState = state;
+        if (localState == State.STARTED) {
+            return true;
+        }
+        if (localState == State.INITIALIZED || localState == State.STOPPED) {
+            return false;
+        }
+        if (localState == State.CLOSED) {
+            throw new IllegalStateException("Can't move to stopped state when closed");
+        }
+        throw new IllegalStateException("Can't move to stopped with unknown state");
+    }
+
+    public boolean moveToStopped() throws IllegalStateException {
+        State localState = state;
+        if (localState == State.STARTED) {
+            state = State.STOPPED;
+            return true;
+        }
+        if (localState == State.INITIALIZED || localState == State.STOPPED) {
+            return false;
+        }
+        if (localState == State.CLOSED) {
+            throw new IllegalStateException("Can't move to stopped state when closed");
+        }
+        throw new IllegalStateException("Can't move to stopped with unknown state");
+    }
+
+    public boolean canMoveToClosed() throws IllegalStateException {
+        State localState = state;
+        if (localState == State.CLOSED) {
+            return false;
+        }
+        if (localState == State.STARTED) {
+            throw new IllegalStateException("Can't move to closed before moving to stopped mode");
+        }
+        return true;
+    }
+
+    public boolean moveToClosed() throws IllegalStateException {
+        State localState = state;
+        if (localState == State.CLOSED) {
+            return false;
+        }
+        if (localState == State.STARTED) {
+            throw new IllegalStateException("Can't move to closed before moving to stopped mode");
+        }
+        state = State.CLOSED;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return state.toString();
+    }
+
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponent.java
similarity index 67%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponent.java
index b0fe537..12917fb 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponent.java
@@ -16,7 +16,24 @@
  * limitations under the License.
  */
 
+package org.apache.bookkeeper.common.component;
+
 /**
- * Annotations used across the whole project.
+ * A component based on lifecycle management.
  */
-package org.apache.bookkeeper.common.annotation;
+public interface LifecycleComponent extends AutoCloseable {
+
+    String getName();
+
+    Lifecycle.State lifecycleState();
+
+    void addLifecycleListener(LifecycleListener listener);
+
+    void removeLifecycleListener(LifecycleListener listener);
+
+    void start();
+
+    void stop();
+
+    void close();
+}
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponentStack.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponentStack.java
new file mode 100644
index 0000000..fa6f529
--- /dev/null
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleComponentStack.java
@@ -0,0 +1,113 @@
+/*
+ * 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.common.component;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.List;
+
+/**
+ * A stack of {@link LifecycleComponent}s.
+ */
+public class LifecycleComponentStack implements LifecycleComponent {
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder to build a stack of {@link LifecycleComponent}s.
+     */
+    public static class Builder {
+
+        private String name;
+        private final List<LifecycleComponent> components;
+
+        private Builder() {
+            components = Lists.newArrayList();
+        }
+
+        public Builder addComponent(LifecycleComponent component) {
+            checkNotNull(component, "Lifecycle component is null");
+            components.add(component);
+            return this;
+        }
+
+        public Builder withName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public LifecycleComponentStack build() {
+            checkNotNull(name, "Lifecycle component stack name is not provided");
+            checkArgument(!components.isEmpty(), "Lifecycle component stack is empty");
+            return new LifecycleComponentStack(
+                name,
+                ImmutableList.copyOf(components));
+        }
+
+    }
+
+    private final String name;
+    private final ImmutableList<LifecycleComponent> components;
+
+    private LifecycleComponentStack(String name,
+                                    ImmutableList<LifecycleComponent> components) {
+        this.name = name;
+        this.components = components;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Lifecycle.State lifecycleState() {
+        return components.get(0).lifecycleState();
+    }
+
+    @Override
+    public void addLifecycleListener(LifecycleListener listener) {
+        components.forEach(component -> component.addLifecycleListener(listener));
+    }
+
+    @Override
+    public void removeLifecycleListener(LifecycleListener listener) {
+        components.forEach(component -> component.removeLifecycleListener(listener));
+    }
+
+    @Override
+    public void start() {
+        components.forEach(component -> component.start());
+    }
+
+    @Override
+    public void stop() {
+        components.reverse().forEach(component -> component.stop());
+    }
+
+    @Override
+    public void close() {
+        components.reverse().forEach(component -> component.close());
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleListener.java
similarity index 74%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleListener.java
index b0fe537..5056548 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/LifecycleListener.java
@@ -16,7 +16,23 @@
  * limitations under the License.
  */
 
+package org.apache.bookkeeper.common.component;
+
 /**
- * Annotations used across the whole project.
+ * Listener listening of the lifecycle changes.
  */
-package org.apache.bookkeeper.common.annotation;
+public interface LifecycleListener {
+
+    void beforeStart();
+
+    void afterStart();
+
+    void beforeStop();
+
+    void afterStop();
+
+    void beforeClose();
+
+    void afterClose();
+
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/package-info.java
similarity index 89%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/package-info.java
index b0fe537..6aa4b64 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/component/package-info.java
@@ -17,6 +17,6 @@
  */
 
 /**
- * Annotations used across the whole project.
+ * Component and lifecycle management.
  */
-package org.apache.bookkeeper.common.annotation;
+package org.apache.bookkeeper.common.component;
diff --git a/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/ComponentConfiguration.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/ComponentConfiguration.java
new file mode 100644
index 0000000..3ebb4a4
--- /dev/null
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/ComponentConfiguration.java
@@ -0,0 +1,285 @@
+/*
+ * 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.common.conf;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+
+/**
+ * Component Configuration.
+ */
+public abstract class ComponentConfiguration implements Configuration {
+
+    protected static final String DELIMITER = ".";
+
+    private final String componentPrefix;
+    private final CompositeConfiguration conf;
+
+    protected ComponentConfiguration(CompositeConfiguration conf,
+                                     String componentPrefix) {
+        super();
+        this.conf = conf;
+        this.componentPrefix = componentPrefix;
+    }
+
+    protected String getKeyName(String name) {
+        return this.componentPrefix + name;
+    }
+
+    public String getComponentPrefix() {
+        return componentPrefix;
+    }
+
+    public CompositeConfiguration getUnderlyingConf() {
+        return conf;
+    }
+
+    /**
+     * Load configuration from a given {@code confURL}.
+     *
+     * @param confURL the url points to the configuration.
+     * @throws ConfigurationException when failed to load configuration.
+     */
+    public void loadConf(URL confURL) throws ConfigurationException {
+        Configuration loadedConf = new PropertiesConfiguration(confURL);
+        conf.addConfiguration(loadedConf);
+    }
+
+    public void validate() throws ConfigurationException {
+        // do nothing by default.
+    }
+
+    @Override
+    public Configuration subset(String prefix) {
+        return conf.subset(getKeyName(prefix));
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return conf.subset(componentPrefix).isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(String key) {
+        return conf.containsKey(getKeyName(key));
+    }
+
+    @Override
+    public void addProperty(String key, Object value) {
+        conf.addProperty(getKeyName(key), value);
+    }
+
+    @Override
+    public void setProperty(String key, Object value) {
+        conf.setProperty(getKeyName(key), value);
+    }
+
+    @Override
+    public void clearProperty(String key) {
+        conf.clearProperty(getKeyName(key));
+    }
+
+    @Override
+    public void clear() {
+        Iterator<String> keys = conf.getKeys();
+        keys.forEachRemaining(s -> {
+            if (s.startsWith(componentPrefix)) {
+                conf.clearProperty(s);
+            }
+        });
+    }
+
+    @Override
+    public Object getProperty(String key) {
+        return conf.getProperty(getKeyName(key));
+    }
+
+    @Override
+    public Iterator<String> getKeys(String prefix) {
+        return conf.getKeys(getKeyName(prefix));
+    }
+
+    @Override
+    public Iterator<String> getKeys() {
+        return conf.getKeys(componentPrefix);
+    }
+
+    @Override
+    public Properties getProperties(String key) {
+        return conf.getProperties(getKeyName(key));
+    }
+
+    @Override
+    public boolean getBoolean(String key) {
+        return conf.getBoolean(getKeyName(key));
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defaultValue) {
+        return conf.getBoolean(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Boolean getBoolean(String key, Boolean defaultValue) {
+        return conf.getBoolean(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public byte getByte(String key) {
+        return conf.getByte(getKeyName(key));
+    }
+
+    @Override
+    public byte getByte(String key, byte defaultValue) {
+        return conf.getByte(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Byte getByte(String key, Byte defaultValue) {
+        return conf.getByte(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public double getDouble(String key) {
+        return conf.getDouble(getKeyName(key));
+    }
+
+    @Override
+    public double getDouble(String key, double defaultValue) {
+        return conf.getDouble(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Double getDouble(String key, Double defaultValue) {
+        return conf.getDouble(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public float getFloat(String key) {
+        return conf.getFloat(getKeyName(key));
+    }
+
+    @Override
+    public float getFloat(String key, float defaultValue) {
+        return conf.getFloat(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Float getFloat(String key, Float defaultValue) {
+        return conf.getFloat(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public int getInt(String key) {
+        return conf.getInt(getKeyName(key));
+    }
+
+    @Override
+    public int getInt(String key, int defaultValue) {
+        return conf.getInt(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Integer getInteger(String key, Integer defaultValue) {
+        return conf.getInt(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public long getLong(String key) {
+        return conf.getLong(getKeyName(key));
+    }
+
+    @Override
+    public long getLong(String key, long defaultValue) {
+        return conf.getLong(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Long getLong(String key, Long defaultValue) {
+        return conf.getLong(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public short getShort(String key) {
+        return conf.getShort(getKeyName(key));
+    }
+
+    @Override
+    public short getShort(String key, short defaultValue) {
+        return conf.getShort(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public Short getShort(String key, Short defaultValue) {
+        return conf.getShort(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public BigDecimal getBigDecimal(String key) {
+        return conf.getBigDecimal(getKeyName(key));
+    }
+
+    @Override
+    public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
+        return conf.getBigDecimal(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public BigInteger getBigInteger(String key) {
+        return conf.getBigInteger(getKeyName(key));
+    }
+
+    @Override
+    public BigInteger getBigInteger(String key, BigInteger defaultValue) {
+        return conf.getBigInteger(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public String getString(String key) {
+        return conf.getString(getKeyName(key));
+    }
+
+    @Override
+    public String getString(String key, String defaultValue) {
+        return conf.getString(getKeyName(key), defaultValue);
+    }
+
+    @Override
+    public String[] getStringArray(String key) {
+        return conf.getStringArray(getKeyName(key));
+    }
+
+    @Override
+    public List<Object> getList(String key) {
+        return conf.getList(getKeyName(key));
+    }
+
+    @Override
+    public List<Object> getList(String key, List<?> defaultValue) {
+        return conf.getList(getKeyName(key), defaultValue);
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/package-info.java
similarity index 89%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/package-info.java
index b0fe537..ba4d756 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/conf/package-info.java
@@ -1,4 +1,4 @@
-/*
+/**
  * 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
@@ -15,8 +15,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 /**
- * Annotations used across the whole project.
+ * Configuration related utilities.
  */
-package org.apache.bookkeeper.common.annotation;
+package org.apache.bookkeeper.common.conf;
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/package-info.java
similarity index 92%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java
copy to bookkeeper-common/src/main/java/org/apache/bookkeeper/common/package-info.java
index 08891b0..04312bb 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java
+++ b/bookkeeper-common/src/main/java/org/apache/bookkeeper/common/package-info.java
@@ -18,7 +18,5 @@
 
 /**
  * Common functions and utils used across the project.
- *
- * <p>NOTE: refactor this package to bookkeeper-common module after 4.5</p>
  */
 package org.apache.bookkeeper.common;
diff --git a/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestComponentStarter.java b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestComponentStarter.java
new file mode 100644
index 0000000..eadbbd9
--- /dev/null
+++ b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestComponentStarter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.common.component;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.CountDownLatch;
+import org.apache.bookkeeper.common.component.ComponentStarter.ComponentShutdownHook;
+import org.junit.Test;
+
+/**
+ * Test Case of {@link ComponentStarter}.
+ */
+public class TestComponentStarter {
+
+  @Test
+  public void testStartComponent() {
+    LifecycleComponent component = mock(LifecycleComponent.class);
+    CountDownLatch latch = new CountDownLatch(1);
+    when(component.getName()).thenReturn("test-start-component");
+    ComponentStarter.startComponent(component, latch);
+    verify(component).start();
+  }
+
+  @Test
+  public void testComponentShutdownHook() throws Exception {
+    LifecycleComponent component = mock(LifecycleComponent.class);
+    CountDownLatch latch = new CountDownLatch(1);
+    when(component.getName()).thenReturn("test-shutdown-hook");
+    ComponentShutdownHook shutdownHook = new ComponentShutdownHook(component, latch);
+    shutdownHook.run();
+    verify(component).close();
+    latch.await();
+  }
+
+}
diff --git a/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestLifecycleComponentStack.java b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestLifecycleComponentStack.java
new file mode 100644
index 0000000..52e532f
--- /dev/null
+++ b/bookkeeper-common/src/test/java/org/apache/bookkeeper/common/component/TestLifecycleComponentStack.java
@@ -0,0 +1,116 @@
+/*
+ * 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.common.component;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link LifecycleComponentStack}.
+ */
+public class TestLifecycleComponentStack {
+
+  @Test(expected = NullPointerException.class)
+  public void testBuilderWithNullName() {
+    LifecycleComponentStack.newBuilder().withName(null).build();
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void testBuilderWithNullComponent() {
+    LifecycleComponentStack.newBuilder().addComponent(null);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testBuilderWithEmptyComponentList() {
+    LifecycleComponentStack.newBuilder().withName("empty-list").build();
+  }
+
+  @Test
+  public void testGetName() {
+    String name = "test-get-name";
+    LifecycleComponentStack stack = LifecycleComponentStack.newBuilder()
+      .withName(name)
+      .addComponent(mock(LifecycleComponent.class))
+      .build();
+    assertEquals(name, stack.getName());
+  }
+
+  @Test
+  public void testLifecycleState() {
+    LifecycleComponent component1 = mock(LifecycleComponent.class);
+    when(component1.lifecycleState()).thenReturn(Lifecycle.State.INITIALIZED);
+    LifecycleComponent component2 = mock(LifecycleComponent.class);
+    when(component2.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
+
+    LifecycleComponentStack stack = LifecycleComponentStack.newBuilder()
+      .withName("get-lifecycle-state")
+      .addComponent(component1)
+      .addComponent(component2)
+      .build();
+
+    assertEquals(Lifecycle.State.INITIALIZED, stack.lifecycleState());
+  }
+
+  @Test
+  public void testAddRemoveLifecycleListener() {
+    LifecycleComponent component1 = mock(LifecycleComponent.class);
+    LifecycleComponent component2 = mock(LifecycleComponent.class);
+
+    LifecycleComponentStack stack = LifecycleComponentStack.newBuilder()
+      .withName("get-lifecycle-listener")
+      .addComponent(component1)
+      .addComponent(component2)
+      .build();
+
+    LifecycleListener listener = mock(LifecycleListener.class);
+    stack.addLifecycleListener(listener);
+    verify(component1).addLifecycleListener(listener);
+    verify(component2).addLifecycleListener(listener);
+    stack.removeLifecycleListener(listener);
+    verify(component1).removeLifecycleListener(listener);
+    verify(component2).removeLifecycleListener(listener);
+  }
+
+  @Test
+  public void testStartStopClose() {
+    LifecycleComponent component1 = mock(LifecycleComponent.class);
+    LifecycleComponent component2 = mock(LifecycleComponent.class);
+
+    LifecycleComponentStack stack = LifecycleComponentStack.newBuilder()
+      .withName("get-lifecycle-listener")
+      .addComponent(component1)
+      .addComponent(component2)
+      .build();
+
+    stack.start();
+    verify(component1).start();
+    verify(component2).start();
+    stack.stop();
+    verify(component1).stop();
+    verify(component2).stop();
+    stack.close();
+    verify(component1).close();
+    verify(component2).close();
+  }
+
+}
diff --git a/bookkeeper-server/bin/bookkeeper b/bookkeeper-server/bin/bookkeeper
index 836ad68..012e37f 100755
--- a/bookkeeper-server/bin/bookkeeper
+++ b/bookkeeper-server/bin/bookkeeper
@@ -203,7 +203,7 @@ OPTS="$OPTS -Dbookkeeper.log.file=$BOOKIE_LOG_FILE"
 #Change to BK_HOME to support relative paths
 cd "$BK_HOME"
 if [ $COMMAND == "bookie" ]; then
-  exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.proto.BookieServer --conf $BOOKIE_CONF $@
+  exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.server.Main --conf $BOOKIE_CONF $@
 elif [ $COMMAND == "autorecovery" ]; then
   exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.replication.AutoRecoveryMain --conf $BOOKIE_CONF $@
 elif [ $COMMAND == "localbookie" ]; then
diff --git a/bookkeeper-server/pom.xml b/bookkeeper-server/pom.xml
index bafa29a..8fa96e5 100644
--- a/bookkeeper-server/pom.xml
+++ b/bookkeeper-server/pom.xml
@@ -30,16 +30,11 @@
   </properties>
   <dependencies>
     <dependency>
-      <groupId>org.apache.bookkeeper.stats</groupId>
-      <artifactId>bookkeeper-stats-api</artifactId>
+      <groupId>org.apache.bookkeeper</groupId>
+      <artifactId>bookkeeper-common</artifactId>
       <version>${project.parent.version}</version>
     </dependency>
     <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-      <version>${guava.version}</version>
-    </dependency>
-    <dependency>
       <groupId>com.google.protobuf</groupId>
       <artifactId>protobuf-java</artifactId>
       <version>${protobuf.version}</version>
@@ -109,11 +104,6 @@
       <version>3.3.2</version>
     </dependency>
     <dependency>
-      <groupId>commons-configuration</groupId>
-      <artifactId>commons-configuration</artifactId>
-      <version>1.6</version>
-    </dependency>
-    <dependency>
       <groupId>commons-cli</groupId>
       <artifactId>commons-cli</artifactId>
       <version>1.2</version>
@@ -134,6 +124,11 @@
       <version>2.4</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-collections4</artifactId>
+      <version>4.1</version>
+    </dependency>
+    <dependency>
       <groupId>net.java.dev.jna</groupId>
       <artifactId>jna</artifactId>
       <version>3.2.7</version>
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
index c015efa..399af6b 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookieInfoReader.java
@@ -30,8 +30,7 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.bookkeeper.common.annotation.InterfaceAudience;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.bookkeeper.client.WeightedRandomSelection.WeightedObject;
 import org.apache.bookkeeper.conf.ClientConfiguration;
 import org.apache.bookkeeper.net.BookieSocketAddress;
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultEnsemblePlacementPolicy.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultEnsemblePlacementPolicy.java
index d401233..14a8a0c 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultEnsemblePlacementPolicy.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/DefaultEnsemblePlacementPolicy.java
@@ -35,7 +35,7 @@ import org.apache.bookkeeper.feature.FeatureProvider;
 import org.apache.bookkeeper.net.BookieSocketAddress;
 import org.apache.bookkeeper.net.DNSToSwitchMapping;
 import org.apache.bookkeeper.stats.StatsLogger;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
index e37f296..a7a59db 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/RackawareEnsemblePlacementPolicyImpl.java
@@ -42,7 +42,7 @@ import org.apache.bookkeeper.net.*;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
 import org.apache.bookkeeper.util.ReflectionUtils;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
index 5ec481f..a38556a 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
@@ -20,13 +20,12 @@
  */
 package org.apache.bookkeeper.proto;
 
+import static org.apache.bookkeeper.bookie.BookKeeperServerStats.BOOKIE_SCOPE;
+import static org.apache.bookkeeper.bookie.BookKeeperServerStats.SERVER_SCOPE;
+
 import com.google.common.annotations.VisibleForTesting;
-import java.io.File;
 import java.io.IOException;
-import java.lang.Integer;
-import java.net.MalformedURLException;
 import java.net.UnknownHostException;
-import java.util.Arrays;
 import org.apache.bookkeeper.bookie.Bookie;
 import org.apache.bookkeeper.bookie.BookieCriticalThread;
 import org.apache.bookkeeper.bookie.BookieException;
@@ -38,31 +37,18 @@ import org.apache.bookkeeper.http.HttpServer;
 import org.apache.bookkeeper.http.HttpServerLoader;
 import org.apache.bookkeeper.net.BookieSocketAddress;
 import org.apache.bookkeeper.processor.RequestProcessor;
-import org.apache.bookkeeper.replication.AutoRecoveryMain;
 import org.apache.bookkeeper.replication.ReplicationException.CompatibilityException;
 import org.apache.bookkeeper.replication.ReplicationException.UnavailableException;
+import org.apache.bookkeeper.server.Main;
 import org.apache.bookkeeper.stats.NullStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
-import org.apache.bookkeeper.stats.StatsProvider;
 import org.apache.bookkeeper.tls.SecurityException;
 import org.apache.bookkeeper.tls.SecurityHandlerFactory;
 import org.apache.bookkeeper.tls.SecurityProviderFactoryFactory;
-import org.apache.bookkeeper.util.ReflectionUtils;
-import org.apache.commons.cli.BasicParser;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.configuration.ConfigurationException;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.apache.bookkeeper.bookie.BookKeeperServerStats.BOOKIE_SCOPE;
-import static org.apache.bookkeeper.bookie.BookKeeperServerStats.SERVER_SCOPE;
-import static org.apache.bookkeeper.replication.ReplicationStats.REPLICATION_SCOPE;
-
 /**
  * Implements the server-side part of the BookKeeper protocol.
  *
@@ -78,9 +64,7 @@ public class BookieServer {
     int exitCode = ExitCode.OK;
 
     // operation stats
-    AutoRecoveryMain autoRecoveryMain = null;
     HttpServer httpServer = null;
-    private boolean isAutoRecoveryDaemonEnabled;
 
     // request processor
     private final RequestProcessor requestProcessor;
@@ -114,11 +98,6 @@ public class BookieServer {
         this.requestProcessor = new BookieRequestProcessor(conf, bookie,
                 statsLogger.scope(SERVER_SCOPE), shFactory);
         this.nettyServer.setRequestProcessor(this.requestProcessor);
-
-        isAutoRecoveryDaemonEnabled = conf.isAutoRecoveryDaemonEnabled();
-        if (isAutoRecoveryDaemonEnabled) {
-            this.autoRecoveryMain = new AutoRecoveryMain(conf, statsLogger.scope(REPLICATION_SCOPE));
-        }
     }
 
     protected Bookie newBookie(ServerConfiguration conf)
@@ -135,9 +114,6 @@ public class BookieServer {
             exitCode = bookie.getExitCode();
             return;
         }
-        if (isAutoRecoveryDaemonEnabled && this.autoRecoveryMain != null) {
-            this.autoRecoveryMain.start();
-        }
         if (conf.isHttpServerEnabled()) {
             BKServiceProvider serviceProvider = new BKServiceProvider.Builder()
                 .setBookieServer(this)
@@ -196,9 +172,6 @@ public class BookieServer {
             return;
         }
         exitCode = bookie.shutdown();
-        if (isAutoRecoveryDaemonEnabled && this.autoRecoveryMain != null) {
-            this.autoRecoveryMain.shutdown();
-        }
         this.requestProcessor.close();
         if (this.httpServer != null && this.httpServer.isRunning()) {
             this.httpServer.stopServer();
@@ -219,16 +192,6 @@ public class BookieServer {
         return bookie.isRunning();
     }
 
-    /**
-     * Whether auto-recovery service running with Bookie?
-     *
-     * @return true if auto-recovery service is running, otherwise return false
-     */
-    public boolean isAutoRecoveryRunning() {
-        return this.autoRecoveryMain != null
-                && this.autoRecoveryMain.isAutoRecoveryRunning();
-    }
-
     public void join() throws InterruptedException {
         bookie.join();
     }
@@ -261,198 +224,15 @@ public class BookieServer {
                     shutdown();
                     break;
                 }
-                if (isAutoRecoveryDaemonEnabled && !isAutoRecoveryRunning()) {
-                    LOG.error("Autorecovery daemon has stopped. Please check the logs");
-                    isAutoRecoveryDaemonEnabled = false; // to avoid spamming the logs
-                }
             }
             LOG.info("BookieDeathWatcher exited loop!");
         }
     }
 
-    static final Options bkOpts = new Options();
-    static {
-        bkOpts.addOption("c", "conf", true, "Configuration for Bookie Server");
-        bkOpts.addOption("withAutoRecovery", false,
-                "Start Autorecovery service Bookie server");
-        bkOpts.addOption("readOnly", false,
-                "Force Start a ReadOnly Bookie server");
-        bkOpts.addOption("z", "zkserver", true, "Zookeeper Server");
-        bkOpts.addOption("m", "zkledgerpath", true, "Zookeeper ledgers root path");
-        bkOpts.addOption("p", "bookieport", true, "bookie port exported");
-        bkOpts.addOption("j", "journal", true, "bookie journal directory");
-        Option indexDirs = new Option ("i", "indexdirs", true, "bookie index directories");
-        indexDirs.setArgs(10);
-        bkOpts.addOption(indexDirs);
-        Option ledgerDirs = new Option ("l", "ledgerdirs", true, "bookie ledgers directories");
-        ledgerDirs.setArgs(10);
-        bkOpts.addOption(ledgerDirs);
-        bkOpts.addOption("h", "help", false, "Print help message");
-    }
-
-    /**
-     * Print usage
-     */
-    private static void printUsage() {
-        HelpFormatter hf = new HelpFormatter();
-        String header = "\n"
-                        + "BookieServer provide an interface to start a bookie with configuration file and/or arguments."
-                        + "The settings in configuration file will be overwrite by provided arguments.\n"
-                        + "Options including:\n";
-        String footer = "Here is an example:\n" +
-                        "\tBookieServer -c bookie.conf -z localhost:2181 -m /bookkeeper/ledgers " +
-                        "-p 3181 -j /mnt/journal -i \"/mnt/index1 /mnt/index2\" -l \"/mnt/ledger1 /mnt/ledger2 /mnt/ledger3\"\n";
-        hf.printHelp("BookieServer [options]\n", header,  bkOpts, footer, true);
-    }
-
-    private static void loadConfFile(ServerConfiguration conf, String confFile)
-        throws IllegalArgumentException {
-        try {
-            conf.loadConf(new File(confFile).toURI().toURL());
-            conf.validate();
-        } catch (MalformedURLException e) {
-            LOG.error("Could not open configuration file: " + confFile, e);
-            throw new IllegalArgumentException();
-        } catch (ConfigurationException e) {
-            LOG.error("Malformed configuration file: " + confFile, e);
-            throw new IllegalArgumentException();
-        }
-        LOG.info("Using configuration file " + confFile);
-    }
-
-    private static ServerConfiguration parseArgs(String[] args)
-        throws IllegalArgumentException {
-        try {
-            BasicParser parser = new BasicParser();
-            CommandLine cmdLine = parser.parse(bkOpts, args);
-
-            if (cmdLine.hasOption('h')) {
-                throw new IllegalArgumentException();
-            }
-
-            ServerConfiguration conf = new ServerConfiguration();
-
-            if (cmdLine.hasOption('c')) {
-                String confFile = cmdLine.getOptionValue("c");
-                loadConfFile(conf, confFile);
-            }
-
-            if (cmdLine.hasOption("withAutoRecovery")) {
-                conf.setAutoRecoveryDaemonEnabled(true);
-            }
-
-            if (cmdLine.hasOption("readOnly")) {
-                conf.setForceReadOnlyBookie(true);
-            }
-
-
-            // command line arguments overwrite settings in configuration file
-            if (cmdLine.hasOption('z')) {
-                String sZK = cmdLine.getOptionValue('z');
-                LOG.info("Get cmdline zookeeper instance: " + sZK);
-                conf.setZkServers(sZK);
-            }
-
-            if (cmdLine.hasOption('m')) {
-                String sZkLedgersRootPath = cmdLine.getOptionValue('m');
-                LOG.info("Get cmdline zookeeper ledger path: " + sZkLedgersRootPath);
-                conf.setZkLedgersRootPath(sZkLedgersRootPath);
-            }
-
-            if (cmdLine.hasOption('p')) {
-                String sPort = cmdLine.getOptionValue('p');
-                LOG.info("Get cmdline bookie port: " + sPort);
-                Integer iPort = Integer.parseInt(sPort);
-                conf.setBookiePort(iPort.intValue());
-            }
-
-            if (cmdLine.hasOption('j')) {
-                String sJournalDir = cmdLine.getOptionValue('j');
-                LOG.info("Get cmdline journal dir: " + sJournalDir);
-                conf.setJournalDirName(sJournalDir);
-            }
-
-            if (cmdLine.hasOption('i')) {
-                String[] sIndexDirs = cmdLine.getOptionValues('i');
-                LOG.info("Get cmdline index dirs: ");
-                for(String index : sIndexDirs) {
-                    LOG.info("indexDir : " + index);
-                }
-                conf.setIndexDirName(sIndexDirs);
-            }
-
-            if (cmdLine.hasOption('l')) {
-                String[] sLedgerDirs = cmdLine.getOptionValues('l');
-                LOG.info("Get cmdline ledger dirs: ");
-                for(String ledger : sLedgerDirs) {
-                    LOG.info("ledgerdir : " + ledger);
-                }
-                conf.setLedgerDirNames(sLedgerDirs);
-            }
-
-            return conf;
-        } catch (ParseException e) {
-            LOG.error("Error parsing command line arguments : ", e);
-            throw new IllegalArgumentException(e);
-        }
-    }
-
     /**
-     * @param args
-     * @throws IOException
-     * @throws InterruptedException
+     * Legacy Method to run bookie server.
      */
     public static void main(String[] args) {
-        ServerConfiguration conf = null;
-        try {
-            conf = parseArgs(args);
-        } catch (IllegalArgumentException iae) {
-            LOG.error("Error parsing command line arguments : ", iae);
-            System.err.println(iae.getMessage());
-            printUsage();
-            System.exit(ExitCode.INVALID_CONF);
-        }
-
-        StringBuilder sb = new StringBuilder();
-        String[] ledgerDirNames = conf.getLedgerDirNames();
-        for (int i = 0; i < ledgerDirNames.length; i++) {
-            if (i != 0) {
-                sb.append(',');
-            }
-            sb.append(ledgerDirNames[i]);
-        }
-
-        String hello = String.format(
-                           "Hello, I'm your bookie, listening on port %1$s. ZKServers are on %2$s. Journals are in %3$s. Ledgers are stored in %4$s.",
-                           conf.getBookiePort(), conf.getZkServers(),
-                           Arrays.asList(conf.getJournalDirNames()), sb);
-        LOG.info(hello);
-        try {
-            // Initialize Stats Provider
-            Class<? extends StatsProvider> statsProviderClass =
-                    conf.getStatsProviderClass();
-            final StatsProvider statsProvider = ReflectionUtils.newInstance(statsProviderClass);
-            statsProvider.start(conf);
-
-            final BookieServer bs = new BookieServer(conf, statsProvider.getStatsLogger(""));
-            bs.start();
-            Runtime.getRuntime().addShutdownHook(new Thread() {
-                @Override
-                public void run() {
-                    bs.shutdown();
-                    LOG.info("Shut down bookie server successfully");
-                }
-            });
-            LOG.info("Register shutdown hook successfully");
-            bs.join();
-
-            statsProvider.stop();
-            LOG.info("Stop stats provider");
-            bs.shutdown();
-            System.exit(bs.getExitCode());
-        } catch (Exception e) {
-            LOG.error("Exception running bookie server : ", e);
-            System.exit(ExitCode.SERVER_EXCEPTION);
-        }
+        Main.main(args);
     }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
index 054e09d..67bde47 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/replication/Auditor.java
@@ -56,7 +56,7 @@ import org.apache.bookkeeper.stats.Counter;
 import org.apache.bookkeeper.stats.OpStatsLogger;
 import org.apache.bookkeeper.stats.StatsLogger;
 import org.apache.bookkeeper.zookeeper.ZooKeeperClient;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.zookeeper.AsyncCallback;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooKeeper;
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/Main.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/Main.java
new file mode 100644
index 0000000..ebf1680
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/Main.java
@@ -0,0 +1,315 @@
+/*
+ * 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.server;
+
+import static org.apache.bookkeeper.replication.ReplicationStats.REPLICATION_SCOPE;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.bookkeeper.bookie.ExitCode;
+import org.apache.bookkeeper.common.component.ComponentStarter;
+import org.apache.bookkeeper.common.component.LifecycleComponent;
+import org.apache.bookkeeper.common.component.LifecycleComponentStack;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.http.BKServiceProvider;
+import org.apache.bookkeeper.server.conf.BookieConfiguration;
+import org.apache.bookkeeper.server.service.AutoRecoveryService;
+import org.apache.bookkeeper.server.service.BookieService;
+import org.apache.bookkeeper.server.service.HttpService;
+import org.apache.bookkeeper.server.service.StatsProviderService;
+import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.commons.cli.BasicParser;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.configuration.ConfigurationException;
+
+/**
+ * A bookie server is a server that run bookie and serving rpc requests.
+ *
+ * <p>It is a rewritten server using {@link org.apache.bookkeeper.common.component.LifecycleComponent},
+ * replacing the legacy server {@link org.apache.bookkeeper.proto.BookieServer}.
+ */
+@Slf4j
+public class Main {
+
+    static final Options bkOpts = new Options();
+    static {
+        bkOpts.addOption("c", "conf", true, "Configuration for Bookie Server");
+        bkOpts.addOption("withAutoRecovery", false,
+                "Start Autorecovery service Bookie server");
+        bkOpts.addOption("readOnly", false,
+                "Force Start a ReadOnly Bookie server");
+        bkOpts.addOption("z", "zkserver", true, "Zookeeper Server");
+        bkOpts.addOption("m", "zkledgerpath", true, "Zookeeper ledgers root path");
+        bkOpts.addOption("p", "bookieport", true, "bookie port exported");
+        bkOpts.addOption("j", "journal", true, "bookie journal directory");
+        Option indexDirs = new Option ("i", "indexdirs", true, "bookie index directories");
+        indexDirs.setArgs(10);
+        bkOpts.addOption(indexDirs);
+        Option ledgerDirs = new Option ("l", "ledgerdirs", true, "bookie ledgers directories");
+        ledgerDirs.setArgs(10);
+        bkOpts.addOption(ledgerDirs);
+        bkOpts.addOption("h", "help", false, "Print help message");
+    }
+
+    /**
+     * Print usage
+     */
+    private static void printUsage() {
+        HelpFormatter hf = new HelpFormatter();
+        String header = "\n"
+                        + "BookieServer provide an interface to start a bookie with configuration file and/or arguments."
+                        + "The settings in configuration file will be overwrite by provided arguments.\n"
+                        + "Options including:\n";
+        String footer = "Here is an example:\n" +
+                        "\tBookieServer -c bookie.conf -z localhost:2181 -m /bookkeeper/ledgers " +
+                        "-p 3181 -j /mnt/journal -i \"/mnt/index1 /mnt/index2\" -l \"/mnt/ledger1 /mnt/ledger2 /mnt/ledger3\"\n";
+        hf.printHelp("BookieServer [options]\n", header,  bkOpts, footer, true);
+    }
+
+    private static void loadConfFile(ServerConfiguration conf, String confFile)
+        throws IllegalArgumentException {
+        try {
+            conf.loadConf(new File(confFile).toURI().toURL());
+            conf.validate();
+        } catch (MalformedURLException e) {
+            log.error("Could not open configuration file: " + confFile, e);
+            throw new IllegalArgumentException();
+        } catch (ConfigurationException e) {
+            log.error("Malformed configuration file: " + confFile, e);
+            throw new IllegalArgumentException();
+        }
+        log.info("Using configuration file " + confFile);
+    }
+
+    private static ServerConfiguration parseArgs(String[] args)
+        throws IllegalArgumentException {
+        try {
+            BasicParser parser = new BasicParser();
+            CommandLine cmdLine = parser.parse(bkOpts, args);
+
+            if (cmdLine.hasOption('h')) {
+                throw new IllegalArgumentException();
+            }
+
+            ServerConfiguration conf = new ServerConfiguration();
+
+            if (cmdLine.hasOption('c')) {
+                String confFile = cmdLine.getOptionValue("c");
+                loadConfFile(conf, confFile);
+            }
+
+            if (cmdLine.hasOption("withAutoRecovery")) {
+                conf.setAutoRecoveryDaemonEnabled(true);
+            }
+
+            if (cmdLine.hasOption("readOnly")) {
+                conf.setForceReadOnlyBookie(true);
+            }
+
+
+            // command line arguments overwrite settings in configuration file
+            if (cmdLine.hasOption('z')) {
+                String sZK = cmdLine.getOptionValue('z');
+                log.info("Get cmdline zookeeper instance: " + sZK);
+                conf.setZkServers(sZK);
+            }
+
+            if (cmdLine.hasOption('m')) {
+                String sZkLedgersRootPath = cmdLine.getOptionValue('m');
+                log.info("Get cmdline zookeeper ledger path: " + sZkLedgersRootPath);
+                conf.setZkLedgersRootPath(sZkLedgersRootPath);
+            }
+
+            if (cmdLine.hasOption('p')) {
+                String sPort = cmdLine.getOptionValue('p');
+                log.info("Get cmdline bookie port: " + sPort);
+                Integer iPort = Integer.parseInt(sPort);
+                conf.setBookiePort(iPort.intValue());
+            }
+
+            if (cmdLine.hasOption('j')) {
+                String sJournalDir = cmdLine.getOptionValue('j');
+                log.info("Get cmdline journal dir: " + sJournalDir);
+                conf.setJournalDirName(sJournalDir);
+            }
+
+            if (cmdLine.hasOption('i')) {
+                String[] sIndexDirs = cmdLine.getOptionValues('i');
+                log.info("Get cmdline index dirs: ");
+                for(String index : sIndexDirs) {
+                    log.info("indexDir : " + index);
+                }
+                conf.setIndexDirName(sIndexDirs);
+            }
+
+            if (cmdLine.hasOption('l')) {
+                String[] sLedgerDirs = cmdLine.getOptionValues('l');
+                log.info("Get cmdline ledger dirs: ");
+                for(String ledger : sLedgerDirs) {
+                    log.info("ledgerdir : " + ledger);
+                }
+                conf.setLedgerDirNames(sLedgerDirs);
+            }
+
+            return conf;
+        } catch (ParseException e) {
+            log.error("Error parsing command line arguments : ", e);
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public static void main(String[] args) {
+        int retCode = doMain(args);
+        Runtime.getRuntime().exit(retCode);
+    }
+
+    static int doMain(String[] args) {
+
+        ServerConfiguration conf;
+
+        // 0. parse command line
+        try {
+            conf = parseCommandLine(args);
+        } catch (IllegalArgumentException iae) {
+            return ExitCode.INVALID_CONF;
+        }
+
+        // 1. building the component stack:
+        LifecycleComponent server;
+        try {
+            server = buildBookieServer(new BookieConfiguration(conf));
+        } catch (Exception e) {
+            log.error("Failed to build bookie server", e);
+            return ExitCode.SERVER_EXCEPTION;
+        }
+
+        // 2. start the server
+        CountDownLatch aliveLatch = new CountDownLatch(1);
+        ComponentStarter.startComponent(server, aliveLatch);
+        try {
+            aliveLatch.await();
+        } catch (InterruptedException ie) {
+            // the server is interrupted
+            log.info("Bookie server is interrupted. Exiting ...");
+        }
+        server.close();
+        return ExitCode.OK;
+    }
+
+    private static ServerConfiguration parseCommandLine(String[] args)
+            throws IllegalArgumentException {
+        ServerConfiguration conf;
+        try {
+            conf = parseArgs(args);
+        } catch (IllegalArgumentException iae) {
+            log.error("Error parsing command line arguments : ", iae);
+            System.err.println(iae.getMessage());
+            printUsage();
+            throw iae;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        String[] ledgerDirNames = conf.getLedgerDirNames();
+        for (int i = 0; i < ledgerDirNames.length; i++) {
+            if (i != 0) {
+                sb.append(',');
+            }
+            sb.append(ledgerDirNames[i]);
+        }
+
+        String hello = String.format(
+            "Hello, I'm your bookie, listening on port %1$s. ZKServers are on %2$s."
+                + " Journals are in %3$s. Ledgers are stored in %4$s.",
+            conf.getBookiePort(),
+            conf.getZkServers(),
+            Arrays.asList(conf.getJournalDirNames()),
+            sb);
+        log.info(hello);
+
+        return conf;
+    }
+
+    /**
+     * Build the bookie server.
+     *
+     * <p>The sequence of the components is:
+     *
+     * <pre>
+     * - stats provider
+     * - bookie server
+     * - autorecovery daemon
+     * - http service
+     * </pre>
+     *
+     * @param conf bookie server configuration
+     * @return lifecycle stack
+     */
+    private static LifecycleComponent buildBookieServer(BookieConfiguration conf) throws Exception {
+        LifecycleComponentStack.Builder serverBuilder = LifecycleComponentStack.newBuilder();
+
+        try {
+            // 1. build stats provider
+            StatsProviderService statsProviderService =
+                new StatsProviderService(conf);
+            StatsLogger rootStatsLogger = statsProviderService.getStatsProvider().getStatsLogger("");
+
+            serverBuilder.addComponent(statsProviderService);
+
+            // 2. build bookie server
+            BookieService bookieService =
+                new BookieService(conf, rootStatsLogger);
+
+            serverBuilder.addComponent(bookieService);
+
+            // 3. build auto recovery
+            if (conf.getServerConf().isAutoRecoveryDaemonEnabled()) {
+                AutoRecoveryService autoRecoveryService =
+                    new AutoRecoveryService(conf, rootStatsLogger.scope(REPLICATION_SCOPE));
+
+                serverBuilder.addComponent(autoRecoveryService);
+            }
+
+            // 4. build http service
+            if (conf.getServerConf().isHttpServerEnabled()) {
+                BKServiceProvider provider = new BKServiceProvider.Builder()
+                    .setBookieServer(bookieService.getServer())
+                    .setServerConfiguration(conf.getServerConf())
+                    .build();
+                HttpService httpService =
+                    new HttpService(provider, conf, rootStatsLogger);
+
+                serverBuilder.addComponent(httpService);
+            }
+        } catch (Exception e) {
+            LifecycleComponent component = serverBuilder.build();
+            component.close();
+            throw e;
+        }
+
+        return serverBuilder.build();
+    }
+
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/BookieConfiguration.java
similarity index 56%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/BookieConfiguration.java
index b0fe537..2e93126 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/BookieConfiguration.java
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -16,7 +16,24 @@
  * limitations under the License.
  */
 
+package org.apache.bookkeeper.server.conf;
+
+import org.apache.bookkeeper.common.conf.ComponentConfiguration;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+
 /**
- * Annotations used across the whole project.
+ * A bookie related {@link ComponentConfiguration}.
  */
-package org.apache.bookkeeper.common.annotation;
+public class BookieConfiguration extends ComponentConfiguration {
+
+    private final ServerConfiguration serverConf;
+
+    public BookieConfiguration(ServerConfiguration conf) {
+        super(conf, "");
+        this.serverConf = conf;
+    }
+
+    public ServerConfiguration getServerConf() {
+        return this.serverConf;
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/package-info.java
similarity index 83%
copy from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
copy to bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/package-info.java
index b0fe537..f11cdf0 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/conf/package-info.java
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -17,6 +17,6 @@
  */
 
 /**
- * Annotations used across the whole project.
+ * Server component related configuration.
  */
-package org.apache.bookkeeper.common.annotation;
+package org.apache.bookkeeper.server.conf;
\ No newline at end of file
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/package-info.java
similarity index 73%
rename from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java
rename to bookkeeper-server/src/main/java/org/apache/bookkeeper/server/package-info.java
index 08891b0..4c991bc 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/package-info.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/package-info.java
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -15,10 +15,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 /**
- * Common functions and utils used across the project.
+ * BookKeeper Server (Bookie) related components.
  *
- * <p>NOTE: refactor this package to bookkeeper-common module after 4.5</p>
+ * <p>This is the new relocation for any sever related classes. It is to prepare for separating common, protocol,
+ * client and server modules.
  */
-package org.apache.bookkeeper.common;
+package org.apache.bookkeeper.server;
\ No newline at end of file
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/AutoRecoveryService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/AutoRecoveryService.java
new file mode 100644
index 0000000..46154ff
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/AutoRecoveryService.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.service;
+
+import java.io.IOException;
+import org.apache.bookkeeper.common.component.AbstractLifecycleComponent;
+import org.apache.bookkeeper.replication.AutoRecoveryMain;
+import org.apache.bookkeeper.replication.ReplicationException.UnavailableException;
+import org.apache.bookkeeper.server.conf.BookieConfiguration;
+import org.apache.bookkeeper.stats.StatsLogger;
+
+/**
+ * A {@link org.apache.bookkeeper.common.component.LifecycleComponent} that runs autorecovery.
+ */
+public class AutoRecoveryService extends AbstractLifecycleComponent<BookieConfiguration> {
+
+    public static final String NAME = "autorecovery";
+
+    private final AutoRecoveryMain main;
+
+    public AutoRecoveryService(BookieConfiguration conf, StatsLogger statsLogger) throws Exception {
+        super(NAME, conf, statsLogger);
+        this.main = new AutoRecoveryMain(
+            conf.getServerConf(),
+            statsLogger);
+    }
+
+    @Override
+    protected void doStart() {
+        try {
+            this.main.start();
+        } catch (UnavailableException e) {
+            throw new RuntimeException("Can't not start '" + NAME + "' component.", e);
+        }
+    }
+
+    @Override
+    protected void doStop() {
+        // no-op
+    }
+
+    @Override
+    protected void doClose() throws IOException {
+        this.main.shutdown();
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/BookieService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/BookieService.java
new file mode 100644
index 0000000..c63388d
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/BookieService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.server.service;
+
+import java.io.IOException;
+import org.apache.bookkeeper.common.component.AbstractLifecycleComponent;
+import org.apache.bookkeeper.proto.BookieServer;
+import org.apache.bookkeeper.replication.ReplicationException.UnavailableException;
+import org.apache.bookkeeper.server.conf.BookieConfiguration;
+import org.apache.bookkeeper.stats.StatsLogger;
+
+/**
+ * A {@link org.apache.bookkeeper.common.component.LifecycleComponent} that starts the core bookie server.
+ */
+public class BookieService extends AbstractLifecycleComponent<BookieConfiguration> {
+
+    public static final String NAME = "bookie-server";
+
+    private final BookieServer server;
+
+    public BookieService(BookieConfiguration conf,
+                         StatsLogger statsLogger)
+            throws Exception {
+        super(NAME, conf, statsLogger);
+        this.server = new BookieServer(conf.getServerConf(), statsLogger);
+    }
+
+    public BookieServer getServer() {
+        return server;
+    }
+
+    @Override
+    protected void doStart() {
+        try {
+            this.server.start();
+        } catch (IOException | UnavailableException | InterruptedException e) {
+            throw new RuntimeException("Failed to start bookie server", e);
+        }
+    }
+
+    @Override
+    protected void doStop() {
+        // no-op
+    }
+
+    @Override
+    protected void doClose() throws IOException {
+        this.server.shutdown();
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/HttpService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/HttpService.java
new file mode 100644
index 0000000..9ba00f6
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/HttpService.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.service;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import org.apache.bookkeeper.common.component.AbstractLifecycleComponent;
+import org.apache.bookkeeper.http.BKServiceProvider;
+import org.apache.bookkeeper.http.HttpServer;
+import org.apache.bookkeeper.http.HttpServerLoader;
+import org.apache.bookkeeper.server.conf.BookieConfiguration;
+import org.apache.bookkeeper.stats.StatsLogger;
+
+/**
+ * A {@link org.apache.bookkeeper.common.component.LifecycleComponent} that runs http service.
+ */
+public class HttpService extends AbstractLifecycleComponent<BookieConfiguration> {
+
+    public static final String NAME = "http-service";
+
+    private HttpServer server;
+
+    public HttpService(BKServiceProvider provider,
+                       BookieConfiguration conf,
+                       StatsLogger statsLogger) {
+        super(NAME, conf, statsLogger);
+
+        HttpServerLoader.loadHttpServer(conf.getServerConf());
+        server = HttpServerLoader.get();
+        checkNotNull(server);
+        server.initialize(provider);
+    }
+
+    @Override
+    protected void doStart() {
+        server.startServer(conf.getServerConf().getHttpServerPort());
+    }
+
+    @Override
+    protected void doStop() {
+        // no-op
+    }
+
+    @Override
+    protected void doClose() throws IOException {
+        server.stopServer();
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/StatsProviderService.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/StatsProviderService.java
new file mode 100644
index 0000000..88fa5bd
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/StatsProviderService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.server.service;
+
+import java.io.IOException;
+import org.apache.bookkeeper.common.component.AbstractLifecycleComponent;
+import org.apache.bookkeeper.server.conf.BookieConfiguration;
+import org.apache.bookkeeper.stats.NullStatsLogger;
+import org.apache.bookkeeper.stats.StatsProvider;
+import org.apache.bookkeeper.util.ReflectionUtils;
+
+/**
+ * A {@link org.apache.bookkeeper.common.component.LifecycleComponent} that runs stats provider.
+ */
+public class StatsProviderService extends AbstractLifecycleComponent<BookieConfiguration> {
+
+    public static final String NAME = "stats-provider";
+
+    private final StatsProvider statsProvider;
+
+    public StatsProviderService(BookieConfiguration conf) throws Exception {
+        super(NAME, conf, NullStatsLogger.INSTANCE);
+
+        Class<? extends StatsProvider> statsProviderClass =
+                    conf.getServerConf().getStatsProviderClass();
+        this.statsProvider = ReflectionUtils.newInstance(statsProviderClass);
+    }
+
+    public StatsProvider getStatsProvider() {
+        return this.statsProvider;
+    }
+
+    @Override
+    protected void doStart() {
+        statsProvider.start(conf);
+    }
+
+    @Override
+    protected void doStop() {
+        statsProvider.stop();
+    }
+
+    @Override
+    protected void doClose() throws IOException {
+        // no-op
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/package-info.java
similarity index 83%
rename from bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
rename to bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/package-info.java
index b0fe537..35e10d8 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/common/annotation/package-info.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/server/service/package-info.java
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -17,6 +17,6 @@
  */
 
 /**
- * Annotations used across the whole project.
+ * Services running in a storage server (bookie).
  */
-package org.apache.bookkeeper.common.annotation;
+package org.apache.bookkeeper.server.service;
\ No newline at end of file
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
index 56fa1fc..f05975f 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperAdminTest.java
@@ -48,9 +48,9 @@ public class BookKeeperAdminTest extends BookKeeperClusterTestCase {
 
     public BookKeeperAdminTest() {
         super(numOfBookies, 480);
-        baseConf.setAutoRecoveryDaemonEnabled(true);
         baseConf.setLostBookieRecoveryDelay(lostBookieRecoveryDelayInitValue);
         baseConf.setOpenLedgerRereplicationGracePeriod(String.valueOf(30000));
+        setAutoRecoveryEnabled(true);
     }
 
     @Test
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestDeathwatcher.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestDeathwatcher.java
deleted file mode 100644
index 2899b7c..0000000
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestDeathwatcher.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.apache.bookkeeper.proto;
-
-/*
- *
- * 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.
- *
- */
-
-import org.junit.*;
-
-import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
-import org.apache.bookkeeper.conf.ServerConfiguration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests for the BookieServer death watcher
- */
-public class TestDeathwatcher extends BookKeeperClusterTestCase {
-    private final static Logger LOG = LoggerFactory.getLogger(TestDeathwatcher.class);
-
-    public TestDeathwatcher() {
-        super(1);
-    }
-
-    /**
-     * Ensure that if the autorecovery daemon is running inside the bookie
-     * then a failure/crash in the autorecovery daemon will not take down the
-     * bookie also.
-     */
-    @Test
-    public void testAutorecoveryFailureDoesntKillBookie() throws Exception {
-        ServerConfiguration conf = newServerConfiguration().setAutoRecoveryDaemonEnabled(true);
-        BookieServer bs = startBookie(conf);
-
-        assertNotNull("Autorecovery daemon should exist", bs.autoRecoveryMain);
-        assertTrue("Bookie should be running", bs.isBookieRunning());
-        bs.autoRecoveryMain.shutdown();
-        Thread.sleep(conf.getDeathWatchInterval()*2); // give deathwatcher time to run
-        assertTrue("Bookie should be running", bs.isBookieRunning());
-        bs.shutdown();
-    }
-}
-
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorRollingRestartTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorRollingRestartTest.java
index 8834ee2..2451c74 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorRollingRestartTest.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/AuditorRollingRestartTest.java
@@ -42,7 +42,7 @@ public class AuditorRollingRestartTest extends BookKeeperClusterTestCase {
     public AuditorRollingRestartTest() {
         super(3, 600);
         // run the daemon within the bookie
-        baseConf.setAutoRecoveryDaemonEnabled(true);
+        setAutoRecoveryEnabled(true);
     }
 
     /**
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/TestAutoRecoveryAlongWithBookieServers.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/TestAutoRecoveryAlongWithBookieServers.java
index 820b935..ec94321 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/TestAutoRecoveryAlongWithBookieServers.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/TestAutoRecoveryAlongWithBookieServers.java
@@ -20,12 +20,14 @@
  */
 package org.apache.bookkeeper.replication;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Map.Entry;
 import java.util.Set;
-
 import org.apache.bookkeeper.client.BookKeeper;
 import org.apache.bookkeeper.client.LedgerEntry;
 import org.apache.bookkeeper.client.LedgerHandle;
@@ -35,8 +37,6 @@ import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
 import org.apache.bookkeeper.util.BookKeeperConstants;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
-
 public class TestAutoRecoveryAlongWithBookieServers extends
         BookKeeperClusterTestCase {
 
@@ -44,7 +44,7 @@ public class TestAutoRecoveryAlongWithBookieServers extends
 
     public TestAutoRecoveryAlongWithBookieServers() {
         super(3);
-        baseConf.setAutoRecoveryDaemonEnabled(true);
+        setAutoRecoveryEnabled(true);
         basePath = baseClientConf.getZkLedgersRootPath() + '/'
                 + BookKeeperConstants.UNDER_REPLICATION_NODE
                 + BookKeeperConstants.DEFAULT_ZK_LEDGERS_ROOT_PATH;
diff --git a/pom.xml b/pom.xml
index 23b0744..f8ecc0c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,7 @@
   </ciManagement>
   <modules>
     <module>buildtools</module>
+    <module>bookkeeper-common</module>
     <module>bookkeeper-stats</module>
     <module>bookkeeper-server</module>
     <module>bookkeeper-benchmark</module>
@@ -95,10 +96,13 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <!-- dependencies -->
+    <commons-configuration.version>1.10</commons-configuration.version>
     <guava.version>20.0</guava.version>
     <hamcrest.version>1.3</hamcrest.version>
     <jmh.version>1.19</jmh.version>
     <junit.version>4.12</junit.version>
+    <lombok.version>1.16.12</lombok.version>
+    <mockito-core.version>2.8.9</mockito-core.version>
     <protobuf.version>3.4.0</protobuf.version>
     <netty.version>4.1.12.Final</netty.version>
     <netty-boringssl.version>2.0.3.Final</netty-boringssl.version>
@@ -125,6 +129,17 @@
       <artifactId>slf4j-api</artifactId>
       <version>${slf4j.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <version>${lombok.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-configuration</groupId>
+      <artifactId>commons-configuration</artifactId>
+      <version>${commons-configuration.version}</version>
+    </dependency>
     <!-- test dependencies -->
     <dependency>
       <groupId>junit</groupId>
@@ -145,6 +160,12 @@
       <version>${slf4j.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>${mockito-core.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/site/community/contributing.md b/site/community/contributing.md
index e711ff3..49792b8 100644
--- a/site/community/contributing.md
+++ b/site/community/contributing.md
@@ -102,6 +102,20 @@ Depending on your preferred development environment, you may need to prepare it
 
 ##### IntelliJ
 
+###### Enable Annotation Processing
+
+To configure annotation processing in IntelliJ:
+
+1. Open Annotation Processors Settings dialog box by going to Settings -> Build, Execution, Deployment -> Compiler -> Annotation Processors.
+1. Select the following buttons:
+    1. "Enable annotation processing"
+    1. "Obtain processors from project classpath"
+    1. "Store generated sources relative to: Module content root"
+1. Set the generated source directories to be equal to the Maven directories:
+    1. Set "Production sources directory:" to "target/generated-sources/annotations".
+    1. Set "Test sources directory:" to "target/generated-test-sources/test-annotations".
+1. Click "OK".
+
 ###### Checkstyle
 IntelliJ supports checkstyle within the IDE using the Checkstyle-IDEA plugin.
 

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

Mime
View raw message