add bearer token support (#81)

diff --git a/pom.xml b/pom.xml
index e2a8639..7b9e61c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>com.baidu.hugegraph</groupId>
     <artifactId>hugegraph-common</artifactId>
-    <version>1.8.9</version>
+    <version>1.8.10</version>
 
     <name>hugegraph-common</name>
     <url>https://github.com/hugegraph/hugegraph-common</url>
diff --git a/src/main/java/com/baidu/hugegraph/perf/NormalStopwatch.java b/src/main/java/com/baidu/hugegraph/perf/NormalStopwatch.java
index c097cad..7011bca 100644
--- a/src/main/java/com/baidu/hugegraph/perf/NormalStopwatch.java
+++ b/src/main/java/com/baidu/hugegraph/perf/NormalStopwatch.java
@@ -268,7 +268,9 @@
             test.run();
             long end = PerfUtil.now();
             long cost = end - start - baseCost;
-            assert cost > 0;
+            if (cost < 0L) {
+                cost = 0L;
+            }
             long eachCost = cost / times;
 
             LOG.info("Wasted time test: cost={}ms, base_cost={}ms, {}={}ns",
diff --git a/src/main/java/com/baidu/hugegraph/rest/AbstractRestClient.java b/src/main/java/com/baidu/hugegraph/rest/AbstractRestClient.java
index 1048fc1..5c96196 100644
--- a/src/main/java/com/baidu/hugegraph/rest/AbstractRestClient.java
+++ b/src/main/java/com/baidu/hugegraph/rest/AbstractRestClient.java
@@ -19,6 +19,7 @@
 
 package com.baidu.hugegraph.rest;
 
+import java.io.IOException;
 import java.net.URI;
 import java.security.KeyManagementException;
 import java.security.SecureRandom;
@@ -38,6 +39,8 @@
 import javax.net.ssl.X509TrustManager;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.Invocation.Builder;
 import javax.ws.rs.client.WebTarget;
@@ -78,6 +81,8 @@
     // Time unit: ms
     private static final long IDLE_TIME = 40L * 1000L;
 
+    private static final String TOKEN_KEY = "tokenKey";
+
     private final Client client;
     private final WebTarget target;
 
@@ -130,6 +135,32 @@
                                      .build());
     }
 
+    public AbstractRestClient(String url, String token, int timeout) {
+        this(url, new ConfigBuilder().configTimeout(timeout)
+                                     .configToken(token)
+                                     .build());
+    }
+
+    public AbstractRestClient(String url, String token, int timeout,
+                              int maxTotal, int maxPerRoute) {
+        this(url, new ConfigBuilder().configTimeout(timeout)
+                                     .configToken(token)
+                                     .configPool(maxTotal, maxPerRoute)
+                                     .build());
+    }
+
+    public AbstractRestClient(String url, String token, int timeout,
+                              int maxTotal, int maxPerRoute,
+                              String trustStoreFile,
+                              String trustStorePassword) {
+        this(url, new ConfigBuilder().configTimeout(timeout)
+                                     .configToken(token)
+                                     .configPool(maxTotal, maxPerRoute)
+                                     .configSSL(trustStoreFile,
+                                                trustStorePassword)
+                                     .build());
+    }
+
     public AbstractRestClient(String url, ClientConfig config) {
         configConnectionManager(url, config);
         this.client = ClientBuilder.newClient(config);
@@ -519,6 +550,12 @@
             return this;
         }
 
+        public ConfigBuilder configToken(String token) {
+            this.config.property(TOKEN_KEY, token);
+            this.config.register(BearerRequestFilter.class);
+            return this;
+        }
+
         public ConfigBuilder configPool(int maxTotal, int maxPerRoute) {
             this.config.property("maxTotal", maxTotal);
             this.config.property("maxPerRoute", maxPerRoute);
@@ -547,4 +584,15 @@
             return this.config;
         }
     }
+
+    public static class BearerRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext context) throws IOException {
+            String token = context.getClient().getConfiguration()
+                                  .getProperty(TOKEN_KEY).toString();
+            context.getHeaders().add(HttpHeaders.AUTHORIZATION,
+                                     "Bearer " + token);
+        }
+    }
 }
diff --git a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
index 773eb36..81a9756 100644
--- a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
+++ b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
@@ -27,5 +27,5 @@
 
     // The second parameter of Version.of() is for all-in-one JAR
     public static final Version VERSION = Version.of(CommonVersion.class,
-                                                     "1.8.9");
+                                                     "1.8.10");
 }
diff --git a/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java b/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java
index a9f4d97..d682025 100644
--- a/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java
@@ -111,6 +111,33 @@
             this.content = "";
         }
 
+        public RestClientImpl(String url, String token,
+                              int timeout, int status) {
+            super(url, token, timeout);
+            this.status = status;
+            this.headers = ImmutableMultivaluedMap.empty();
+            this.content = "";
+        }
+
+        public RestClientImpl(String url, String token, int timeout,
+                              int maxTotal, int maxPerRoute, int status) {
+            super(url, token, timeout, maxTotal, maxPerRoute);
+            this.status = status;
+            this.headers = ImmutableMultivaluedMap.empty();
+            this.content = "";
+        }
+
+        public RestClientImpl(String url, String token, int timeout,
+                              int maxTotal, int maxPerRoute,
+                              String trustStoreFile,
+                              String trustStorePassword, int status) {
+            super(url, token, timeout, maxTotal, maxPerRoute,
+                  trustStoreFile, trustStorePassword);
+            this.status = status;
+            this.headers = ImmutableMultivaluedMap.empty();
+            this.content = "";
+        }
+
         public RestClientImpl(String url, int timeout, int status) {
             this(url, timeout, status, ImmutableMultivaluedMap.empty(), "");
         }
@@ -219,6 +246,13 @@
     }
 
     @Test
+    public void testPostWithToken() {
+        RestClient client = new RestClientImpl("/test", "token", 1000, 200);
+        RestResult restResult = client.post("path", "body");
+        Assert.assertEquals(200, restResult.status());
+    }
+
+    @Test
     public void testPostWithAllParams() {
         RestClient client = new RestClientImpl("/test", "user", "", 1000,
                                                10, 5, 200);
@@ -227,6 +261,14 @@
     }
 
     @Test
+    public void testPostWithTokenAndAllParams() {
+        RestClient client = new RestClientImpl("/test", "token", 1000,
+                                               10, 5, 200);
+        RestResult restResult = client.post("path", "body");
+        Assert.assertEquals(200, restResult.status());
+    }
+
+    @Test
     public void testPostHttpsWithAllParams() {
         String trustStoreFile = "src/test/resources/cacerts.jks";
         String trustStorePassword = "changeit";
@@ -238,6 +280,17 @@
     }
 
     @Test
+    public void testPostHttpsWithTokenAndAllParams() {
+        String trustStoreFile = "src/test/resources/cacerts.jks";
+        String trustStorePassword = "changeit";
+        RestClient client = new RestClientImpl("/test", "token", 1000,
+                                               10, 5, trustStoreFile,
+                                               trustStorePassword, 200);
+        RestResult restResult = client.post("path", "body");
+        Assert.assertEquals(200, restResult.status());
+    }
+
+    @Test
     public void testHostNameVerifier() {
         BiFunction<String, String, Boolean> verifer = (url, hostname) -> {
             AbstractRestClient.HostNameVerifier verifier;