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 <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
