You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/03/22 09:16:36 UTC

[incubator-servicecomb-java-chassis] 01/05: Fault-Injection handler implementation

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 28eedb4b2493430d39e82903ba87010edd0e06ef
Author: maheshrajus <ma...@huawei.com>
AuthorDate: Tue Mar 20 22:27:24 2018 +0530

    Fault-Injection handler implementation
---
 coverage-reports/pom.xml                           |   4 +
 handlers/handler-fault-injection/pom.xml           |  44 ++
 .../servicecomb/faultinjection/AbortFault.java     |  64 ++
 .../servicecomb/faultinjection/AbstractFault.java  |  25 +
 .../servicecomb/faultinjection/BeanHolder.java     |  47 ++
 .../servicecomb/faultinjection/DelayFault.java     |  81 +++
 .../apache/servicecomb/faultinjection/Fault.java   |  27 +
 .../faultinjection/FaultInjectionConfig.java       |  59 ++
 .../faultinjection/FaultInjectionConst.java        |  36 ++
 .../faultinjection/FaultInjectionHandler.java      |  72 +++
 .../faultinjection/FaultInjectionUtil.java         | 168 +++++
 .../servicecomb/faultinjection/FaultParam.java     |  37 ++
 .../servicecomb/faultinjection/FaultResponse.java  |  63 ++
 .../main/resources/META-INF/spring/cse.bean.xml}   |  37 +-
 .../src/main/resources/config/cse.handler.xml      |  21 +
 .../faultinjection/TestFaultInjectConfig.java      | 102 +++
 .../faultinjection/TestFaultInjectHandler.java     | 683 +++++++++++++++++++++
 .../faultinjection/TestFaultInjectUtil.java        |  73 +++
 handlers/pom.xml                                   |   1 +
 java-chassis-dependencies/pom.xml                  |   5 +
 java-chassis-distribution/pom.xml                  |   4 +
 21 files changed, 1629 insertions(+), 24 deletions(-)

diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml
index 27a6c0e..337ae26 100644
--- a/coverage-reports/pom.xml
+++ b/coverage-reports/pom.xml
@@ -72,6 +72,10 @@
     </dependency>
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
+      <artifactId>handler-fault-injection</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
       <artifactId>common-rest</artifactId>
     </dependency>
     <dependency>
