You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by sh...@apache.org on 2022/11/11 05:17:47 UTC
[incubator-teaclave-java-tee-sdk] 07/48: [Enc] Enclave invocation framework
This is an automated email from the ASF dual-hosted git repository.
shaojunwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-java-tee-sdk.git
commit 9ed2b341ea74544723197f66029859e764f051d5
Author: cengfeng.lzy <ce...@alibaba-inc.com>
AuthorDate: Mon Mar 14 21:03:54 2022 +0800
[Enc] Enclave invocation framework
Summary: Create new invocation framework in Enclave side.
Test Plan: all Enclave tests pass
Reviewers: lei.yul, jeffery.wsj, sanhong.lsh
Issue: https://aone.alibaba-inc.com/task/40112920
CR:
https://code.aone.alibaba-inc.com/java-tee/JavaEnclave/codereview/8038632
---
.../common/EnclaveInvocationContext.java | 6 +-
.../exception/ConfidentialComputingException.java | 5 +-
sdk/enclave/pom.xml | 33 ++++
.../enclave/framework/EnclaveContext.java | 89 +++++++++++
.../enclave/framework/EnclaveMethodInvoker.java | 15 ++
.../enclave/framework/LoadServiceInvoker.java | 25 +++
.../enclave/framework/ServiceMethodInvoker.java | 115 ++++++++++++++
.../enclave/framework/UnloadServiceInvoker.java | 24 +++
.../enclave/EnclaveTestHelper.java | 12 ++
.../framework/ServiceMethodInvokerTest.java | 170 +++++++++++++++++++++
.../enclave/framework/ServiceOperationTest.java | 49 ++++++
.../enclave/testservice/IntegerMath.java | 8 +
.../enclave/testservice/MathService.java | 13 ++
.../enclave/testservice/NumericMath.java | 28 ++++
.../enclave/testservice/Point.java | 11 ++
.../enclave/testservice/PointMath.java | 18 +++
...entialcomputing.enclave.testservice.MathService | 3 +
17 files changed, 620 insertions(+), 4 deletions(-)
diff --git a/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/EnclaveInvocationContext.java b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/EnclaveInvocationContext.java
index 9a1f3be..c0325a0 100644
--- a/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/EnclaveInvocationContext.java
+++ b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/EnclaveInvocationContext.java
@@ -3,9 +3,9 @@ package com.alibaba.confidentialcomputing.common;
import java.io.Serializable;
/**
- * EnclaveInvocationInputMeta stores a method's necessary information for reflection
- * call, include object's unique instanceIdentity、interface name、class name、method
- * signature name, and its parameters.
+ * This class stores a method's necessary information for reflection
+ * call, including the service instance's unique instanceIdentity, interface name, class name,
+ * method name and its parameters.
*/
public final class EnclaveInvocationContext implements Serializable {
private static final long serialVersionUID = 6878585714134748604L;
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/exception/ConfidentialComputingException.java b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/exception/ConfidentialComputingException.java
similarity index 85%
rename from sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/exception/ConfidentialComputingException.java
rename to sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/exception/ConfidentialComputingException.java
index f525dcf..0887a33 100644
--- a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/exception/ConfidentialComputingException.java
+++ b/sdk/common/src/main/java/com/alibaba/confidentialcomputing/common/exception/ConfidentialComputingException.java
@@ -1,4 +1,4 @@
-package com.alibaba.confidentialcomputing.enclave.exception;
+package com.alibaba.confidentialcomputing.common.exception;
/**
* ConfidentialComputingException {@link ConfidentialComputingException} is base exception in
@@ -7,6 +7,9 @@ package com.alibaba.confidentialcomputing.enclave.exception;
* Programmers need to handle ConfidentialComputingException seriously.
*/
public class ConfidentialComputingException extends Exception {
+
+ private static final long serialVersionUID = 5964126736764332957L;
+
/**
* @param info exception information.
*/
diff --git a/sdk/enclave/pom.xml b/sdk/enclave/pom.xml
index 9b47431..e533f1f 100644
--- a/sdk/enclave/pom.xml
+++ b/sdk/enclave/pom.xml
@@ -14,6 +14,21 @@
<url></url>
<build>
<plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <source>11</source>
+ <target>11</target>
+ <compilerArgs>
+ <arg>--add-modules</arg>
+ <arg>jdk.internal.vm.ci</arg>
+ <arg>--add-exports</arg>
+ <arg>jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
@@ -46,7 +61,25 @@
</plugin>
</plugins>
</build>
+ <properties>
+ <graal.version>enclave-22.0.0</graal.version>
+ </properties>
<dependencies>
+ <dependency>
+ <groupId>org.graalvm.sdk</groupId>
+ <artifactId>graal-sdk</artifactId>
+ <version>${graal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.nativeimage</groupId>
+ <artifactId>svm</artifactId>
+ <version>${graal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.nativeimage</groupId>
+ <artifactId>pointsto</artifactId>
+ <version>${graal.version}</version>
+ </dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveContext.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveContext.java
new file mode 100644
index 0000000..43a1919
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveContext.java
@@ -0,0 +1,89 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This class maintains the enclave context, i.e. the cached service instances.
+ */
+final class EnclaveContext {
+
+ private static final EnclaveContext instance = new EnclaveContext();
+
+ public static EnclaveContext getInstance() {
+ return instance;
+ }
+
+ private final Map<String, Object> cachedServiceInstances;
+
+ private final AtomicLong serviceCounter;
+
+ private EnclaveContext() {
+ cachedServiceInstances = new ConcurrentHashMap<>();
+ serviceCounter = new AtomicLong(0);
+ }
+
+ public Object removeCache(String key) {
+ return cachedServiceInstances.remove(key);
+ }
+
+ public void clearCache() {
+ cachedServiceInstances.clear();
+ }
+
+ public int servicesSize() {
+ return cachedServiceInstances.size();
+ }
+
+ /**
+ * Lookup the service instance with the given identity checksum, service name and implementation class name from
+ * cached map.
+ *
+ * @param instanceIdentity service instance identity checksum
+ * @param serviceName the name of the service
+ * @param implementationClassName the implementation class name
+ * @return cached service instance
+ */
+ public Object lookupServiceInstance(String instanceIdentity, String serviceName, String implementationClassName) throws ConfidentialComputingException {
+ if (!cachedServiceInstances.containsKey(instanceIdentity)) {
+ throw new ConfidentialComputingException(String.format("No stored service %s with identity %s", serviceName, instanceIdentity));
+ }
+ Object serviceInstance = cachedServiceInstances.get(instanceIdentity);
+ if (serviceInstance != null) {
+ Class<?> serviceInstanceClass = serviceInstance.getClass();
+ try {
+ Class<?> interfaceClass = Class.forName(serviceName);
+ if (!interfaceClass.isAssignableFrom(serviceInstanceClass)) {
+ throw new ConfidentialComputingException(String.format("Cached service instance with identity %s doesn't implement the interface %s.",
+ instanceIdentity, serviceName));
+ }
+ } catch (ClassNotFoundException e) {
+ throw new ConfidentialComputingException(String.format("Can't find the interface class %s.",
+ serviceName), e);
+ }
+
+ String cachedImplementationClassName = serviceInstanceClass.getName();
+ if (!cachedImplementationClassName.equals(implementationClassName)) {
+ throw new ConfidentialComputingException(String.format("Implementation class does not match, expected is %s, but found is %s.", implementationClassName, cachedImplementationClassName));
+ }
+ }
+ return serviceInstance;
+ }
+
+ public ServiceHandler[] loadService(Class<?> service) {
+ List<ServiceHandler> serviceHandlerList = new ArrayList<>();
+ for (Object currentServiceInstance : ServiceLoader.load(service)) {
+ String identity = String.valueOf(serviceCounter.addAndGet(1));
+ cachedServiceInstances.put(identity, currentServiceInstance);
+ serviceHandlerList.add(new ServiceHandler(service.getName(), currentServiceInstance.getClass().getName(), identity));
+ }
+ return serviceHandlerList.toArray(new ServiceHandler[0]);
+ }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveMethodInvoker.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveMethodInvoker.java
new file mode 100644
index 0000000..f174d08
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/EnclaveMethodInvoker.java
@@ -0,0 +1,15 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+
+/**
+ * There are two types of method invocations in Enclave:
+ * <p>
+ * <li>Business methods: The subclass {@link ServiceMethodInvoker} of this class takes care
+ * of the business method invocation.</li>
+ * <li>Framework methods: The SDK defined methods that run inside the enclave to maintain the framework. These methods
+ * must be static, are taken care by the subclass {@link LoadServiceInvoker}.</li>
+ */
+public interface EnclaveMethodInvoker<T> {
+ EnclaveInvocationResult callMethod(T input);
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/LoadServiceInvoker.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/LoadServiceInvoker.java
new file mode 100644
index 0000000..d0c03ae
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/LoadServiceInvoker.java
@@ -0,0 +1,25 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+
+/**
+ * This class handles loadService method invocation.
+ */
+public final class LoadServiceInvoker implements EnclaveMethodInvoker<String> {
+
+ /**
+ * Call loadService method.
+ *
+ * @param inputData name of the service to load.
+ */
+ @Override
+ public EnclaveInvocationResult callMethod(String inputData) {
+ Class<?> service;
+ try {
+ service = Class.forName(inputData);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Can't find the service interface class.", e);
+ }
+ return new EnclaveInvocationResult(EnclaveContext.getInstance().loadService(service), null);
+ }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvoker.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvoker.java
new file mode 100644
index 0000000..899ec5a
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvoker.java
@@ -0,0 +1,115 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationContext;
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+import jdk.vm.ci.meta.MetaUtil;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class handles the service method invocation. The user defined business methods that run inside the enclave follow the
+ * SPI (<a href="https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html">Service Provider Interface</a>)
+ * idiom, so they are defined in the form of service methods. This class delegates the user method invocation request
+ * wrapped in {@link EnclaveInvocationContext} to the actual method by reflection.
+ */
+public final class ServiceMethodInvoker implements EnclaveMethodInvoker<EnclaveInvocationContext> {
+
+ /**
+ * Prepare and make the target method call by reflection. Any exception thrown from method invocation is captured
+ * and saved in the returned {@link EnclaveInvocationResult}. This method can only throw exception happens at invocation
+ * preparation time.
+ *
+ * @param inputData all necessary information to reflectively invoke the target method.
+ * @return value returned by the target method invocation or the exception captured in method invocation.
+ */
+ @Override
+ public EnclaveInvocationResult callMethod(EnclaveInvocationContext inputData) {
+ Throwable throwable = null;
+ Object returnedValue = null;
+ List<Class<?>> parameterClassList = extractParamClasses(inputData.getParameterTypes());
+ ServiceHandler serviceHandler = inputData.getServiceHandler();
+ String instanceIdentity = serviceHandler.getInstanceIdentity();
+ String serviceName = serviceHandler.getServiceInterfaceName();
+ String implementationClassName = serviceHandler.getServiceImplClassName();
+ Object receiverInstance;
+ try {
+ receiverInstance = EnclaveContext.getInstance().lookupServiceInstance(instanceIdentity, serviceName, implementationClassName);
+ } catch (ConfidentialComputingException e) {
+ return new EnclaveInvocationResult(null, e);
+ }
+ if (receiverInstance != null) {
+ String methodName = inputData.getMethodName();
+ Method method;
+ // Get the public method to invoke
+ try {
+ Class<?> serviceClass = Class.forName(implementationClassName);
+ method = serviceClass.getMethod(methodName, parameterClassList.toArray(new Class<?>[0]));
+ method.setAccessible(true);
+ } catch (ReflectiveOperationException e) {
+ // Reflection exception is taken as framework's exception
+ return new EnclaveInvocationResult(null, new ConfidentialComputingException(e));
+ }
+ try {
+ // Call the actual method
+ returnedValue = method.invoke(receiverInstance, inputData.getArguments());
+ } catch (InvocationTargetException e) {
+ // The exception happens in the vocation is the user's exception, it will be returned to the user.
+ throwable = e.getCause();
+ } catch (Throwable t) {
+ return new EnclaveInvocationResult(null, new ConfidentialComputingException(t));
+ }
+ } else {
+ throwable = new ConfidentialComputingException(
+ String.format("Didn't match any service implementation with the given class name: %s", implementationClassName));
+ }
+ return new EnclaveInvocationResult(returnedValue, throwable);
+ }
+
+ private static List<Class<?>> extractParamClasses(String[] parameterTypes) {
+ List<Class<?>> parameterClassList = new ArrayList<>();
+ for (String parameterType : parameterTypes) {
+ try {
+ parameterClassList.add(nameToType(parameterType));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Can't found the specified class from parameters:", e);
+ }
+ }
+ return parameterClassList;
+ }
+
+ private static Class<?> nameToType(String typeName) throws ClassNotFoundException {
+ String name = typeName;
+ if (name.indexOf('[') != -1) {
+ /* accept "int[][]", "java.lang.String[]" */
+ name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true);
+ }
+ if (name.indexOf('.') == -1) {
+ switch (name) {
+ case "boolean":
+ return boolean.class;
+ case "char":
+ return char.class;
+ case "float":
+ return float.class;
+ case "double":
+ return double.class;
+ case "byte":
+ return byte.class;
+ case "short":
+ return short.class;
+ case "int":
+ return int.class;
+ case "long":
+ return long.class;
+ case "void":
+ return void.class;
+ }
+ }
+ return Class.forName(name);
+ }
+}
diff --git a/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/UnloadServiceInvoker.java b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/UnloadServiceInvoker.java
new file mode 100644
index 0000000..5f5fb6b
--- /dev/null
+++ b/sdk/enclave/src/main/java/com/alibaba/confidentialcomputing/enclave/framework/UnloadServiceInvoker.java
@@ -0,0 +1,24 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+
+/**
+ * This class handles the unloadService method to unload the specified service.
+ */
+public class UnloadServiceInvoker implements EnclaveMethodInvoker<ServiceHandler> {
+
+ @Override
+ public EnclaveInvocationResult callMethod(ServiceHandler inputData) {
+ Object ret = EnclaveContext.getInstance().removeCache(inputData.getInstanceIdentity());
+ Throwable t = null;
+ if (ret == null) {
+ t = new ConfidentialComputingException(String.format("No instance for service %s is found with the given identity %s", inputData.getServiceInterfaceName(),
+ inputData.getInstanceIdentity()));
+ }
+ // unloadService method's return type is void.
+ return new EnclaveInvocationResult(null, t);
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java
new file mode 100644
index 0000000..f47d64b
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/EnclaveTestHelper.java
@@ -0,0 +1,12 @@
+package com.alibaba.confidentialcomputing.enclave;
+
+import com.alibaba.confidentialcomputing.enclave.testservice.MathService;
+import com.alibaba.confidentialcomputing.enclave.testservice.NumericMath;
+
+public class EnclaveTestHelper {
+ public static final String MATH_SERVICE = MathService.class.getName();
+ public static final String NUMERIC_MATH = NumericMath.class.getName();
+ public static final String[] MATH_ADD_PARAM_TYPES = {"java.lang.Number", "java.lang.Number"};
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java
new file mode 100644
index 0000000..8b4bbe4
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceMethodInvokerTest.java
@@ -0,0 +1,170 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationContext;
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.common.exception.ConfidentialComputingException;
+import com.alibaba.confidentialcomputing.enclave.testservice.MathService;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.EMPTY_OBJECT_ARRAY;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.EMPTY_STRING_ARRAY;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_ADD_PARAM_TYPES;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.NUMERIC_MATH;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ServiceMethodInvokerTest {
+
+ private static ServiceMethodInvoker serviceMethodInvoker = new ServiceMethodInvoker();
+ private ServiceHandler[] services;
+
+ @BeforeEach
+ public void setup() {
+ services = EnclaveContext.getInstance().loadService(MathService.class);
+ assertEquals(3, services.length);
+ assertEquals(MATH_SERVICE, services[0].getServiceInterfaceName());
+ assertEquals(NUMERIC_MATH, services[0].getServiceImplClassName());
+ }
+
+ @AfterEach
+ public void tear() {
+ EnclaveContext.getInstance().clearCache();
+ }
+
+ /**
+ * Test the invocation is successfully made.
+ */
+ @Test
+ public void testSuccessCall() {
+ EnclaveInvocationResult result = callNumericAdd(services[0], 1, 2);
+ assertNotNull(result);
+ Object wrappedResult = result.getResult();
+ assertNotNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+ assertNull(result.getException());
+ assertEquals(3, (Integer) wrappedResult);
+ }
+
+ /**
+ * Test the exception thrown from service implementation is properly returned.
+ */
+ @Test
+ public void testInvocationFail() {
+ // Prepare a div(1, 0) method call which should report a divide 0 exception.
+ EnclaveInvocationResult result = callServiceImplMethod(services[0],
+ "div", MATH_ADD_PARAM_TYPES, new Object[]{1, 0});
+ assertNotNull(result);
+ Object wrappedResult = result.getResult();
+ assertNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+ Throwable e = result.getException();
+ assertNotNull(e);
+ assertTrue(e instanceof ArithmeticException);
+ }
+
+ /**
+ * Call a not exist service
+ */
+ @Test
+ public void testCallNotExistService() {
+ EnclaveInvocationResult ret = callServiceImplMethod(new ServiceHandler("MATH_SERVICE", services[0].getServiceImplClassName(), services[0].getInstanceIdentity()),
+ "add",
+ MATH_ADD_PARAM_TYPES,
+ new Object[]{1, 2});
+ assertNotNull(ret);
+ assertNull(ret.getResult());
+ assertTrue(ret.getException() instanceof ConfidentialComputingException);
+ }
+
+ /**
+ * Call a not exist service implementation
+ */
+ @Test
+ public void testCallNotExistImpl() {
+ EnclaveInvocationResult ret = callServiceImplMethod(
+ new ServiceHandler(services[0].getServiceInterfaceName(), "NUMERIC_MATH", services[0].getInstanceIdentity()),
+ "add",
+ MATH_ADD_PARAM_TYPES,
+ new Object[]{1, 2});
+ assertNotNull(ret);
+ assertNull(ret.getResult());
+ assertTrue(ret.getException() instanceof ConfidentialComputingException);
+ }
+
+ /**
+ * Call a not exist service implementation method
+ */
+ @Test
+ public void testCallNotExistMethod() {
+ EnclaveInvocationResult ret = callServiceImplMethod(services[0],
+ "add123",
+ MATH_ADD_PARAM_TYPES,
+ new Object[]{1, 2});
+ assertNotNull(ret);
+ assertNull(ret.getResult());
+ assertTrue(ret.getException() instanceof ConfidentialComputingException);
+ assertTrue(ret.getException().getCause() instanceof NoSuchMethodException);
+ }
+
+ @Test
+ public void testServiceConsistency() {
+ ServiceHandler[] secondLoadings = EnclaveContext.getInstance().loadService(MathService.class);
+ int i = 0;
+ while (i++ < 2) {
+ callNumericAdd(services[0], 1, 2);
+ }
+ callNumericAdd(secondLoadings[0], 1, 2);
+ assertEquals(2, callGetCounter(services[0]).getResult(),
+ "Add method in service instance with identity " + services[0].getInstanceIdentity() + "has been called twice, the counter should be 2");
+ assertEquals(1, callGetCounter(secondLoadings[0]).getResult(),
+ "Add method in service instance with identity " + secondLoadings[0].getInstanceIdentity() + "has been called twice, the counter should be 1");
+ }
+
+ @Test
+ public void testDefaultMethod(){
+ EnclaveInvocationResult result = callServiceImplMethod(services[0],
+ "getConstant",
+ EMPTY_STRING_ARRAY,
+ EMPTY_OBJECT_ARRAY);
+ assertNotNull(result);
+ Object wrappedResult = result.getResult();
+ assertNotNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+ assertNull(result.getException());
+ assertEquals(100, (Integer) wrappedResult);
+ }
+
+ @Test
+ public void testGrandChildMethod(){
+ EnclaveInvocationResult result = callNumericAdd(services[2], 1, 2);
+ assertNotNull(result);
+ Object wrappedResult = result.getResult();
+ assertNotNull(wrappedResult, "Expect to have non-null result from invoking service method call.");
+ assertNull(result.getException());
+ assertEquals(3, (Integer) wrappedResult);
+ }
+
+
+ private static EnclaveInvocationResult callGetCounter(ServiceHandler serviceHandler) {
+ return callServiceImplMethod(serviceHandler,
+ "getCounter",
+ EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY);
+ }
+
+ private static EnclaveInvocationResult callNumericAdd(ServiceHandler serviceHandler, int x, int y) {
+ return callServiceImplMethod(serviceHandler,
+ "add",
+ MATH_ADD_PARAM_TYPES,
+ new Object[]{x, y});
+ }
+
+ private static EnclaveInvocationResult callServiceImplMethod(ServiceHandler serviceHandler, String method,
+ String[] paramTypes, Object[] paramValues) {
+ EnclaveInvocationContext input = new EnclaveInvocationContext(serviceHandler,
+ method, paramTypes, paramValues);
+ return serviceMethodInvoker.callMethod(input);
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java
new file mode 100644
index 0000000..726e8e5
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/framework/ServiceOperationTest.java
@@ -0,0 +1,49 @@
+package com.alibaba.confidentialcomputing.enclave.framework;
+
+import com.alibaba.confidentialcomputing.common.EnclaveInvocationResult;
+import com.alibaba.confidentialcomputing.common.ServiceHandler;
+import com.alibaba.confidentialcomputing.enclave.testservice.NumericMath;
+import com.alibaba.confidentialcomputing.enclave.testservice.PointMath;
+import org.junit.jupiter.api.Test;
+
+import static com.alibaba.confidentialcomputing.enclave.EnclaveTestHelper.MATH_SERVICE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * This class tests loading and unloading service.
+ */
+public class ServiceOperationTest {
+
+ @Test
+ public void testLoadingAndUnloading() {
+ LoadServiceInvoker loadServiceInvoker = new LoadServiceInvoker();
+ // Call loadService twice, check the service identities are different.
+ EnclaveInvocationResult ret1 = loadServiceInvoker.callMethod(MATH_SERVICE);
+ EnclaveInvocationResult ret2 = loadServiceInvoker.callMethod(MATH_SERVICE);
+ ServiceHandler[] serviceHandlers1 = (ServiceHandler[]) ret1.getResult();
+ ServiceHandler[] serviceHandlers2 = (ServiceHandler[]) ret2.getResult();
+
+ // There should be two service implementation instances
+ assertEquals(3, serviceHandlers1.length);
+ // They should have the same order as defined in the service configuration file
+ assertTrue(serviceHandlers1[0].getServiceImplClassName().equals(NumericMath.class.getName()));
+ assertTrue(serviceHandlers1[1].getServiceImplClassName().equals(PointMath.class.getName()));
+
+ // The second group of service implementations should be the same
+ assertEquals(3, serviceHandlers2.length);
+ assertTrue(serviceHandlers2[0].getServiceImplClassName().equals(NumericMath.class.getName()));
+ assertTrue(serviceHandlers2[1].getServiceImplClassName().equals(PointMath.class.getName()));
+
+ // Compare the service instance identities, should be different
+ assertNotEquals(serviceHandlers1[0].getInstanceIdentity(), serviceHandlers2[0].getInstanceIdentity());
+ assertNotEquals(serviceHandlers1[1].getInstanceIdentity(), serviceHandlers2[1].getInstanceIdentity());
+
+ // There are 4 services cached in EnclaveContext, and should be 3 left after unloading one.
+ assertEquals(6, EnclaveContext.getInstance().servicesSize());
+ UnloadServiceInvoker unloadServiceInvoker = new UnloadServiceInvoker();
+ unloadServiceInvoker.callMethod(serviceHandlers1[0]);
+ assertEquals(5, EnclaveContext.getInstance().servicesSize());
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/IntegerMath.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/IntegerMath.java
new file mode 100644
index 0000000..4beed50
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/IntegerMath.java
@@ -0,0 +1,8 @@
+package com.alibaba.confidentialcomputing.enclave.testservice;
+
+public class IntegerMath extends NumericMath {
+ @Override
+ public Number add(Number x, Number y) {
+ return super.add(x, y);
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java
new file mode 100644
index 0000000..9a01c09
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/MathService.java
@@ -0,0 +1,13 @@
+package com.alibaba.confidentialcomputing.enclave.testservice;
+
+public interface MathService<T> {
+ T add(T x, T y);
+
+ T minus(T x, T y);
+
+ T div(T x, T y);
+
+ default int getConstant(){
+ return 100;
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/NumericMath.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/NumericMath.java
new file mode 100644
index 0000000..a26c861
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/NumericMath.java
@@ -0,0 +1,28 @@
+package com.alibaba.confidentialcomputing.enclave.testservice;
+
+public class NumericMath implements MathService<Number> {
+
+ private int counter = 0;
+
+ @Override
+ public Number add(Number x, Number y) {
+ counter++;
+ return x.intValue() + y.intValue();
+ }
+
+ @Override
+ public Number minus(Number x, Number y) {
+ counter++;
+ return x.intValue() - y.intValue();
+ }
+
+ @Override
+ public Number div(Number x, Number y) {
+ counter++;
+ return x.intValue() / y.intValue();
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java
new file mode 100644
index 0000000..f252d7f
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/Point.java
@@ -0,0 +1,11 @@
+package com.alibaba.confidentialcomputing.enclave.testservice;
+
+public class Point {
+ int x;
+ int y;
+
+ public Point(int x, int y){
+ this.x = x;
+ this.y = y;
+ }
+}
diff --git a/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/PointMath.java b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/PointMath.java
new file mode 100644
index 0000000..0805277
--- /dev/null
+++ b/sdk/enclave/src/test/java/com/alibaba/confidentialcomputing/enclave/testservice/PointMath.java
@@ -0,0 +1,18 @@
+package com.alibaba.confidentialcomputing.enclave.testservice;
+
+public class PointMath implements MathService<Point>{
+ @Override
+ public Point add(Point x, Point y) {
+ return new Point(x.x + y.x, x.y + y.y);
+ }
+
+ @Override
+ public Point minus(Point x, Point y) {
+ return new Point(x.x - y.x, x.y - y.y);
+ }
+
+ @Override
+ public Point div(Point x, Point y) {
+ return new Point(x.x / y.x, x.y / y.y);
+ }
+}
diff --git a/sdk/enclave/src/test/resources/META-INF/services/com.alibaba.confidentialcomputing.enclave.testservice.MathService b/sdk/enclave/src/test/resources/META-INF/services/com.alibaba.confidentialcomputing.enclave.testservice.MathService
new file mode 100644
index 0000000..4753cf1
--- /dev/null
+++ b/sdk/enclave/src/test/resources/META-INF/services/com.alibaba.confidentialcomputing.enclave.testservice.MathService
@@ -0,0 +1,3 @@
+com.alibaba.confidentialcomputing.enclave.testservice.NumericMath
+com.alibaba.confidentialcomputing.enclave.testservice.PointMath
+com.alibaba.confidentialcomputing.enclave.testservice.IntegerMath
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org