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/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/master by this push: new be2e665 [SCB-2122] consumer interface support default method (#2075) be2e665 is described below commit be2e665ce38e913eca150269508bad1f533a6a65 Author: wujimin <wuji...@huawei.com> AuthorDate: Tue Nov 24 14:31:32 2020 +0800 [SCB-2122] consumer interface support default method (#2075) --- .../servicecomb/it/testcase/TestDefaultMethod.java | 84 ++++++++++++++++++++++ .../provider/pojo/DefaultMethodMeta.java | 73 +++++++++++++++++++ .../apache/servicecomb/provider/pojo/Invoker.java | 7 ++ .../swagger/generator/core/utils/MethodUtils.java | 3 + 4 files changed, 167 insertions(+) diff --git a/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDefaultMethod.java b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDefaultMethod.java new file mode 100644 index 0000000..5fe8879 --- /dev/null +++ b/integration-tests/it-consumer/src/main/java/org/apache/servicecomb/it/testcase/TestDefaultMethod.java @@ -0,0 +1,84 @@ +/* + * 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.it.testcase; + +import static org.junit.Assert.assertEquals; + +import org.apache.servicecomb.it.Consumers; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.Test; + +import io.swagger.models.Swagger; + +public class TestDefaultMethod { + interface DataTypePojoIntf { + default int intBodyWithDefault() { + return intBody(100); + } + + int intBody(int input); + } + + private static Consumers<DataTypePojoIntf> consumersPojo = new Consumers<>("dataTypePojo", + DataTypePojoIntf.class); + + @Test + public void should_support_default_method() { + assertEquals(100, consumersPojo.getIntf().intBodyWithDefault()); + } + + @Test + public void should_generate_swagger_without_default_method() { + Swagger swagger = SwaggerGenerator.generate(DataTypePojoIntf.class); + assertEquals("---\n" + + "swagger: \"2.0\"\n" + + "info:\n" + + " version: \"1.0.0\"\n" + + " title: \"swagger definition for org.apache.servicecomb.it.testcase.TestDefaultMethod$DataTypePojoIntf\"\n" + + " x-java-interface: \"org.apache.servicecomb.it.testcase.TestDefaultMethod$DataTypePojoIntf\"\n" + + "basePath: \"/DataTypePojoIntf\"\n" + + "consumes:\n" + + "- \"application/json\"\n" + + "produces:\n" + + "- \"application/json\"\n" + + "paths:\n" + + " /intBody:\n" + + " post:\n" + + " operationId: \"intBody\"\n" + + " parameters:\n" + + " - in: \"body\"\n" + + " name: \"input\"\n" + + " required: false\n" + + " schema:\n" + + " $ref: \"#/definitions/intBodyBody\"\n" + + " responses:\n" + + " \"200\":\n" + + " description: \"response of 200\"\n" + + " schema:\n" + + " type: \"integer\"\n" + + " format: \"int32\"\n" + + "definitions:\n" + + " intBodyBody:\n" + + " type: \"object\"\n" + + " properties:\n" + + " input:\n" + + " type: \"integer\"\n" + + " format: \"int32\"\n", + SwaggerUtils.swaggerToString(swagger)); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.java new file mode 100644 index 0000000..30711af --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.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.provider.pojo; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.springframework.util.ReflectionUtils; + +public class DefaultMethodMeta { + private final Map<Method, MethodHandle> defaultMethodHandles = new ConcurrentHashMapEx<>(); + + // java11 + private final Method privateLookupIn = ReflectionUtils.findMethod(MethodHandles.class, + "privateLookupIn", Class.class, Lookup.class); + + // only for java8 + private Constructor<Lookup> lookupConstructor; + + public MethodHandle getOrCreateMethodHandle(Object proxy, Method method) { + return defaultMethodHandles.computeIfAbsent(method, key -> createMethodHandle(proxy, method)); + } + + protected MethodHandle createMethodHandle(Object proxy, Method method) { + try { + if (privateLookupIn != null) { + return createForJava11(proxy, method); + } + + return createForJava8(proxy, method); + } catch (Exception e) { + AsyncUtils.rethrow(e); + return null; + } + } + + protected MethodHandle createForJava11(Object proxy, Method method) throws Exception { + Lookup lookup = MethodHandles.lookup(); + Lookup privateLookup = (Lookup) privateLookupIn.invoke(null, method.getDeclaringClass(), lookup); + return privateLookup + .unreflectSpecial(method, method.getDeclaringClass()) + .bindTo(proxy); + } + + protected MethodHandle createForJava8(Object proxy, Method method) throws Exception { + if (lookupConstructor == null) { + lookupConstructor = ReflectionUtils.accessibleConstructor(Lookup.class, Class.class); + } + return lookupConstructor.newInstance(method.getDeclaringClass()) + .unreflectSpecial(method, method.getDeclaringClass()) + .bindTo(proxy); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java index b486cfb..cd050ac 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java @@ -36,6 +36,8 @@ public class Invoker implements InvocationHandler { protected final PojoInvocationCreator invocationCreator; + protected final DefaultMethodMeta defaultMethodMeta = new DefaultMethodMeta(); + @SuppressWarnings("unchecked") public static <T> T createProxy(String microserviceName, String schemaId, Class<?> consumerIntf) { Invoker invoker = new Invoker(microserviceName, schemaId, consumerIntf); @@ -58,6 +60,11 @@ public class Invoker implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.isDefault()) { + return defaultMethodMeta.getOrCreateMethodHandle(proxy, method) + .invokeWithArguments(args); + } + PojoConsumerMeta pojoConsumerMeta = metaRefresher.getLatestMeta(); PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); Invocation invocation = invocationCreator.create(consumerOperationMeta, args); diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java index e2d34b8..3c8bcd8 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java @@ -67,6 +67,9 @@ public class MethodUtils { * false if this method should be added in to Swagger schema */ public static boolean isSkipMethod(Class<?> cls, Method method) { + if (method.isDefault()) { + return true; + } if (method.getDeclaringClass() == Object.class) { return true; }