diff --git a/handlers/handler-fault-injection/pom.xml b/handlers/handler-fault-injection/pom.xml
new file mode 100755
index 0000000..3fda7b4
--- /dev/null
+++ b/handlers/handler-fault-injection/pom.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.servicecomb</groupId>
+		<artifactId>handlers</artifactId>
+		<version>1.0.0-m2-SNAPSHOT</version>
+	</parent>
+	<artifactId>handler-fault-injection</artifactId>
+	<name>Java Chassis::Handlers::Fault Injection</name>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.servicecomb</groupId>
+			<artifactId>java-chassis-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java
new file mode 100644
index 0000000..3bf4d84
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java
@@ -0,0 +1,64 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AbortFault extends AbstractFault {
+  private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionConfig.class);
+
+  @Override
+  public FaultResponse injectFault(Invocation invocation, FaultParam faultParam) {
+    // get the config values related to delay.
+    int abortPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation,
+        "abort.percent");
+
+    if (abortPercent == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) {
+      LOGGER.info("Fault injection: Abort percentage is not configured");
+      return new FaultResponse();
+    }
+
+    // check fault abort condition.
+    boolean isAbort = FaultInjectionUtil.checkFaultInjectionDelayAndAbort(faultParam.getReqCount(), abortPercent);
+    if (isAbort) {
+      // get the config values related to delay percentage.
+      int errorCode = FaultInjectionUtil.getFaultInjectionConfig(invocation,
+          "abort.httpStatus");
+
+      if (errorCode == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) {
+        LOGGER.info("Fault injection: Abort error code is not configured");
+        return new FaultResponse();
+      }
+      // if request need to be abort then return failure with given error code
+      CommonExceptionData errorData = new CommonExceptionData("aborted by fault inject");
+      return new FaultResponse(-1, errorCode, errorData);
+    }
+
+    return new FaultResponse();
+  }
+
+  @Override
+  public int getPriority() {
+    return FaultInjectionConst.FAULTINJECTION_PRIORITY_MIN;
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java
new file mode 100644
index 0000000..db88a5e
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java
@@ -0,0 +1,25 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public abstract class AbstractFault implements Fault {
+
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java
new file mode 100644
index 0000000..756ad4b
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class BeanHolder {
+
+  @Inject
+  private List<Fault> faultInjectionFeatureList = Collections.emptyList();
+
+  public void init() {
+    //sort the fault injection feature list based on priority.
+    Collections.sort(faultInjectionFeatureList, new Comparator<Fault>() {
+
+      @Override
+      public int compare(Fault o1, Fault o2) {
+        return Integer.compare(o1.getPriority(), o2.getPriority());
+      }
+    });
+
+    FaultInjectionHandler.getFaultFeature().addAll(faultInjectionFeatureList);
+
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java
new file mode 100644
index 0000000..5ab0ed9
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java
@@ -0,0 +1,81 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import io.vertx.core.Handler;
+import io.vertx.core.Vertx;
+
+@Component
+public class DelayFault extends AbstractFault {
+  private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionHandler.class);
+
+  @Override
+  public int getPriority() {
+    return FaultInjectionConst.FAULTINJECTION_PRIORITY_MAX;
+  }
+
+  @Override
+  public FaultResponse injectFault(Invocation invocation, FaultParam faultAttributes) {
+    int delayPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation,
+        "delay.percent");
+
+    if (delayPercent == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) {
+      LOGGER.info("Fault injection: delay percentage is not configured");
+      return new FaultResponse();
+    }
+
+    // check fault delay condition.
+    boolean isDelay = FaultInjectionUtil.checkFaultInjectionDelayAndAbort(faultAttributes.getReqCount(), delayPercent);
+    if (isDelay) {
+      LOGGER.info("Fault injection: delay is added for the request by fault inject handler");
+      long delay = FaultInjectionUtil.getFaultInjectionConfig(invocation,
+          "delay.fixedDelay");
+
+      if (delay == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) {
+        LOGGER.info("Fault injection: delay is not configured");
+        return new FaultResponse();
+      }
+
+      CountDownLatch latch = new CountDownLatch(1);
+      Vertx vertx = VertxUtils.getOrCreateVertxByName("faultinjection", null);
+      vertx.setTimer(delay, new Handler<Long>() {
+        @Override
+        public void handle(Long timeID) {
+          latch.countDown();
+        }
+      });
+
+      try {
+        latch.await();
+      } catch (InterruptedException e) {
+        LOGGER.info("Interrupted exception is received");
+      }
+    }
+
+    return new FaultResponse();
+  }
+
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java
new file mode 100644
index 0000000..9d17e07
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import org.apache.servicecomb.core.Invocation;
+
+public interface Fault {
+
+  int getPriority();
+
+  FaultResponse injectFault(Invocation invocation, FaultParam faultAttributes);
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java
new file mode 100755
index 0000000..f6000e5
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java
@@ -0,0 +1,59 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.config.DynamicIntProperty;
+import com.netflix.config.DynamicPropertyFactory;
+
+/**
+ * Handles the fault injection configuration read from micro service file/config
+ * center.
+ */
+public final class FaultInjectionConfig {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionConfig.class);
+
+  // key is configuration parameter.
+  private static Map<String, String> cfgCallback = new ConcurrentHashMapEx<>();
+
+  public static int getConfigVal(String config, int defaultValue) {
+    DynamicIntProperty dynamicIntProperty = DynamicPropertyFactory.getInstance().getIntProperty(config,
+        defaultValue);
+
+    if (cfgCallback.get(config) == null) {
+      cfgCallback.put(config, config);
+      dynamicIntProperty.addCallback(() -> {
+        int newValue = dynamicIntProperty.get();
+        String cfgName = dynamicIntProperty.getName();
+
+        //store the value in config center map and check for next requests.
+        FaultInjectionUtil.setConfigCenterValue(cfgName, new AtomicInteger(newValue));
+        LOGGER.info("{} changed to {}", cfgName, newValue);
+      });
+    }
+
+    return dynamicIntProperty.get();
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java
new file mode 100644
index 0000000..fdc2a8b
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+/**
+ * Handles the all constant values for fault injection.
+ */
+public class FaultInjectionConst {
+
+  public static final int FAULT_INJECTION_CFG_NULL = -1;
+
+  public static final String CONSUMER_FAULTINJECTION = "cse.governance.Consumer.";
+
+  public static final String CONSUMER_FAULTINJECTION_GLOBAL = "cse.governance.Consumer._global.";
+
+  public static final String CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS = "policy.fault.protocols.";
+
+  public static final int FAULTINJECTION_PRIORITY_MIN = 10;
+
+  public static final int FAULTINJECTION_PRIORITY_MAX = 1;
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java
new file mode 100755
index 0000000..090b5a9
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java
@@ -0,0 +1,72 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.servicecomb.core.Handler;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.apache.servicecomb.swagger.invocation.context.HttpStatus;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+/**
+ * Fault injection handler which injects the delay/abort for requests based on
+ * the percentage configured in service file.
+ *
+ */
+
+public class FaultInjectionHandler implements Handler {
+
+  private static List<Fault> faultInjectionFeatureList = new ArrayList<>();
+
+  //added only for unit testing
+  public void setFaultFeature(List<Fault> faultFeature) {
+    faultInjectionFeatureList = faultFeature;
+  }
+
+  public static List<Fault> getFaultFeature() {
+    return faultInjectionFeatureList;
+  }
+
+  @Override
+  public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+
+    // prepare the key and lookup for request count.
+    String key = invocation.getTransport().getName() + invocation.getMicroserviceQualifiedName();
+    AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key);
+    long reqCountCurrent = reqCount.get();
+
+    // increment the request count here after checking the delay/abort condition.
+    reqCount.incrementAndGet();
+
+    for (Fault fault : faultInjectionFeatureList) {
+      FaultResponse faultResponse = fault.injectFault(invocation, new FaultParam(reqCountCurrent));
+
+      if (faultResponse.getStatusCode() != 0) {
+        asyncResp.consumerFail(new InvocationException(new HttpStatus(faultResponse.getErrorCode(),
+            "invocation exception induced by fault injection"), faultResponse.getErrorData()));
+        return;
+      }
+    }
+
+    invocation.next(asyncResp);
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java
new file mode 100755
index 0000000..2ff4abf
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java
@@ -0,0 +1,168 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION;
+import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL;
+import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS;
+import static org.apache.servicecomb.faultinjection.FaultInjectionConst.FAULT_INJECTION_CFG_NULL;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+
+/**
+ * Handles the count for all request based key[transport + microservice qualified name].
+ */
+public class FaultInjectionUtil {
+
+  // key is transport+operQualifiedName
+  private static Map<String, AtomicLong> requestCount = new ConcurrentHashMapEx<>();
+
+  // key is config paramter
+  private static Map<String, AtomicInteger> configCenterValue = new ConcurrentHashMapEx<>();
+
+  /**
+   * Returns total requests per provider for operational level.
+   * 
+   * @param key
+   *            transport+operational name
+   * @return long total requests
+   */
+  public static AtomicLong getOperMetTotalReq(String key) {
+    return requestCount.computeIfAbsent(key, p -> new AtomicLong(1));
+  }
+
+  /**
+   * Returns the map of config parameter key and values.
+   * @return value of config parameter map
+   */
+  public static Map<String, AtomicInteger> getConfigCenterMap() {
+    return configCenterValue;
+  }
+
+  /**
+   * Sets the value for given config parameter.
+   * @param key
+   * @param value
+   */
+  public static void setConfigCenterValue(String key, AtomicInteger value) {
+    configCenterValue.put(key, value);
+  }
+
+  /**
+   * Handles the reading fault injection configuration.
+   * 
+   * @param invocation
+   *            invocation of request
+   * @param key
+   *            configuration key
+   * @return configuration value
+   */
+  public static int getFaultInjectionConfig(Invocation invocation, String key) {
+    int value = 0;
+    String config;
+
+    // get the config base on priority. operationName-->schema-->service-->global
+    String operationName = invocation.getOperationName();
+    String schema = invocation.getSchemaId();
+    String serviceName = invocation.getMicroserviceName();
+
+    config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + ".operations." + operationName + "."
+        + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key;
+
+    value = getConfigValue(config);
+    if ((value != FAULT_INJECTION_CFG_NULL)) {
+      return value;
+    }
+
+    config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + "."
+        + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key;
+
+    value = getConfigValue(config);
+    if ((value != FAULT_INJECTION_CFG_NULL)) {
+      return value;
+    }
+
+    config = CONSUMER_FAULTINJECTION + serviceName + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS
+        + invocation.getTransport().getName() + "." + key;
+    value = getConfigValue(config);
+    if ((value != FAULT_INJECTION_CFG_NULL)) {
+      return value;
+    }
+
+    config = CONSUMER_FAULTINJECTION_GLOBAL + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS
+        + invocation.getTransport().getName() + "." + key;
+
+    value = getConfigValue(config);
+    return value;
+  }
+
+  /**
+   * Get the configuration value
+   * @param config config parameter
+   * @return int value
+   */
+  private static int getConfigValue(String config) {
+    int value = 0;
+    //first need to check in config center map which has high priority.
+    Map<String, AtomicInteger> cfgMap = FaultInjectionUtil.getConfigCenterMap();
+
+    if (cfgMap.containsKey(config)) {
+      return cfgMap.get(config).get();
+    }
+
+    value = FaultInjectionConfig.getConfigVal(config, FAULT_INJECTION_CFG_NULL);
+    return value;
+
+  }
+
+  /**
+   * It will check the delay/abort condition based on request count and percentage
+   * received.
+   * 
+   * @param reqCount
+   * @param percentage
+   * @param key
+   * @return true/false
+   */
+  public static boolean checkFaultInjectionDelayAndAbort(long reqCount, int percentage) {
+    /*
+     * Example: delay/abort percentage configured is 10% and Get the count(suppose
+     * if it is 10th request) from map and calculate resultNew(10th request) and
+     * requestOld(9th request). Like this for every request it will calculate
+     * current request count and previous count. if both not matched need to add
+     * delay/abort otherwise no need to add.
+     */
+
+    // calculate the value with current request count.
+    long resultNew = (reqCount * percentage) / 100;
+
+    // calculate the value with previous count value.
+    long resultOld = ((reqCount - 1) * percentage) / 100;
+
+    // if both are not matching then delay/abort should be added.
+    if (resultNew != resultOld) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java
new file mode 100644
index 0000000..0dcd19b
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java
@@ -0,0 +1,37 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+/**
+ * Fault injection parameters which decides the fault injection condition.
+ */
+public class FaultParam {
+  private long reqCount;
+
+  public long getReqCount() {
+    return reqCount;
+  }
+
+  public void setReqCount(long reqCount) {
+    this.reqCount = reqCount;
+  }
+
+  FaultParam(long reqCount) {
+    this.reqCount = reqCount;
+  }
+}
diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.java
new file mode 100644
index 0000000..ee59d71
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.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.servicecomb.faultinjection;
+
+/**
+ * Fault response which contains status of fault injection.
+ */
+public class FaultResponse {
+  private int statusCode;
+
+  private int errorCode;
+
+  private Object errorData;
+
+  FaultResponse(int statusCode, int errorCode, Object errorData) {
+    this.statusCode = statusCode;
+    this.errorCode = errorCode;
+    this.errorData = errorData;
+  }
+
+  FaultResponse() {
+  }
+
+  public int getStatusCode() {
+    return statusCode;
+  }
+
+  public void setStatusCode(int statusCode) {
+    this.statusCode = statusCode;
+  }
+
+  public int getErrorCode() {
+    return errorCode;
+  }
+
+  public void setErrorCode(int errorCode) {
+    this.errorCode = errorCode;
+  }
+
+  public Object getErrorData() {
+    return errorData;
+  }
+
+  public void setErrorData(Object errorData) {
+    this.errorData = errorData;
+  }
+
+}
diff --git a/handlers/pom.xml b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml
old mode 100644
new mode 100755
similarity index 51%
copy from handlers/pom.xml
copy to handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml
index 2afaa77..b8f5c57
--- a/handlers/pom.xml
+++ b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml
@@ -16,27 +16,16 @@
   ~ limitations under the License.
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <parent>
-    <groupId>org.apache.servicecomb</groupId>
-    <artifactId>java-chassis-parent</artifactId>
-    <version>1.0.0-m2-SNAPSHOT</version>
-    <relativePath>../parent</relativePath>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-
-  <artifactId>handlers</artifactId>
-  <name>Java Chassis::Handlers</name>
-  <packaging>pom</packaging>
-
-  <modules>
-    <module>handler-tracing-zipkin</module>
-    <module>handler-bizkeeper</module>
-    <module>handler-flowcontrol-qps</module>
-    <module>handler-loadbalance</module>
-    <module>handler-publickey-auth</module>
-  </modules>
-
-</project>
+  <beans xmlns = "http://www.springframework.org/schema/beans"
+   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
+   xmlns:context = "http://www.springframework.org/schema/context"
+   xsi:schemaLocation = "http://www.springframework.org/schema/beans
+   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+   http://www.springframework.org/schema/context
+   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+  
+  <context:annotation-config />
+	<!-- <context:spring-configured /> -->
+  <context:component-scan base-package="org.apache.servicecomb.faultinjection" />
+  <bean class="org.apache.servicecomb.faultinjection.BeanHolder" init-method="init"/>
+</beans>
diff --git a/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml b/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml
new file mode 100755
index 0000000..477963b
--- /dev/null
+++ b/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<config>
+	<handler id="fault-injection"
+		class="org.apache.servicecomb.faultinjection.FaultInjectionHandler" />
+</config>
diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java
new file mode 100644
index 0000000..f4d3137
--- /dev/null
+++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java
@@ -0,0 +1,102 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the fault injection configuration.
+ */
+public class TestFaultInjectConfig {
+  FaultInjectionConfig faultCfg;
+
+  FaultInjectionConst faultConst;
+
+  FaultInjectionUtil faultUtil;
+
+  FaultParam faultParam;
+
+  FaultResponse faultResp;
+
+  AbortFault abortFault;
+
+  DelayFault delayFault;
+
+  @Before
+  public void setUp() throws Exception {
+    faultCfg = new FaultInjectionConfig();
+    faultConst = new FaultInjectionConst();
+    faultUtil = new FaultInjectionUtil();
+    faultParam = new FaultParam(10);
+    faultResp = new FaultResponse();
+    abortFault = new AbortFault();
+    delayFault = new DelayFault();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    faultCfg = null;
+    faultConst = null;
+    faultUtil = null;
+    faultParam = null;
+  }
+
+  @Test
+  public void testFaultInjectConfig() throws Exception {
+    int val = FaultInjectionConfig.getConfigVal("cse.servicecomb.handler.consumer.faultinject.config", 0);
+    Assert.assertEquals(0, val);
+  }
+
+  @Test
+  public void testConstants() {
+    assertEquals("cse.governance.Consumer.", FaultInjectionConst.CONSUMER_FAULTINJECTION);
+    assertEquals("policy.fault.protocols.", FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS);
+    assertEquals(-1, FaultInjectionConst.FAULT_INJECTION_CFG_NULL);
+    assertEquals("cse.governance.Consumer._global.", FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL);
+    assertEquals(10, FaultInjectionConst.FAULTINJECTION_PRIORITY_MIN);
+    assertEquals(1, FaultInjectionConst.FAULTINJECTION_PRIORITY_MAX);
+  }
+
+  @Test
+  public void testFaultParam() {
+    faultParam.setReqCount(100);
+    assertEquals(100, faultParam.getReqCount());
+  }
+
+  @Test
+  public void testFaultResponse() {
+    Object obj = new Object();
+    faultResp.setErrorCode(100);
+    faultResp.setErrorData(obj);
+    faultResp.setStatusCode(123);
+    assertEquals(123, faultResp.getStatusCode());
+    assertEquals(100, faultResp.getErrorCode());
+    assertEquals(obj, faultResp.getErrorData());
+  }
+
+  @Test
+  public void testFaultPriority() {
+    assertEquals(10, abortFault.getPriority());
+    assertEquals(1, delayFault.getPriority());
+  }
+}
diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java
new file mode 100644
index 0000000..7288f78
--- /dev/null
+++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java
@@ -0,0 +1,683 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.Transport;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests the fault injection handler functionality.
+ */
+public class TestFaultInjectHandler {
+  FaultInjectionHandler handler;
+
+  Invocation invocation;
+
+  AsyncResponse asyncResp;
+
+  OperationMeta operationMeta;
+
+  private Transport transport;
+
+  Response response;
+
+
+  @InjectMocks
+  FaultInjectionHandler faultHandler;
+
+  @InjectMocks
+  AbortFault abortFault;
+
+  @InjectMocks
+  DelayFault delayFault;
+
+  AsyncResponse ar = new AsyncResponse() {
+    @Override
+    public void handle(Response resp) {
+      response = resp;
+    }
+  };
+
+
+  @Before
+  public void setUp() throws Exception {
+    handler = new FaultInjectionHandler();
+
+    invocation = Mockito.mock(Invocation.class);
+
+    asyncResp = Mockito.mock(AsyncResponse.class);
+
+    operationMeta = Mockito.mock(OperationMeta.class);
+
+    transport = Mockito.mock(Transport.class);
+    MockitoAnnotations.initMocks(this);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    handler = null;
+
+    invocation = null;
+
+    asyncResp = null;
+
+    operationMeta = null;
+
+    transport = null;
+  }
+
+  /**
+   * Tests the fault injection handler functionality with default values for
+   * highway transport.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerHighwayWithDefaultCfg() throws Exception {
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName1");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("highway");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHello");
+    Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("hello");
+
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    handler.handle(invocation, asyncResp);
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("highwayMicroserviceQualifiedName1");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with default values for rest
+   * transport.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerRestWithDefaultCfg() throws Exception {
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName2");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHello");
+    Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("hello");
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    handler.handle(invocation, asyncResp);
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName2");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with global configuration
+   * with delay/abort condition.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeGlobal() throws Exception {
+
+    System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", "5");
+    System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "10");
+    System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", "10");
+    System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName3");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHello");
+    Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("hello");
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    boolean validAssert;
+    try {
+      validAssert = true;
+      handler.handle(invocation, asyncResp);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+
+    System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent");
+    System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent");
+    System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus");
+
+    Assert.assertTrue(validAssert);
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName3");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with service level configuration
+   * with delay/abort condition.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerServiceCfgSuccess() throws Exception {
+
+    System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.fixedDelay", "1");
+    System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.percent", "10");
+    System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.percent", "10");
+    System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.httpStatus", "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName4");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHello");
+    Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts");
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    boolean validAssert;
+    try {
+      validAssert = true;
+      handler.handle(invocation, asyncResp);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+
+    System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.percent");
+    System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.percent");
+    System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.httpStatus");
+
+    Assert.assertTrue(validAssert);
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName4");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with schema level configuration
+   * with delay/abort condition.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerSchemaCfgSuccess() throws Exception {
+
+    System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.fixedDelay",
+        "1");
+    System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.percent",
+        "10");
+    System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.percent",
+        "10");
+    System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.httpStatus",
+        "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName5");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHello");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts");
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    boolean validAssert;
+    try {
+      validAssert = true;
+      handler.handle(invocation, asyncResp);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.httpStatus");
+
+    Assert.assertTrue(validAssert);
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName5");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with operation level configuration
+   * with delay/abort condition.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerOperationCfgSuccess() throws Exception {
+
+    System.setProperty(
+        "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.fixedDelay",
+        "1");
+    System.setProperty(
+        "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.percent",
+        "10");
+    System.setProperty(
+        "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.percent",
+        "10");
+    System.setProperty(
+        "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.httpStatus",
+        "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName6");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayHi");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts");
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    boolean validAssert;
+    try {
+      validAssert = true;
+      handler.handle(invocation, asyncResp);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.httpStatus");
+    Assert.assertTrue(validAssert);
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName6");
+    assertEquals(2, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with configuration change event for global level config.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeEvent1() throws Exception {
+
+    System.setProperty(
+        "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay",
+        "1");
+    System.setProperty(
+        "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus",
+        "420");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName7");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayBye1");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema1");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts1");
+    boolean validAssert;
+    long timeOld = System.currentTimeMillis();
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    try {
+      validAssert = true;
+      handler.handle(invocation, ar);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+    Assert.assertTrue(validAssert);
+    TestFaultInjectUtil
+        .updateProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", 500);
+    TestFaultInjectUtil
+        .updateProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", 421);
+
+    handler.handle(invocation, ar -> {
+      //check whether error code return, defaut is 421.
+      assertEquals(421, response.getStatusCode());
+      assertEquals(true, response.isFailed());
+      long timeNow = System.currentTimeMillis();
+      //if really time delay is added it should be greater than 5s.
+      Assert.assertTrue((timeNow - timeOld) >= 500);
+    });
+
+
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus");
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName7");
+    assertEquals(3, count.get());
+
+  }
+
+  /**
+   * Tests the fault injection handler functionality with configuration change event for operation level config.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeEvent2() throws Exception {
+
+    System.setProperty(
+        "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay",
+        "1");
+    System.setProperty(
+        "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.httpStatus",
+        "420");
+
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName8");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayBye2");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema2");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts2");
+    boolean validAssert;
+    long timeOld = System.currentTimeMillis();
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    try {
+      validAssert = true;
+      handler.handle(invocation, ar);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+    Assert.assertTrue(validAssert);
+    TestFaultInjectUtil
+        .updateProperty(
+            "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay",
+            500);
+
+    handler.handle(invocation, ar -> {
+      //check whether error code return
+      assertEquals(420, response.getStatusCode());
+      assertEquals(true, response.isFailed());
+      long timeNow = System.currentTimeMillis();
+      //if really time delay is added it should be greater than 5s.
+      Assert.assertTrue((timeNow - timeOld) >= 500);
+    });
+
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove(
+            "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.httpStatus");
+
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName8");
+    assertEquals(3, count.get());
+
+  }
+
+  /**
+   * Tests the fault injection handler functionality with configuration change event for schema level config.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeEvent3() throws Exception {
+
+    System.setProperty(
+        "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay",
+        "1");
+    System.setProperty(
+        "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.httpStatus",
+        "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName9");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayBye3");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema3");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts3");
+    boolean validAssert;
+    long timeOld = System.currentTimeMillis();
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    try {
+      validAssert = true;
+      handler.handle(invocation, ar);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+    Assert.assertTrue(validAssert);
+    TestFaultInjectUtil
+        .updateProperty(
+            "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay",
+            500);
+
+    handler.handle(invocation, ar -> {
+      //check whether error code return, defaut is 421.
+      assertEquals(421, response.getStatusCode());
+      assertEquals(true, response.isFailed());
+      long timeNow = System.currentTimeMillis();
+      //if really time delay is added it should be greater than 5s.
+      Assert.assertTrue((timeNow - timeOld) >= 500);
+    });
+
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.httpStatus");
+
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName9");
+    assertEquals(3, count.get());
+  }
+
+  /**
+   * Tests the fault injection handler functionality with configuration change event for service level config.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeEvent4() throws Exception {
+    System.setProperty("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay", "1");
+
+    System.setProperty(
+        "cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.httpStatus",
+        "421");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName10");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayBye4");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts4");
+    boolean validAssert;
+    long timeOld = System.currentTimeMillis();
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    try {
+      validAssert = true;
+      handler.handle(invocation, ar);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+    Assert.assertTrue(validAssert);
+    TestFaultInjectUtil
+        .updateProperty("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay", 500);
+
+    handler.handle(invocation, ar -> {
+      //check whether error code return,
+      assertEquals(421, response.getStatusCode());
+      assertEquals(true, response.isFailed());
+      long timeNow = System.currentTimeMillis();
+      //if really time delay is added it should be greater than 5s.
+      Assert.assertTrue((timeNow - timeOld) >= 500);
+    });
+
+
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.httpStatus");
+
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName10");
+    assertEquals(3, count.get());
+
+  }
+
+  /**
+   * Tests the fault injection handler functionality with configuration change event for service level config.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testFaultInjectHandlerConfigChangeEvent5() throws Exception {
+    System.setProperty(
+        "cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.percent",
+        "100");
+    System.setProperty(
+        "cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent",
+        "100");
+
+    Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName11");
+    Mockito.when(invocation.getTransport()).thenReturn(transport);
+    Mockito.when(transport.getName()).thenReturn("rest");
+    Mockito.when(invocation.getOperationName()).thenReturn("sayBye4");
+    Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4");
+    Mockito.when(invocation.getMicroserviceName()).thenReturn("carts5");
+    boolean validAssert;
+    long timeOld = System.currentTimeMillis();
+
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    handler.setFaultFeature(faultInjectionFeatureList);
+
+    try {
+      validAssert = true;
+      handler.handle(invocation, ar);
+    } catch (Exception e) {
+      validAssert = false;
+    }
+    Assert.assertTrue(validAssert);
+    TestFaultInjectUtil
+        .updateProperty("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent", 500);
+
+    handler.handle(invocation, ar -> {
+      //check whether error code return,
+      assertEquals(421, response.getStatusCode());
+      assertEquals(true, response.isFailed());
+      long timeNow = System.currentTimeMillis();
+      //if really time delay is added it should be greater than 5s.
+      Assert.assertTrue((timeNow - timeOld) >= 500);
+    });
+
+
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.fixedDelay");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent");
+    System.getProperties()
+        .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.httpStatus");
+
+
+    AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName11");
+    assertEquals(3, count.get());
+
+  }
+}
diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java
new file mode 100644
index 0000000..46b428e
--- /dev/null
+++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java
@@ -0,0 +1,73 @@
+/*
+ * 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.servicecomb.faultinjection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.util.ReflectionUtils;
+
+import com.netflix.config.DynamicProperty;
+
+/**
+ * Tests the fault inject util functionality.
+ */
+public class TestFaultInjectUtil {
+  private static Method updatePropertyMethod =
+      ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class);
+
+  static {
+    updatePropertyMethod.setAccessible(true);
+  }
+
+  DelayFault delayFault = Mockito.mock(DelayFault.class);
+
+  AbortFault abortFault = Mockito.mock(AbortFault.class);
+
+  public static void updateProperty(String key, Object value) {
+    ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value);
+  }
+
+  @Test
+  public void testFaultInjectUtil() throws Exception {
+    BeanHolder beanHolder = new BeanHolder();
+    List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault);
+    Field field = beanHolder.getClass().getDeclaredField("faultInjectionFeatureList");
+    field.setAccessible(true);
+    field.set(beanHolder, faultInjectionFeatureList);
+    Mockito.when(delayFault.getPriority()).thenReturn(1);
+    Mockito.when(abortFault.getPriority()).thenReturn(10);
+
+    beanHolder.init();
+    AtomicLong count1 = FaultInjectionUtil.getOperMetTotalReq("test");
+    Assert.assertEquals(1, count1.get());
+    count1.incrementAndGet();
+    AtomicLong count2 = FaultInjectionUtil.getOperMetTotalReq("test");
+    Assert.assertEquals(2, count2.get());
+    FaultInjectionUtil.setConfigCenterValue("sayHi", new AtomicInteger(123));
+    int value = FaultInjectionUtil.getConfigCenterMap().get("sayHi").get();
+    Assert.assertEquals(123, value);
+  }
+}
diff --git a/handlers/pom.xml b/handlers/pom.xml
index 2afaa77..a16e93e 100644
--- a/handlers/pom.xml
+++ b/handlers/pom.xml
@@ -36,6 +36,7 @@
     <module>handler-bizkeeper</module>
     <module>handler-flowcontrol-qps</module>
     <module>handler-loadbalance</module>
+    <module>handler-fault-injection</module>
     <module>handler-publickey-auth</module>
   </modules>
 
diff --git a/java-chassis-dependencies/pom.xml b/java-chassis-dependencies/pom.xml
index 5793296..e9ae9ec 100644
--- a/java-chassis-dependencies/pom.xml
+++ b/java-chassis-dependencies/pom.xml
@@ -836,6 +836,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.servicecomb</groupId>
+        <artifactId>handler-fault-injection</artifactId>
+        <version>1.0.0-m2-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.servicecomb</groupId>
         <artifactId>common-rest</artifactId>
         <version>1.0.0-m2-SNAPSHOT</version>
       </dependency>
diff --git a/java-chassis-distribution/pom.xml b/java-chassis-distribution/pom.xml
index 1b7f364..95b30e1 100644
--- a/java-chassis-distribution/pom.xml
+++ b/java-chassis-distribution/pom.xml
@@ -129,6 +129,10 @@
             <artifactId>handler-tracing-zipkin</artifactId>
         </dependency>
         <dependency>
+          <groupId>org.apache.servicecomb</groupId>
+          <artifactId>handler-fault-injection</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.servicecomb</groupId>
             <artifactId>provider-jaxrs</artifactId>
         </dependency>

-- 
To stop receiving notification emails like this one, please contact
liubao@apache.org.