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/30 08:34:48 UTC

[incubator-servicecomb-java-chassis] 01/04: SCB-447 cache instances by SPI type

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 862006574e7cd6d4b6abf964378565b1206146a0
Author: wujimin <wu...@huawei.com>
AuthorDate: Thu Mar 29 13:06:55 2018 +0800

    SCB-447 cache instances by SPI type
---
 .../foundation/common/utils/SPIServiceUtils.java   | 90 +++++++++++++++++-----
 .../common/utils/SPIServiceDef0Impl.java           | 22 ++++++
 .../common/utils/TestSPIServiceUtils.java          | 23 ++++++
 3 files changed, 114 insertions(+), 21 deletions(-)

diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java
index 5011abe..9cbfc86 100644
--- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java
+++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java
@@ -22,8 +22,10 @@ import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -38,31 +40,21 @@ import org.springframework.util.ReflectionUtils;
 public final class SPIServiceUtils {
   private static final Logger LOGGER = LoggerFactory.getLogger(SPIServiceUtils.class);
 
+  // load one service, maybe trigger load another service
+  // computeIfAbsent can not support this feature
+  // so use double check
+  private static final Object LOCK = new Object();
+
+  private static final Map<Class<?>, List<Object>> cache = new ConcurrentHashMap<>();
+
   private SPIServiceUtils() {
 
   }
 
   /**
-   * get target service.if target services are array,only random access to a service.
+   * no cache, return new instances every time.
    */
-  public static <T> T getTargetService(Class<T> serviceType) {
-    ServiceLoader<T> loader = ServiceLoader.load(serviceType);
-    for (T service : loader) {
-      LOGGER.info("get the SPI service success, the extend service is: {}", service.getClass());
-      return service;
-    }
-    LOGGER.info("Can not get the SPI service, the interface type is: {}", serviceType.toString());
-    return null;
-  }
-
-  public static <T> List<T> getAllService(Class<T> serviceType) {
-    List<T> list = new ArrayList<>();
-    ServiceLoader.load(serviceType).forEach(list::add);
-
-    return list;
-  }
-
-  public static <T> List<T> getSortedService(Class<T> serviceType) {
+  public static <T> List<T> loadSortedService(Class<T> serviceType) {
     List<Entry<Integer, T>> serviceEntries = new ArrayList<>();
     ServiceLoader<T> serviceLoader = ServiceLoader.load(serviceType);
     serviceLoader.forEach(service -> {
@@ -76,18 +68,74 @@ public final class SPIServiceUtils {
       serviceEntries.add(entry);
     });
 
-    return serviceEntries.stream()
+    List<T> services = serviceEntries.stream()
         .sorted(Comparator.comparingInt(Entry::getKey))
         .map(Entry::getValue)
         .collect(Collectors.toList());
+
+    LOGGER.info("Found SPI service {}, count={}.", serviceType.getName(), services.size());
+    for (int idx = 0; idx < services.size(); idx++) {
+      T service = services.get(idx);
+      LOGGER.info("  {}. {}.", idx, service.getClass().getName());
+    }
+
+    return services;
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> List<T> getOrLoadSortedService(Class<T> serviceType) {
+    List<Object> services = cache.get(serviceType);
+    if (services == null) {
+      synchronized (LOCK) {
+        services = cache.get(serviceType);
+        if (services == null) {
+          services = (List<Object>) loadSortedService(serviceType);
+          cache.put(serviceType, services);
+        }
+      }
+    }
+
+    return (List<T>) services;
+  }
+
+  /**
+   * get target service.if target services are array,only random access to a service.
+   */
+  public static <T> T getTargetService(Class<T> serviceType) {
+    List<T> services = getOrLoadSortedService(serviceType);
+    if (services.isEmpty()) {
+      LOGGER.info("Can not find SPI service for {}", serviceType.getName());
+      return null;
+    }
+
+    return services.get(0);
+  }
+
+  public static <T> List<T> getAllService(Class<T> serviceType) {
+    return getOrLoadSortedService(serviceType);
+  }
+
+  public static <T> List<T> getSortedService(Class<T> serviceType) {
+    return getOrLoadSortedService(serviceType);
   }
 
   public static <T> T getPriorityHighestService(Class<T> serviceType) {
-    List<T> services = getSortedService(serviceType);
+    List<T> services = getOrLoadSortedService(serviceType);
     if (services.isEmpty()) {
+      LOGGER.info("Can not find SPI service for {}", serviceType.getName());
       return null;
     }
 
     return services.get(0);
   }
+
+  @SuppressWarnings("unchecked")
+  public static <T, IMPL> IMPL getTargetService(Class<T> serviceType, Class<IMPL> implType) {
+    List<T> services = getOrLoadSortedService(serviceType);
+    return (IMPL) services
+        .stream()
+        .filter(service -> service.getClass().equals(implType))
+        .findFirst()
+        .orElse(null);
+  }
 }
diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java
new file mode 100644
index 0000000..08cd5a3
--- /dev/null
+++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java
@@ -0,0 +1,22 @@
+/*
+ * 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.foundation.common.utils;
+
+public class SPIServiceDef0Impl implements SPIServiceDef0 {
+
+}
diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java
index 24e81bf..2bb136b 100644
--- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java
+++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java
@@ -18,6 +18,7 @@
 package org.apache.servicecomb.foundation.common.utils;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
 
@@ -46,6 +47,19 @@ public class TestSPIServiceUtils {
   public void testGetTargetServiceNotNull() {
     SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class);
     Assert.assertTrue(SPIServiceDef.class.isInstance(service));
+
+    Assert.assertSame(service, SPIServiceUtils.getTargetService(SPIServiceDef.class));
+  }
+
+  @Test
+  public void testGetTargetService_special_null() {
+    Assert.assertNull(SPIServiceUtils.getTargetService(SPIServiceDef0.class, SPIServiceDef0Impl.class));
+  }
+
+  @Test
+  public void testGetTargetService_special_notNull() {
+    SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class, SPIServiceDefImpl.class);
+    Assert.assertTrue(SPIServiceDefImpl.class.isInstance(service));
   }
 
   @Test
@@ -68,6 +82,15 @@ public class TestSPIServiceUtils {
     };
 
     Assert.assertThat(SPIServiceUtils.getSortedService(Ordered.class), Matchers.contains(o1, o2));
+    Assert.assertThat(SPIServiceUtils.getAllService(Ordered.class), Matchers.contains(o1, o2));
     Assert.assertThat(SPIServiceUtils.getPriorityHighestService(Ordered.class), Matchers.is(o1));
+
+    Map<Class<?>, List<Object>> cache = Deencapsulation.getField(SPIServiceUtils.class, "cache");
+    cache.clear();
+  }
+
+  @Test
+  public void getPriorityHighestService_null() {
+    Assert.assertNull(SPIServiceUtils.getPriorityHighestService(SPIServiceDef0.class));
   }
 }

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