diff --git a/.gitignore b/.gitignore index 1614fdc5ea..8041fee7bd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ target/ *.zip *.tar *.tar.gz +.flattened-pom.xml # eclipse ignore .settings/ diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java index 8ba22b1e8d..e340e3dbcc 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java @@ -48,7 +48,7 @@ public class RouterChain { private RouterChain(URL url) { List extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class) - .getActivateExtension(url, (String[]) null); + .getActivateExtension(url, "router"); List routers = extensionFactories.stream() .map(factory -> factory.getRouter(url)) diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java index c20c5f3abd..68c8254079 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/mock/MockInvokersSelector.java @@ -52,7 +52,7 @@ public class MockInvokersSelector extends AbstractRouter { if (invocation.getAttachments() == null) { return getNormalInvokers(invokers); } else { - String value = invocation.getAttachments().get(INVOCATION_NEED_MOCK); + String value = (String) invocation.getAttachments().get(INVOCATION_NEED_MOCK); if (value == null) { return getNormalInvokers(invokers); } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java index c96f6a225c..81792a113b 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java @@ -98,7 +98,7 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { List> result = invokers; String tag = StringUtils.isEmpty(invocation.getAttachment(Constants.TAG_KEY)) ? url.getParameter(Constants.TAG_KEY) : - invocation.getAttachment(Constants.TAG_KEY); + (String) invocation.getAttachment(Constants.TAG_KEY); // if we are requesting for a Provider with a specific tag if (StringUtils.isNotEmpty(tag)) { @@ -164,7 +164,7 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { List> result = invokers; // Dynamic param String tag = StringUtils.isEmpty(invocation.getAttachment(Constants.TAG_KEY)) ? url.getParameter(Constants.TAG_KEY) : - invocation.getAttachment(Constants.TAG_KEY); + (String) invocation.getAttachment(Constants.TAG_KEY); // Tag request if (!StringUtils.isEmpty(tag)) { result = filterInvoker(invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY))); @@ -189,7 +189,7 @@ public class TagRouter extends AbstractRouter implements ConfigurationListener { } private boolean isForceUseTag(Invocation invocation) { - return Boolean.valueOf(invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); + return Boolean.valueOf((String) invocation.getAttachment(FORCE_USE_TAG, url.getParameter(FORCE_USE_TAG, "false"))); } private List> filterInvoker(List> invokers, Predicate> predicate) { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java index db2d401eba..4f266539d2 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java @@ -237,7 +237,7 @@ public abstract class AbstractClusterInvoker implements Invoker { checkWhetherDestroyed(); // binding attachments into invocation. - Map contextAttachments = RpcContext.getContext().getAttachments(); + Map contextAttachments = RpcContext.getContext().getAttachments(); if (contextAttachments != null && contextAttachments.size() != 0) { ((RpcInvocation) invocation).addAttachments(contextAttachments); } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java index cc99a87598..80e86f9eb7 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java @@ -46,8 +46,8 @@ public class MockDirInvocation implements Invocation { return new Object[]{"aa"}; } - public Map getAttachments() { - Map attachments = new HashMap(); + public Map getAttachments() { + Map attachments = new HashMap(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); @@ -58,12 +58,12 @@ public class MockDirInvocation implements Invocation { } @Override - public void setAttachment(String key, String value) { + public void setAttachment(String key, Object value) { } @Override - public void setAttachmentIfAbsent(String key, String value) { + public void setAttachmentIfAbsent(String key, Object value) { } @@ -71,12 +71,27 @@ public class MockDirInvocation implements Invocation { return null; } - public String getAttachment(String key) { + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + public Object getAttachment(String key) { return getAttachments().get(key); } - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return getAttachments().get(key); } -} +} \ No newline at end of file diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java index 16f75ff4c0..93d9da3e5b 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java @@ -150,7 +150,7 @@ public class AbstractClusterInvokerTest { // setup attachment RpcContext.getContext().setAttachment(attachKey, attachValue); - Map attachments = RpcContext.getContext().getAttachments(); + Map attachments = RpcContext.getContext().getAttachments(); Assertions.assertTrue( attachments != null && attachments.size() == 1,"set attachment failed!"); cluster = new AbstractClusterInvoker(dic) { @@ -158,7 +158,7 @@ public class AbstractClusterInvokerTest { protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance) throws RpcException { // attachment will be bind to invocation - String value = invocation.getAttachment(attachKey); + String value = (String) invocation.getAttachment(attachKey); Assertions.assertTrue(value != null && value.equals(attachValue),"binding attachment failed!"); return null; } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java index aa27fabe45..8943b9a278 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.java @@ -1,156 +1,155 @@ -/* - * 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.dubbo.rpc.cluster.support; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.AppResponse; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.Result; -import org.apache.dubbo.rpc.RpcContext; -import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.cluster.Directory; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * ForkingClusterInvokerTest - */ -@SuppressWarnings("unchecked") -public class ForkingClusterInvokerTest { - - private List> invokers = new ArrayList>(); - private URL url = URL.valueOf("test://test:11/test?forks=2"); - private Invoker invoker1 = mock(Invoker.class); - private Invoker invoker2 = mock(Invoker.class); - private Invoker invoker3 = mock(Invoker.class); - private RpcInvocation invocation = new RpcInvocation(); - private Directory dic; - private Result result = new AppResponse(); - - @BeforeEach - public void setUp() throws Exception { - - dic = mock(Directory.class); - - given(dic.getUrl()).willReturn(url); - given(dic.list(invocation)).willReturn(invokers); - given(dic.getInterface()).willReturn(ForkingClusterInvokerTest.class); - - invocation.setMethodName("method1"); - - invokers.add(invoker1); - invokers.add(invoker2); - invokers.add(invoker3); - - } - - private void resetInvokerToException() { - given(invoker1.invoke(invocation)).willThrow(new RuntimeException()); - given(invoker1.getUrl()).willReturn(url); - given(invoker1.isAvailable()).willReturn(true); - given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); - - given(invoker2.invoke(invocation)).willThrow(new RuntimeException()); - given(invoker2.getUrl()).willReturn(url); - given(invoker2.isAvailable()).willReturn(true); - given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); - - given(invoker3.invoke(invocation)).willThrow(new RuntimeException()); - given(invoker3.getUrl()).willReturn(url); - given(invoker3.isAvailable()).willReturn(true); - given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); - } - - private void resetInvokerToNoException() { - given(invoker1.invoke(invocation)).willReturn(result); - given(invoker1.getUrl()).willReturn(url); - given(invoker1.isAvailable()).willReturn(true); - given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); - - given(invoker2.invoke(invocation)).willReturn(result); - given(invoker2.getUrl()).willReturn(url); - given(invoker2.isAvailable()).willReturn(true); - given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); - - given(invoker3.invoke(invocation)).willReturn(result); - given(invoker3.getUrl()).willReturn(url); - given(invoker3.isAvailable()).willReturn(true); - given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); - } - - @Test - public void testInvokeException() { - resetInvokerToException(); - ForkingClusterInvoker invoker = new ForkingClusterInvoker( - dic); - - try { - invoker.invoke(invocation); - Assertions.fail(); - } catch (RpcException expected) { - Assertions.assertTrue(expected.getMessage().contains("Failed to forking invoke provider")); - assertFalse(expected.getCause() instanceof RpcException); - } - } - - @Test - public void testClearRpcContext() { - resetInvokerToException(); - ForkingClusterInvoker invoker = new ForkingClusterInvoker( - dic); - - String attachKey = "attach"; - String attachValue = "value"; - - RpcContext.getContext().setAttachment(attachKey, attachValue); - - Map attachments = RpcContext.getContext().getAttachments(); - Assertions.assertTrue(attachments != null && attachments.size() == 1, "set attachment failed!"); - try { - invoker.invoke(invocation); - Assertions.fail(); - } catch (RpcException expected) { - Assertions.assertTrue(expected.getMessage().contains("Failed to forking invoke provider"), "Succeeded to forking invoke provider !"); - assertFalse(expected.getCause() instanceof RpcException); - } - Map afterInvoke = RpcContext.getContext().getAttachments(); - Assertions.assertTrue(afterInvoke != null && afterInvoke.size() == 0, "clear attachment failed!"); - } - - @Test() - public void testInvokeNoException() { - - resetInvokerToNoException(); - - ForkingClusterInvoker invoker = new ForkingClusterInvoker( - dic); - Result ret = invoker.invoke(invocation); - Assertions.assertSame(result, ret); - } - +/* + * 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.dubbo.rpc.cluster.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.AppResponse; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.cluster.Directory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * ForkingClusterInvokerTest + */ +@SuppressWarnings("unchecked") +public class ForkingClusterInvokerTest { + + private List> invokers = new ArrayList>(); + private URL url = URL.valueOf("test://test:11/test?forks=2"); + private Invoker invoker1 = mock(Invoker.class); + private Invoker invoker2 = mock(Invoker.class); + private Invoker invoker3 = mock(Invoker.class); + private RpcInvocation invocation = new RpcInvocation(); + private Directory dic; + private Result result = new AppResponse(); + + @BeforeEach + public void setUp() throws Exception { + + dic = mock(Directory.class); + + given(dic.getUrl()).willReturn(url); + given(dic.list(invocation)).willReturn(invokers); + given(dic.getInterface()).willReturn(ForkingClusterInvokerTest.class); + + invocation.setMethodName("method1"); + + invokers.add(invoker1); + invokers.add(invoker2); + invokers.add(invoker3); + + } + + private void resetInvokerToException() { + given(invoker1.invoke(invocation)).willThrow(new RuntimeException()); + given(invoker1.getUrl()).willReturn(url); + given(invoker1.isAvailable()).willReturn(true); + given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); + + given(invoker2.invoke(invocation)).willThrow(new RuntimeException()); + given(invoker2.getUrl()).willReturn(url); + given(invoker2.isAvailable()).willReturn(true); + given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); + + given(invoker3.invoke(invocation)).willThrow(new RuntimeException()); + given(invoker3.getUrl()).willReturn(url); + given(invoker3.isAvailable()).willReturn(true); + given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); + } + + private void resetInvokerToNoException() { + given(invoker1.invoke(invocation)).willReturn(result); + given(invoker1.getUrl()).willReturn(url); + given(invoker1.isAvailable()).willReturn(true); + given(invoker1.getInterface()).willReturn(ForkingClusterInvokerTest.class); + + given(invoker2.invoke(invocation)).willReturn(result); + given(invoker2.getUrl()).willReturn(url); + given(invoker2.isAvailable()).willReturn(true); + given(invoker2.getInterface()).willReturn(ForkingClusterInvokerTest.class); + + given(invoker3.invoke(invocation)).willReturn(result); + given(invoker3.getUrl()).willReturn(url); + given(invoker3.isAvailable()).willReturn(true); + given(invoker3.getInterface()).willReturn(ForkingClusterInvokerTest.class); + } + + @Test + public void testInvokeException() { + resetInvokerToException(); + ForkingClusterInvoker invoker = new ForkingClusterInvoker( + dic); + + try { + invoker.invoke(invocation); + Assertions.fail(); + } catch (RpcException expected) { + Assertions.assertTrue(expected.getMessage().contains("Failed to forking invoke provider")); + assertFalse(expected.getCause() instanceof RpcException); + } + } + + @Test + public void testClearRpcContext() { + resetInvokerToException(); + ForkingClusterInvoker invoker = new ForkingClusterInvoker( + dic); + + String attachKey = "attach"; + String attachValue = "value"; + + RpcContext.getContext().setAttachment(attachKey, attachValue); + + Map attachments = RpcContext.getContext().getAttachments(); + Assertions.assertTrue(attachments != null && attachments.size() == 1, "set attachment failed!"); + try { + invoker.invoke(invocation); + Assertions.fail(); + } catch (RpcException expected) { + Assertions.assertTrue(expected.getMessage().contains("Failed to forking invoke provider"), "Succeeded to forking invoke provider !"); + assertFalse(expected.getCause() instanceof RpcException); + } + Map afterInvoke = RpcContext.getContext().getAttachments(); + Assertions.assertTrue(afterInvoke != null && afterInvoke.size() == 0, "clear attachment failed!"); + } + + @Test() + public void testInvokeNoException() { + + resetInvokerToNoException(); + + ForkingClusterInvoker invoker = new ForkingClusterInvoker( + dic); + Result ret = invoker.invoke(invocation); + Assertions.assertSame(result, ret); + } + } \ No newline at end of file diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java index 3223b27ca8..c470e63a1e 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/MergeableClusterInvokerTest.java @@ -107,7 +107,7 @@ public class MergeableClusterInvokerTest { given(invocation.getMethodName()).willReturn("getMenu"); given(invocation.getParameterTypes()).willReturn(new Class[]{}); given(invocation.getArguments()).willReturn(new Object[]{}); - given(invocation.getAttachments()).willReturn(new HashMap()) + given(invocation.getAttachments()).willReturn(new HashMap()) ; given(invocation.getInvoker()).willReturn(firstInvoker); @@ -190,7 +190,7 @@ public class MergeableClusterInvokerTest { new Class[]{String.class, List.class}); given(invocation.getArguments()).willReturn(new Object[]{menu, menuItems}) ; - given(invocation.getAttachments()).willReturn(new HashMap()) + given(invocation.getAttachments()).willReturn(new HashMap()) ; given(invocation.getInvoker()).willReturn(firstInvoker); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/PropertiesConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/PropertiesConfiguration.java index 4f8b98620a..79ac1cbcde 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/PropertiesConfiguration.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/PropertiesConfiguration.java @@ -16,18 +16,47 @@ */ package org.apache.dubbo.common.config; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.utils.ConfigUtils; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; + /** * Configuration from system properties and dubbo.properties */ public class PropertiesConfiguration extends AbstractPrefixConfiguration { - private static final Logger logger = LoggerFactory.getLogger(PropertiesConfiguration.class); public PropertiesConfiguration(String prefix, String id) { super(prefix, id); + + ExtensionLoader propertiesProviderExtensionLoader = ExtensionLoader.getExtensionLoader(OrderedPropertiesProvider.class); + Set propertiesProviderNames = propertiesProviderExtensionLoader.getSupportedExtensions(); + if (propertiesProviderNames == null || propertiesProviderNames.isEmpty()) { + return; + } + List orderedPropertiesProviders = new ArrayList<>(); + for (String propertiesProviderName : propertiesProviderNames) { + orderedPropertiesProviders.add(propertiesProviderExtensionLoader.getExtension(propertiesProviderName)); + } + + //order the propertiesProvider according the priority descending + orderedPropertiesProviders.sort((OrderedPropertiesProvider a, OrderedPropertiesProvider b) -> { + return b.priority() - a.priority(); + }); + + //load the default properties + Properties properties = ConfigUtils.getProperties(); + + //override the properties. + for (OrderedPropertiesProvider orderedPropertiesProvider : + orderedPropertiesProviders) { + properties.putAll(orderedPropertiesProvider.initProperties()); + } + + ConfigUtils.setProperties(properties); } public PropertiesConfiguration() { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java new file mode 100644 index 0000000000..322d8d9773 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/ThreadlessExecutor.java @@ -0,0 +1,150 @@ +/* + * 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.dubbo.common.threadpool; + +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; + +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * The most important difference between this Executor and other normal Executor is that this one doesn't manage + * any thread. + * + * Tasks submitted to this executor through {@link #execute(Runnable)} will not get scheduled to a specific thread, though normal executors always do the schedule. + * Those tasks are stored in a blocking queue and will only be executed when a thead calls {@link #waitAndDrain()}, the thead executing the task + * is exactly the same as the one calling waitAndDrain. + */ +public class ThreadlessExecutor extends AbstractExecutorService { + private static final Logger logger = LoggerFactory.getLogger(ThreadlessExecutor.class.getName()); + + private final BlockingQueue queue = new LinkedBlockingQueue<>(); + + private ExecutorService sharedExecutor; + + private volatile boolean waiting = true; + + private final Object lock = new Object(); + + public ThreadlessExecutor(ExecutorService sharedExecutor) { + this.sharedExecutor = sharedExecutor; + } + + public boolean isWaiting() { + return waiting; + } + + /** + * Waits until there is a Runnable, then executes it and all queued Runnables after it. + */ + public void waitAndDrain() throws InterruptedException { + Runnable runnable = queue.take(); + + synchronized (lock) { + waiting = false; + runnable.run(); + } + + runnable = queue.poll(); + while (runnable != null) { + try { + runnable.run(); + } catch (Throwable t) { + logger.info(t); + + } + runnable = queue.poll(); + } + } + + public long waitAndDrain(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + /*long startInMs = System.currentTimeMillis(); + Runnable runnable = queue.poll(timeout, unit); + if (runnable == null) { + throw new TimeoutException(); + } + runnable.run(); + long elapsedInMs = System.currentTimeMillis() - startInMs; + long timeLeft = timeout - elapsedInMs; + if (timeLeft < 0) { + throw new TimeoutException(); + } + return timeLeft;*/ + throw new UnsupportedOperationException(); + } + + /** + * If the calling thread is still waiting for a callback task, add the task into the blocking queue to wait for schedule. + * Otherwise, submit to shared callback executor directly. + * + * @param runnable + */ + @Override + public void execute(Runnable runnable) { + synchronized (lock) { + if (!waiting) { + sharedExecutor.execute(runnable); + } else { + queue.add(runnable); + } + } + } + + /** + * tells the thread blocking on {@link #waitAndDrain()} to return, despite of the current status, to avoid endless waiting. + */ + public void notifyReturn() { + // an empty runnable task. + execute(() -> { + }); + } + + /** + * The following methods are still not supported + */ + + @Override + public void shutdown() { + + } + + @Override + public List shutdownNow() { + return null; + } + + @Override + public boolean isShutdown() { + return false; + } + + @Override + public boolean isTerminated() { + return false; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return false; + } +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java new file mode 100644 index 0000000000..327cdf18b1 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java @@ -0,0 +1,107 @@ +/* + * 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.dubbo.common.threadpool.manager; + +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.threadpool.ThreadPool; +import org.apache.dubbo.common.utils.NamedThreadFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Consider implementing {@link Licycle} to enable executors shutdown when the process stops. + */ +public class DefaultExecutorRepository implements ExecutorRepository { + private static final Logger logger = LoggerFactory.getLogger(DefaultExecutorRepository.class); + + private int DEFAULT_SCHEDULER_SIZE = Runtime.getRuntime().availableProcessors(); + + private final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true)); + + private Ring scheduledExecutors = new Ring<>(); + + private ScheduledExecutorService reconnectScheduledExecutor; + + private ConcurrentMap> data = new ConcurrentHashMap<>(); + + public DefaultExecutorRepository() { + for (int i = 0; i < DEFAULT_SCHEDULER_SIZE; i++) { + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-scheduler")); + scheduledExecutors.addItem(scheduler); + } + + reconnectScheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-reconnect-scheduler")); + } + + public ExecutorService createExecutorIfAbsent(URL url) { + String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY; + if (Constants.CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(Constants.SIDE_KEY))) { + componentKey = Constants.CONSUMER_SIDE; + } + Map executors = data.computeIfAbsent(componentKey, k -> new ConcurrentHashMap<>()); + return executors.computeIfAbsent(Integer.toString(url.getPort()), k -> (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url)); + } + + @Override + public void updateThreadpool(URL url, ExecutorService executor) { + try { + if (url.hasParameter(Constants.THREADS_KEY) + && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) { + ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; + int threads = url.getParameter(Constants.THREADS_KEY, 0); + int max = threadPoolExecutor.getMaximumPoolSize(); + int core = threadPoolExecutor.getCorePoolSize(); + if (threads > 0 && (threads != max || threads != core)) { + if (threads < core) { + threadPoolExecutor.setCorePoolSize(threads); + if (core == max) { + threadPoolExecutor.setMaximumPoolSize(threads); + } + } else { + threadPoolExecutor.setMaximumPoolSize(threads); + if (core == max) { + threadPoolExecutor.setCorePoolSize(threads); + } + } + } + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + } + + @Override + public ScheduledExecutorService nextScheduledExecutor() { + return scheduledExecutors.pollItem(); + } + + @Override + public ExecutorService getSharedExecutor() { + return SHARED_EXECUTOR; + } + +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java new file mode 100644 index 0000000000..1d95a87da7 --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/ExecutorRepository.java @@ -0,0 +1,63 @@ +/* + * 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.dubbo.common.threadpool.manager; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.SPI; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; + +/** + * + */ +@SPI("default") +public interface ExecutorRepository { + + /** + * Called by both Client and Server. TODO, consider separate these two parts. + * When the Client or Server starts for the first time, generate a new threadpool according to the parameters passed in usr. + * + * @param url + * @return + */ + ExecutorService createExecutorIfAbsent(URL url); + + /** + * Modify some of the threadpool's properties according to the url, for example, coreSize, maxSize, ... + * + * @param url + * @param executor + */ + void updateThreadpool(URL url, ExecutorService executor); + + /** + * Returns a scheduler from the scheduler list, call this method whenever you need a scheduler for a cron job. + * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicate one. + * + * @return + */ + ScheduledExecutorService nextScheduledExecutor(); + + /** + * Get the default shared threadpool. + * + * @return + */ + ExecutorService getSharedExecutor(); + +} diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/Ring.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/Ring.java new file mode 100644 index 0000000000..eb9e50a9ee --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/Ring.java @@ -0,0 +1,66 @@ +/* + * 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.dubbo.common.threadpool.manager; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +public class Ring { + + AtomicInteger count = new AtomicInteger(); + + private List itemList = new CopyOnWriteArrayList(); + + public void addItem(T t) { + if (t != null) { + itemList.add(t); + } + } + + public T pollItem() { + if (itemList.isEmpty()) { + return null; + } + if (itemList.size() == 1) { + return itemList.get(0); + } + + if (count.intValue() > Integer.MAX_VALUE - 10000) { + count.set(count.get() % itemList.size()); + } + + int index = Math.abs(count.getAndIncrement()) % itemList.size(); + return itemList.get(index); + } + + public T peekItem() { + if (itemList.isEmpty()) { + return null; + } + if (itemList.size() == 1) { + return itemList.get(0); + } + int index = Math.abs(count.get()) % itemList.size(); + return itemList.get(index); + } + + public List listItems() { + return Collections.unmodifiableList(itemList); + } +} diff --git a/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository new file mode 100644 index 0000000000..44199b0219 --- /dev/null +++ b/dubbo-common/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository @@ -0,0 +1 @@ +default=org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/MockOrderedPropertiesProvider2.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/MockOrderedPropertiesProvider2.java new file mode 100644 index 0000000000..87c416c717 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/MockOrderedPropertiesProvider2.java @@ -0,0 +1,33 @@ +/* + * 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.dubbo.common.config; + +import java.util.Properties; + +public class MockOrderedPropertiesProvider2 implements OrderedPropertiesProvider { + @Override + public int priority() { + return 1; + } + + @Override + public Properties initProperties() { + Properties properties = new Properties(); + properties.put("testKey", "999"); + return properties; + } +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java new file mode 100644 index 0000000000..30b81f0a67 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/PropertiesConfigurationTest.java @@ -0,0 +1,31 @@ +/* + * 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.dubbo.common.config; + + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PropertiesConfigurationTest { + + @Test + public void testOrderPropertiesProviders() { + PropertiesConfiguration configuration = new PropertiesConfiguration("test", null); + Assertions.assertTrue(configuration.getInternalProperty("testKey").equals("999")); + } + +} diff --git a/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider new file mode 100644 index 0000000000..d2b3463f3f --- /dev/null +++ b/dubbo-common/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.common.config.OrderedPropertiesProvider @@ -0,0 +1,2 @@ +mock1=org.apache.dubbo.common.config.MockOrderedPropertiesProvider1 +mock2=org.apache.dubbo.common.config.MockOrderedPropertiesProvider2 \ No newline at end of file diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java index d283c63b26..3188bd305c 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java @@ -30,15 +30,15 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation { } @Override - default void setAttachmentIfAbsent(String key, String value) { + default void setAttachmentIfAbsent(String key, Object value) { } @Override - default void setAttachment(String key, String value) { + default void setAttachment(String key, Object value) { } - class CompatibleInvocation implements Invocation, org.apache.dubbo.rpc.Invocation { + class CompatibleInvocation implements Invocation { private org.apache.dubbo.rpc.Invocation delegate; @@ -62,17 +62,17 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation { } @Override - public Map getAttachments() { + public Map getAttachments() { return delegate.getAttachments(); } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { return delegate.getAttachment(key); } @Override - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return delegate.getAttachment(key, defaultValue); } @@ -81,6 +81,21 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation { return new Invoker.CompatibleInvoker(delegate.getInvoker()); } + @Override + public Object put(Object key, Object value) { + return delegate.put(key, value); + } + + @Override + public Object get(Object key) { + return delegate.get(key); + } + + @Override + public Map getAttributes() { + return delegate.getAttributes(); + } + @Override public org.apache.dubbo.rpc.Invocation getOriginal() { return delegate; diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java index fb175af16f..190de395d7 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Result.java @@ -83,32 +83,32 @@ public interface Result extends org.apache.dubbo.rpc.Result { } @Override - public Map getAttachments() { + public Map getAttachments() { return delegate.getAttachments(); } @Override - public void addAttachments(Map map) { + public void addAttachments(Map map) { delegate.addAttachments(map); } @Override - public void setAttachments(Map map) { + public void setAttachments(Map map) { delegate.setAttachments(map); } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { return delegate.getAttachment(key); } @Override - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return delegate.getAttachment(key, defaultValue); } @Override - public void setAttachment(String key, String value) { + public void setAttachment(String key, Object value) { delegate.setAttachment(key, value); } } diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java index 40943544e5..0c9a82ecae 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java @@ -65,17 +65,17 @@ public class CacheTest { } @Override - public Map getAttachments() { + public Map getAttachments() { return null; } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { return null; } @Override - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return null; } @@ -83,5 +83,20 @@ public class CacheTest { public Invoker getInvoker() { return null; } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } } } diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java index a98f22e6ef..4e36863172 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java @@ -19,6 +19,7 @@ package org.apache.dubbo.filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; +import org.apache.dubbo.common.Constants; import java.util.HashMap; import java.util.Map; @@ -53,8 +54,8 @@ public class LegacyInvocation implements Invocation { return new Object[]{arg0}; } - public Map getAttachments() { - Map attachments = new HashMap(); + public Map getAttachments() { + Map attachments = new HashMap(); attachments.put(PATH_KEY, "dubbo"); attachments.put(GROUP_KEY, "dubbo"); attachments.put(VERSION_KEY, "1.0.0"); @@ -68,11 +69,26 @@ public class LegacyInvocation implements Invocation { return null; } - public String getAttachment(String key) { + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + public Object getAttachment(String key) { return getAttachments().get(key); } - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return getAttachments().get(key); } diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java index 0834746676..4c0d4eee0f 100644 --- a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java +++ b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java @@ -1,88 +1,103 @@ -/* - * 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.dubbo.service; - -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.Invoker; - -import java.util.HashMap; -import java.util.Map; - -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY; -import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; - -/** - * MockInvocation.java - */ -public class MockInvocation implements Invocation { - - private String arg0; - - public MockInvocation(String arg0) { - this.arg0 = arg0; - } - - public String getMethodName() { - return "echo"; - } - - public Class[] getParameterTypes() { - return new Class[]{String.class}; - } - - public Object[] getArguments() { - return new Object[]{arg0}; - } - - public Map getAttachments() { - Map attachments = new HashMap(); - attachments.put(PATH_KEY, "dubbo"); - attachments.put(GROUP_KEY, "dubbo"); - attachments.put(VERSION_KEY, "1.0.0"); - attachments.put(DUBBO_VERSION_KEY, "1.0.0"); - attachments.put(TOKEN_KEY, "sfag"); - attachments.put(TIMEOUT_KEY, "1000"); - return attachments; - } - - @Override - public void setAttachment(String key, String value) { - - } - - @Override - public void setAttachmentIfAbsent(String key, String value) { - - } - - public Invoker getInvoker() { - return null; - } - - public String getAttachment(String key) { - return getAttachments().get(key); - } - - public String getAttachment(String key, String defaultValue) { - return getAttachments().get(key); - } - -} +/* + * 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.dubbo.service; + +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; + +/** + * MockInvocation.java + */ +public class MockInvocation implements Invocation { + + private String arg0; + + public MockInvocation(String arg0) { + this.arg0 = arg0; + } + + public String getMethodName() { + return "echo"; + } + + public Class[] getParameterTypes() { + return new Class[]{String.class}; + } + + public Object[] getArguments() { + return new Object[]{arg0}; + } + + public Map getAttachments() { + Map attachments = new HashMap(); + attachments.put(PATH_KEY, "dubbo"); + attachments.put(GROUP_KEY, "dubbo"); + attachments.put(VERSION_KEY, "1.0.0"); + attachments.put(DUBBO_VERSION_KEY, "1.0.0"); + attachments.put(TOKEN_KEY, "sfag"); + attachments.put(TIMEOUT_KEY, "1000"); + return attachments; + } + + @Override + public void setAttachment(String key, Object value) { + + } + + @Override + public void setAttachmentIfAbsent(String key, Object value) { + + } + + public Invoker getInvoker() { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + public Object getAttachment(String key) { + return getAttachments().get(key); + } + + public Object getAttachment(String key, Object defaultValue) { + return getAttachments().get(key); + } + +} \ No newline at end of file diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AppendParametersComponent.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AppendParametersComponent.java new file mode 100644 index 0000000000..01de9bc4a6 --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AppendParametersComponent.java @@ -0,0 +1,34 @@ +/* + * 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.dubbo.config; + +import org.apache.dubbo.common.extension.SPI; + +/** + * Dynamically add some parameters / check config + */ + +@SPI +public interface AppendParametersComponent { + default void appendReferParameters(ReferenceConfig referenceConfig) { + + } + + default void appendExportParameters(ServiceConfig serviceConfig) { + + } +} diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java index 3065a638bc..96f50824ec 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java @@ -38,7 +38,9 @@ import org.apache.dubbo.rpc.cluster.directory.StaticDirectory; import org.apache.dubbo.rpc.cluster.support.ClusterUtils; import org.apache.dubbo.rpc.cluster.support.RegistryAwareCluster; import org.apache.dubbo.rpc.model.ApplicationModel; +import org.apache.dubbo.rpc.model.ConsumerMethodModel; import org.apache.dubbo.rpc.model.ConsumerModel; +import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; @@ -46,7 +48,6 @@ import org.apache.dubbo.rpc.support.ProtocolUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -172,6 +173,8 @@ public class ReferenceConfig extends AbstractReferenceConfig { */ private transient volatile boolean destroyed; + private ServiceMetadata serviceMetadata; + @SuppressWarnings("unused") private final Object finalizerGuardian = new Object() { @Override @@ -193,9 +196,13 @@ public class ReferenceConfig extends AbstractReferenceConfig { }; public ReferenceConfig() { + serviceMetadata = new ServiceMetadata(); + serviceMetadata.addAttribute("ORIGIN_CONFIG", this); } public ReferenceConfig(Reference reference) { + serviceMetadata = new ServiceMetadata(); + serviceMetadata.addAttribute("ORIGIN_CONFIG", this); appendAnnotation(Reference.class, reference); setMethods(MethodConfig.constructMethodConfig(reference.methods())); } @@ -238,6 +245,13 @@ public class ReferenceConfig extends AbstractReferenceConfig { resolveFile(); checkApplication(); checkMetadataReport(); + appendParameters(); + } + + private void appendParameters() { + URL appendParametersUrl = URL.valueOf("appendParameters://"); + List appendParametersComponents = ExtensionLoader.getExtensionLoader(AppendParametersComponent.class).getActivateExtension(appendParametersUrl, (String[]) null); + appendParametersComponents.forEach(component -> component.appendReferParameters(this)); } public synchronized T get() { @@ -275,11 +289,18 @@ public class ReferenceConfig extends AbstractReferenceConfig { } checkStubAndLocal(interfaceClass); checkMock(interfaceClass); + + //init serivceMetadata + serviceMetadata.setVersion(version); + serviceMetadata.setGroup(group); + serviceMetadata.setDefaultGroup(group); + serviceMetadata.setServiceType(getActualInterface()); + serviceMetadata.setServiceInterfaceName(interfaceName); + Map map = new HashMap(); - map.put(SIDE_KEY, CONSUMER_SIDE); - appendRuntimeParameters(map); + if (!isGeneric()) { String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { @@ -326,13 +347,29 @@ public class ReferenceConfig extends AbstractReferenceConfig { } map.put(REGISTER_IP_KEY, hostToRegistry); + serviceMetadata.getAttachments().putAll(map); + ref = createProxy(map); String serviceKey = URL.buildKey(interfaceName, group, version); - ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes)); + ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes, serviceMetadata)); + serviceMetadata.setTarget(ref); + consumerModel.getServiceMetadata().addAttribute(Constants.PROXY_CLASS_REF, ref); initialized = true; } + private Class getActualInterface() { + Class actualInterface = interfaceClass; + if (interfaceClass == GenericService.class) { + try { + actualInterface = Class.forName(interfaceName); + } catch (ClassNotFoundException e) { + // ignore + } + } + return actualInterface; + } + private ConsumerModel buildConsumerModel(String serviceKey, Map attributes) { Method[] methods = interfaceClass.getMethods(); Class serviceInterface = interfaceClass; @@ -622,6 +659,10 @@ public class ReferenceConfig extends AbstractReferenceConfig { return invoker; } + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; + } + @Override @Parameter(excluded = true) public String getPrefix() { @@ -661,4 +702,9 @@ public class ReferenceConfig extends AbstractReferenceConfig { } } } + + @Parameter(excluded = true) + public String getUniqueServiceName() { + return URL.buildKey(interfaceName, group, version); + } } diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index 19f83ccb7a..c2f9ffcaa8 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -40,6 +40,7 @@ import org.apache.dubbo.rpc.ProxyFactory; import org.apache.dubbo.rpc.cluster.ConfiguratorFactory; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceMetadata; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; @@ -112,7 +113,7 @@ public class ServiceConfig extends AbstractServiceConfig { * *
  • when the url is dubbo://224.5.6.7:1234/org.apache.dubbo.config.api.DemoService?application=dubbo-sample, then * the protocol is DubboProtocol
  • - *

    + * * Actually,when the {@link ExtensionLoader} init the {@link Protocol} instants,it will automatically wraps two * layers, and eventually will get a ProtocolFilterWrapper or ProtocolListenerWrapper */ @@ -194,10 +195,16 @@ public class ServiceConfig extends AbstractServiceConfig { */ private volatile String generic; + private ServiceMetadata serviceMetadata; + public ServiceConfig() { + serviceMetadata = new ServiceMetadata(); + serviceMetadata.addAttribute("ORIGIN_CONFIG", this); } public ServiceConfig(Service service) { + serviceMetadata = new ServiceMetadata(); + serviceMetadata.addAttribute("ORIGIN_CONFIG", this); appendAnnotation(Service.class, service); setMethods(MethodConfig.constructMethodConfig(service.methods())); } @@ -354,6 +361,13 @@ public class ServiceConfig extends AbstractServiceConfig { } checkStubAndLocal(interfaceClass); checkMock(interfaceClass); + appendParameters(); + } + + private void appendParameters() { + URL appendParametersUrl = URL.valueOf("appendParameters://"); + List appendParametersComponents = ExtensionLoader.getExtensionLoader(AppendParametersComponent.class).getActivateExtension(appendParametersUrl, (String[]) null); + appendParametersComponents.forEach(component -> component.appendExportParameters(this)); } /** @@ -368,6 +382,14 @@ public class ServiceConfig extends AbstractServiceConfig { public synchronized void export() { checkAndUpdateSubConfigs(); + //init serviceMetadata + serviceMetadata.setVersion(version); + serviceMetadata.setGroup(group); + serviceMetadata.setDefaultGroup(group); + serviceMetadata.setServiceType(interfaceClass); + serviceMetadata.setServiceInterfaceName(interfaceName); + serviceMetadata.setTarget(ref); + if (!shouldExport()) { return; } @@ -556,6 +578,9 @@ public class ServiceConfig extends AbstractServiceConfig { map.put(TOKEN_KEY, token); } } + //init serviceMetadata attachments + serviceMetadata.getAttachments().putAll(map); + // export service String host = this.findConfigedHosts(protocolConfig, registryURLs, map); Integer port = this.findConfigedPorts(protocolConfig, name, map); @@ -1032,6 +1057,10 @@ public class ServiceConfig extends AbstractServiceConfig { return urls; } + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; + } + /** * @deprecated Replace to getProtocols() */ @@ -1053,4 +1082,9 @@ public class ServiceConfig extends AbstractServiceConfig { public String getPrefix() { return DUBBO + ".service." + interfaceName; } + + @Parameter(excluded = true) + public String getUniqueServiceName() { + return URL.buildKey(interfaceName, group, version); + } } diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml index 405b29e323..04a8223bcb 100644 --- a/dubbo-config/dubbo-config-spring/pom.xml +++ b/dubbo-config/dubbo-config-spring/pom.xml @@ -128,6 +128,5 @@ tomcat-embed-core test - diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java index 357632c277..b1fa0c1406 100644 --- a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java +++ b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java @@ -48,7 +48,7 @@ public interface DynamicConfiguration extends Configuration { } - /** + /* * {@link #removeListener(String, String, ConfigurationListener)} * * @param key the key to represent a configuration diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java index 4305ed6c05..db6b7559a7 100644 --- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java +++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-consumer/src/main/java/org/apache/dubbo/demo/consumer/comp/DemoServiceComponent.java @@ -24,6 +24,8 @@ import org.apache.dubbo.demo.DemoService; import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component("demoServiceComponent") public class DemoServiceComponent implements DemoService { @Reference @@ -33,4 +35,9 @@ public class DemoServiceComponent implements DemoService { public String sayHello(String name) { return demoService.sayHello(name); } + + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } } diff --git a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index cb06537ae0..137fa23ead 100644 --- a/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -25,6 +25,8 @@ import org.apache.dubbo.rpc.RpcContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + @Service public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -35,4 +37,9 @@ public class DemoServiceImpl implements DemoService { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } + } diff --git a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index 5e2ef2350d..2f13a5c671 100644 --- a/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-api/dubbo-demo-api-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -31,4 +33,9 @@ public class DemoServiceImpl implements DemoService { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + return null; + } + } diff --git a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java index 1172c9be0f..bd9c9287de 100644 --- a/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java +++ b/dubbo-demo/dubbo-demo-interface/src/main/java/org/apache/dubbo/demo/DemoService.java @@ -16,8 +16,11 @@ */ package org.apache.dubbo.demo; +import java.util.concurrent.CompletableFuture; + public interface DemoService { String sayHello(String name); + CompletableFuture sayHelloAsync(String name); } \ No newline at end of file diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java index d0d315c9b4..e95caa6044 100644 --- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java +++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/DemoServiceImpl.java @@ -22,6 +22,8 @@ import org.apache.dubbo.rpc.RpcContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @@ -31,4 +33,14 @@ public class DemoServiceImpl implements DemoService { return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } + @Override + public CompletableFuture sayHelloAsync(String name) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return CompletableFuture.completedFuture("future return value!"); + } + } diff --git a/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java b/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java index f65eb0573d..de46dabe1a 100644 --- a/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java +++ b/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/support/jvalidation/mock/JValidatorTestTarget.java @@ -35,6 +35,12 @@ public interface JValidatorTestTarget { void someMethod5(Map map); + public void someMethod3(ValidationParameter[] parameters); + + public void someMethod4(List strings); + + public void someMethod5(Map map); + @interface Test2 { } diff --git a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java index 1622fff771..0f5d241359 100644 --- a/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java +++ b/dubbo-monitor/dubbo-monitor-api/src/main/java/org/apache/dubbo/monitor/MonitorService.java @@ -88,5 +88,4 @@ public interface MonitorService { */ List lookup(URL query); - } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java new file mode 100644 index 0000000000..9c79f15819 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/AddressListener.java @@ -0,0 +1,35 @@ +/* + * 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.dubbo.registry; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.SPI; + +import java.util.List; + +@SPI +public interface AddressListener { + + /** + * processing when receiving the address list + * + * @param addresses provider address list + * @param registryDirectoryUrl + */ + List notify(List addresses, URL registryDirectoryUrl); + +} \ No newline at end of file diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java index 692621e1e2..f122ec31f0 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java @@ -28,6 +28,7 @@ import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.configcenter.DynamicConfiguration; +import org.apache.dubbo.registry.AddressListener; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.remoting.Constants; @@ -230,6 +231,16 @@ public class RegistryDirectory extends AbstractDirectory implements Notify // providers List providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList()); + /** + * 3.x added for extend URL address + */ + ExtensionLoader addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class); + List supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null); + if (supportedListeners != null && !supportedListeners.isEmpty()) { + for (AddressListener addressListener : supportedListeners) { + providerURLs = addressListener.notify(providerURLs, getUrl()); + } + } refreshOverrideAndInvoker(providerURLs); } diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java index 7a4961bf95..e563da616c 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockChannel.java @@ -24,6 +24,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; public class MockChannel implements ExchangeChannel { @@ -93,6 +94,16 @@ public class MockChannel implements ExchangeChannel { return null; } + @Override + public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { + return null; + } + + @Override + public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { + return null; + } + public ExchangeHandler getExchangeHandler() { return null; } diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java index 1afe0d550c..ad06cd2096 100644 --- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java +++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/MockedClient.java @@ -29,6 +29,7 @@ import java.net.InetSocketAddress; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeoutException; /** @@ -81,13 +82,23 @@ public class MockedClient implements ExchangeClient { } public CompletableFuture request(Object msg) throws RemotingException { - return request(msg, 0); + return request(msg, null); } public CompletableFuture request(Object msg, int timeout) throws RemotingException { + return this.request(msg, timeout, null); + } + + @Override + public CompletableFuture request(Object msg, ExecutorService executor) throws RemotingException { + return this.request(msg, 0, executor); + } + + @Override + public CompletableFuture request(Object msg, int timeout, ExecutorService executor) throws RemotingException { this.invoked = msg; return new CompletableFuture() { - public Object get() throws InterruptedException, ExecutionException { + public Object get() throws InterruptedException, ExecutionException { return received; } diff --git a/dubbo-registry/dubbo-registry-nacos/pom.xml b/dubbo-registry/dubbo-registry-nacos/pom.xml index de374651b5..f52f862f3d 100644 --- a/dubbo-registry/dubbo-registry-nacos/pom.xml +++ b/dubbo-registry/dubbo-registry-nacos/pom.xml @@ -33,20 +33,17 @@ org.apache.dubbo dubbo-registry-api ${project.version} - true org.apache.dubbo dubbo-common ${project.version} - true com.alibaba.nacos nacos-client - true diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java index 0e4917d200..c0cf131d8e 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/ExchangeChannel.java @@ -20,6 +20,7 @@ import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.RemotingException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; /** * ExchangeChannel. (API/SPI, Prototype, ThreadSafe) @@ -33,6 +34,7 @@ public interface ExchangeChannel extends Channel { * @return response future * @throws RemotingException */ + @Deprecated CompletableFuture request(Object request) throws RemotingException; /** @@ -43,8 +45,28 @@ public interface ExchangeChannel extends Channel { * @return response future * @throws RemotingException */ + @Deprecated CompletableFuture request(Object request, int timeout) throws RemotingException; + /** + * send request. + * + * @param request + * @return response future + * @throws RemotingException + */ + CompletableFuture request(Object request, ExecutorService executor) throws RemotingException; + + /** + * send request. + * + * @param request + * @param timeout + * @return response future + * @throws RemotingException + */ + CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException; + /** * get message handler. * diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java index 568ecf1160..b0453d6135 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Response.java @@ -16,6 +16,16 @@ */ package org.apache.dubbo.remoting.exchange; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.Codec; +import org.apache.dubbo.remoting.Decodeable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + /** * Response */ @@ -173,4 +183,55 @@ public class Response { return "Response [id=" + mId + ", version=" + mVersion + ", status=" + mStatus + ", event=" + mEvent + ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]"; } + + public static class AppResult { + + protected Map attachments = new HashMap(); + + protected Object result; + + protected Throwable exception; + + public Map getAttachments() { + return attachments; + } + + public void setAttachments(Map attachments) { + this.attachments = attachments; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + + public Throwable getException() { + return exception; + } + + public void setException(Throwable exception) { + this.exception = exception; + } + } + + public static class DecodeableAppResult implements Codec, Decodeable { + + @Override + public void decode() throws Exception { + + } + + @Override + public void encode(Channel channel, OutputStream output, Object message) throws IOException { + + } + + @Override + public Object decode(Channel channel, InputStream input) throws IOException { + return null; + } + } } \ No newline at end of file diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java index 2529ac7e48..c6392da59b 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java @@ -31,9 +31,7 @@ import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; - -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; +import java.util.concurrent.ExecutorService; /** * ExchangeReceiver @@ -100,11 +98,21 @@ final class HeaderExchangeChannel implements ExchangeChannel { @Override public CompletableFuture request(Object request) throws RemotingException { - return request(request, channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT)); + return request(request, null); } @Override public CompletableFuture request(Object request, int timeout) throws RemotingException { + return request(request, timeout, null); + } + + @Override + public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { + return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT), executor); + } + + @Override + public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { if (closed) { throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } @@ -113,7 +121,7 @@ final class HeaderExchangeChannel implements ExchangeChannel { req.setVersion(Version.getProtocolVersion()); req.setTwoWay(true); req.setData(request); - DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout); + DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor); try { channel.send(req); } catch (RemotingException e) { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java index 5a010faf52..6671d1a30b 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java @@ -31,6 +31,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler; import java.net.InetSocketAddress; import java.util.Collections; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import static org.apache.dubbo.remoting.utils.UrlUtils.getHeartbeat; @@ -84,6 +85,16 @@ public class HeaderExchangeClient implements ExchangeClient { return channel.request(request, timeout); } + @Override + public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { + return channel.request(request, executor); + } + + @Override + public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { + return channel.request(request, timeout, executor); + } + @Override public ChannelHandler getChannelHandler() { return channel.getChannelHandler(); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java index e005ad533b..2f378bedc7 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java @@ -21,7 +21,7 @@ import org.apache.dubbo.common.Version; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.store.DataStore; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; @@ -50,6 +50,7 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client private final Lock connectLock = new ReentrantLock(); private final boolean needReconnect; protected volatile ExecutorService executor; + private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); public AbstractClient(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); @@ -84,11 +85,7 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t); } - - executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class) - .getDefaultExtension().get(CONSUMER_SIDE, Integer.toString(url.getPort())); - ExtensionLoader.getExtensionLoader(DataStore.class) - .getDefaultExtension().remove(CONSUMER_SIDE, Integer.toString(url.getPort())); + executor = executorRepository.createExecutorIfAbsent(url); } protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) { diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java index d1cbdbbb0e..50736ed4f8 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java @@ -20,7 +20,7 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.store.DataStore; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.ExecutorUtil; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.remoting.Channel; @@ -32,7 +32,6 @@ import org.apache.dubbo.remoting.Server; import java.net.InetSocketAddress; import java.util.Collection; import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; @@ -55,6 +54,8 @@ public abstract class AbstractServer extends AbstractEndpoint implements Server private int accepts; private int idleTimeout; + private ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension(); + public AbstractServer(URL url, ChannelHandler handler) throws RemotingException { super(url, handler); localAddress = getUrl().toInetSocketAddress(); @@ -76,9 +77,7 @@ public abstract class AbstractServer extends AbstractEndpoint implements Server throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName() + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t); } - //fixme replace this with better method - DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension(); - executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort())); + executor = executorRepository.createExecutorIfAbsent(url); } protected abstract void doOpen() throws Throwable; @@ -110,30 +109,7 @@ public abstract class AbstractServer extends AbstractEndpoint implements Server } catch (Throwable t) { logger.error(t.getMessage(), t); } - try { - if (url.hasParameter(THREADS_KEY) - && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) { - ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; - int threads = url.getParameter(THREADS_KEY, 0); - int max = threadPoolExecutor.getMaximumPoolSize(); - int core = threadPoolExecutor.getCorePoolSize(); - if (threads > 0 && (threads != max || threads != core)) { - if (threads < core) { - threadPoolExecutor.setCorePoolSize(threads); - if (core == max) { - threadPoolExecutor.setMaximumPoolSize(threads); - } - } else { - threadPoolExecutor.setMaximumPoolSize(threads); - if (core == max) { - threadPoolExecutor.setCorePoolSize(threads); - } - } - } - } - } catch (Throwable t) { - logger.error(t.getMessage(), t); - } + executorRepository.updateThreadpool(url, executor); super.setUrl(getUrl().addParameters(url.getParameters())); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java index db4c71638c..8c74fe564f 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/CodecSupport.java @@ -40,6 +40,7 @@ public class CodecSupport { private static final Logger logger = LoggerFactory.getLogger(CodecSupport.class); private static Map ID_SERIALIZATION_MAP = new HashMap(); private static Map ID_SERIALIZATIONNAME_MAP = new HashMap(); + private static Map SERIALIZATIONNAME_ID_MAP = new HashMap(); static { Set supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions(); @@ -55,6 +56,7 @@ public class CodecSupport { } ID_SERIALIZATION_MAP.put(idByte, serialization); ID_SERIALIZATIONNAME_MAP.put(idByte, name); + SERIALIZATIONNAME_ID_MAP.put(name, idByte); } } @@ -65,6 +67,10 @@ public class CodecSupport { return ID_SERIALIZATION_MAP.get(id); } + public static byte getIDByName(String name) { + return SERIALIZATIONNAME_ID_MAP.get(name); + } + public static Serialization getSerialization(URL url) { return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension( url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION)); diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java index cdf020a9eb..a709a45318 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java @@ -20,29 +20,21 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.store.DataStore; -import org.apache.dubbo.common.threadpool.ThreadPool; -import org.apache.dubbo.common.utils.NamedThreadFactory; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; -import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.exchange.Request; +import org.apache.dubbo.remoting.exchange.Response; +import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.apache.dubbo.remoting.transport.ChannelHandlerDelegate; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE; -import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; public class WrappedChannelHandler implements ChannelHandlerDelegate { protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class); - protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true)); - - protected final ExecutorService executor; - protected final ChannelHandler handler; protected final URL url; @@ -50,24 +42,10 @@ public class WrappedChannelHandler implements ChannelHandlerDelegate { public WrappedChannelHandler(ChannelHandler handler, URL url) { this.handler = handler; this.url = url; - executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url); - - String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY; - if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { - componentKey = CONSUMER_SIDE; - } - DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension(); - dataStore.put(componentKey, Integer.toString(url.getPort()), executor); } public void close() { - try { - if (executor != null) { - executor.shutdown(); - } - } catch (Throwable t) { - logger.warn("fail to destroy thread pool of server: " + t.getMessage(), t); - } + } @Override @@ -95,8 +73,16 @@ public class WrappedChannelHandler implements ChannelHandlerDelegate { handler.caught(channel, exception); } - public ExecutorService getExecutor() { - return executor; + protected void sendFeedback(Channel channel, Request request, Throwable t) throws RemotingException { + if (request.isTwoWay()) { + String msg = "Server side(" + url.getIp() + "," + url.getPort() + + ") thread pool is exhausted, detail msg:" + t.getMessage(); + Response response = new Response(request.getId(), request.getVersion()); + response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); + response.setErrorMessage(msg); + channel.send(response); + return; + } } @Override @@ -112,8 +98,45 @@ public class WrappedChannelHandler implements ChannelHandlerDelegate { return url; } + /** + * Currently, this method is mainly customized to facilitate the thread model on consumer side. + * 1. Use ThreadlessExecutor, aka., delegate callback directly to the thread initiating the call. + * 2. Use shared executor to execute the callback. + * + * @param msg + * @return + */ + public ExecutorService getPreferredExecutorService(Object msg) { + if (msg instanceof Response) { + Response response = (Response) msg; + DefaultFuture responseFuture = DefaultFuture.getFuture(response.getId()); + // a typical scenario is the response returned after timeout, the timeout response may has completed the future + if (responseFuture == null) { + return getSharedExecutorService(); + } else { + ExecutorService executor = responseFuture.getExecutor(); + if (executor == null || executor.isShutdown()) { + executor = getSharedExecutorService(); + } + return executor; + } + } else { + return getSharedExecutorService(); + } + } + + /** + * get the shared executor for current Server or Client + * + * @return + */ + public ExecutorService getSharedExecutorService() { + return ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension().createExecutorIfAbsent(url); + } + + @Deprecated public ExecutorService getExecutorService() { - return executor == null || executor.isShutdown() ? SHARED_EXECUTOR : executor; + return getSharedExecutorService(); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java index 3ea9d40a67..25158d0d21 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java @@ -22,7 +22,6 @@ import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; -import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; @@ -58,22 +57,13 @@ public class AllChannelHandler extends WrappedChannelHandler { @Override public void received(Channel channel, Object message) throws RemotingException { - ExecutorService executor = getExecutorService(); + ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { - //TODO A temporary solution to the problem that the exception information can not be sent to the opposite end after the thread pool is full. Need a refactoring - //fix The thread pool is full, refuses to call, does not return, and causes the consumer to wait for time out if(message instanceof Request && t instanceof RejectedExecutionException){ - Request request = (Request)message; - if(request.isTwoWay()){ - String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage(); - Response response = new Response(request.getId(), request.getVersion()); - response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); - response.setErrorMessage(msg); - channel.send(response); - return; - } + sendFeedback(channel, (Request) message, t); + return; } throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java index 33bc2791bb..2132cac9d1 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java @@ -24,7 +24,6 @@ import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; -import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; @@ -80,21 +79,13 @@ public class ConnectionOrderedChannelHandler extends WrappedChannelHandler { @Override public void received(Channel channel, Object message) throws RemotingException { - ExecutorService executor = getExecutorService(); + ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { - //fix, reject exception can not be sent to consumer because thread pool is full, resulting in consumers waiting till timeout. if (message instanceof Request && t instanceof RejectedExecutionException) { - Request request = (Request) message; - if (request.isTwoWay()) { - String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage(); - Response response = new Response(request.getId(), request.getVersion()); - response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); - response.setErrorMessage(msg); - channel.send(response); - return; - } + sendFeedback(channel, (Request) message, t); + return; } throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectChannelHandler.java new file mode 100644 index 0000000000..9bb8d1f40f --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectChannelHandler.java @@ -0,0 +1,51 @@ +/* + * 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.dubbo.remoting.transport.dispatcher.direct; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.ChannelHandler; +import org.apache.dubbo.remoting.ExecutionException; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; +import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; +import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; + +import java.util.concurrent.ExecutorService; + +public class DirectChannelHandler extends WrappedChannelHandler { + + public DirectChannelHandler(ChannelHandler handler, URL url) { + super(handler, url); + } + + @Override + public void received(Channel channel, Object message) throws RemotingException { + ExecutorService executor = getPreferredExecutorService(message); + if (executor instanceof ThreadlessExecutor) { + try { + executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); + } catch (Throwable t) { + throw new ExecutionException(message, channel, getClass() + " error when process received event .", t); + } + } else { + handler.received(channel, message); + } + } + +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectDispatcher.java index f18065d9e7..aaed4e7922 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectDispatcher.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/direct/DirectDispatcher.java @@ -29,7 +29,7 @@ public class DirectDispatcher implements Dispatcher { @Override public ChannelHandler dispatch(ChannelHandler handler, URL url) { - return handler; + return new DirectChannelHandler(handler, url); } } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionChannelHandler.java index e39d138af1..761e26ca6d 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionChannelHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/execution/ExecutionChannelHandler.java @@ -17,12 +17,12 @@ package org.apache.dubbo.remoting.transport.dispatcher.execution; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.ExecutionException; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.exchange.Request; -import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable; import org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.ChannelState; import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler; @@ -42,7 +42,8 @@ public class ExecutionChannelHandler extends WrappedChannelHandler { @Override public void received(Channel channel, Object message) throws RemotingException { - ExecutorService executor = getExecutorService(); + ExecutorService executor = getPreferredExecutorService(message); + if (message instanceof Request) { try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); @@ -51,19 +52,12 @@ public class ExecutionChannelHandler extends WrappedChannelHandler { // therefore the consumer side has to wait until gets timeout. This is a temporary solution to prevent // this scenario from happening, but a better solution should be considered later. if (t instanceof RejectedExecutionException) { - Request request = (Request) message; - if (request.isTwoWay()) { - String msg = "Server side(" + url.getIp() + "," + url.getPort() - + ") thread pool is exhausted, detail msg:" + t.getMessage(); - Response response = new Response(request.getId(), request.getVersion()); - response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR); - response.setErrorMessage(msg); - channel.send(response); - return; - } + sendFeedback(channel, (Request) message, t); } throw new ExecutionException(message, channel, getClass() + " error when process received event.", t); } + } else if (executor instanceof ThreadlessExecutor) { + executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } else { handler.received(channel, message); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyChannelHandler.java index 2f51860578..2cd20cc43a 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyChannelHandler.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/message/MessageOnlyChannelHandler.java @@ -35,7 +35,7 @@ public class MessageOnlyChannelHandler extends WrappedChannelHandler { @Override public void received(Channel channel, Object message) throws RemotingException { - ExecutorService executor = getExecutorService(); + ExecutorService executor = getPreferredExecutorService(message); try { executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message)); } catch (Throwable t) { diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java index 2dd400545a..6482232b42 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/DefaultFutureTest.java @@ -23,6 +23,7 @@ import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.handler.MockedChannel; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; @@ -59,6 +60,7 @@ public class DefaultFutureTest { * start time: 2018-06-21 15:13:02.215, end time: 2018-06-21 15:13:07.231... */ @Test + @Disabled public void timeoutNotSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println("before a future is create , time is : " + LocalDateTime.now().format(formatter)); @@ -89,13 +91,14 @@ public class DefaultFutureTest { * start time: 2018-06-21 15:12:38.337, end time: 2018-06-21 15:12:43.354... */ @Test + @Disabled public void timeoutSend() throws Exception { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println("before a future is create , time is : " + LocalDateTime.now().format(formatter)); // timeout after 5 seconds. Channel channel = new MockedChannel(); Request request = new Request(10); - DefaultFuture f = DefaultFuture.newFuture(channel, request, 5000); + DefaultFuture f = DefaultFuture.newFuture(channel, request, 5000, null); //mark the future is sent DefaultFuture.sent(channel, request); while (!f.isDone()) { @@ -119,7 +122,7 @@ public class DefaultFutureTest { private DefaultFuture defaultFuture(int timeout) { Channel channel = new MockedChannel(); Request request = new Request(index.getAndIncrement()); - return DefaultFuture.newFuture(channel, request, timeout); + return DefaultFuture.newFuture(channel, request, timeout, null); } -} \ No newline at end of file +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannelTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannelTest.java index 92739bfef9..2affe7d513 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannelTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeChannelTest.java @@ -17,23 +17,21 @@ package org.apache.dubbo.remoting.exchange.support.header; import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.Channel; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import org.apache.dubbo.remoting.Channel; -import org.apache.dubbo.remoting.RemotingException; -import org.apache.dubbo.remoting.exchange.Request; - import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.util.List; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class HeaderExchangeChannelTest { @@ -193,7 +191,7 @@ public class HeaderExchangeChannelTest { public void closeWithTimeoutTest02() { Assertions.assertFalse(channel.isClosed()); Request request = new Request(); - DefaultFuture.newFuture(channel, request, 100); + DefaultFuture.newFuture(channel, request, 100, null); header.close(100); //return directly header.close(1000); diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java index d3aeb5efa4..ea91756d83 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java +++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java @@ -51,7 +51,7 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest { handler.disconnected(new MockedChannel()); Assertions.assertTrue(executor.getActiveCount() <= 1, executor.getActiveCount() + " must <=1"); } - //queue.size + //queue.size Assertions.assertEquals(taskCount - 1, executor.getQueue().size()); for (int i = 0; i < taskCount; i++) { @@ -107,6 +107,7 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest { } @Test + @Disabled("FIXME") public void test_Received_InvokeInExecuter() throws RemotingException { Assertions.assertThrows(ExecutionException.class, () -> { handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url); @@ -142,4 +143,4 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest { }, req); Assertions.assertEquals(1, count.get(), "channel.send must be invoke"); } -} \ No newline at end of file +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java index 86766d9db2..21cefc0fe0 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AppResponse.java @@ -17,6 +17,9 @@ package org.apache.dubbo.rpc; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; @@ -49,7 +52,7 @@ public class AppResponse extends AbstractResult implements Serializable { private Throwable exception; - private Map attachments = new HashMap(); + private Map attachments = new HashMap(); public AppResponse() { } @@ -113,7 +116,7 @@ public class AppResponse extends AbstractResult implements Serializable { } @Override - public Map getAttachments() { + public Map getAttachments() { return attachments; } @@ -122,38 +125,35 @@ public class AppResponse extends AbstractResult implements Serializable { * * @param map contains all key-value pairs to append */ - @Override - public void setAttachments(Map map) { - this.attachments = map == null ? new HashMap() : map; + public void setAttachments(Map map) { + this.attachments = map == null ? new HashMap() : map; } - @Override - public void addAttachments(Map map) { + public void addAttachments(Map map) { if (map == null) { return; } if (this.attachments == null) { - this.attachments = new HashMap(); + this.attachments = new HashMap(); } this.attachments.putAll(map); } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { return attachments.get(key); } @Override - public String getAttachment(String key, String defaultValue) { - String result = attachments.get(key); - if (result == null || result.length() == 0) { + public Object getAttachment(String key, Object defaultValue) { + Object result = attachments.get(key); + if (result == null) { result = defaultValue; } return result; } - @Override - public void setAttachment(String key, String value) { + public void setAttachment(String key, Object value) { attachments.put(key, value); } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java index ef1adc4e2d..5d0a643b2e 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java @@ -47,6 +47,7 @@ public class AsyncRpcResult extends AbstractResult { */ private RpcContext storedContext; private RpcContext storedServerContext; + private Executor executor; private Invocation invocation; @@ -136,6 +137,30 @@ public class AsyncRpcResult extends AbstractResult { return new AppResponse(); } + + /** + * This method will always return after a maximum 'timeout' waiting: + * 1. if value returns before timeout, return normally. + * 2. if no value returns after timeout, throw TimeoutException. + * + * @return + * @throws InterruptedException + * @throws ExecutionException + */ + @Override + public Result get() throws InterruptedException, ExecutionException { + if (executor != null) { + ThreadlessExecutor threadlessExecutor = (ThreadlessExecutor) executor; + threadlessExecutor.waitAndDrain(); + } + return super.get(); + } + + @Override + public Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return this.get(); + } + @Override public Object recreate() throws Throwable { RpcInvocation rpcInvocation = (RpcInvocation) invocation; @@ -172,32 +197,32 @@ public class AsyncRpcResult extends AbstractResult { } @Override - public Map getAttachments() { + public Map getAttachments() { return getAppResponse().getAttachments(); } @Override - public void setAttachments(Map map) { + public void setAttachments(Map map) { getAppResponse().setAttachments(map); } @Override - public void addAttachments(Map map) { + public void addAttachments(Map map) { getAppResponse().addAttachments(map); } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { return getAppResponse().getAttachment(key); } @Override - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { return getAppResponse().getAttachment(key, defaultValue); } @Override - public void setAttachment(String key, String value) { + public void setAttachment(String key, Object value) { getAppResponse().setAttachment(key, value); } @@ -213,6 +238,14 @@ public class AsyncRpcResult extends AbstractResult { return invocation; } + public Executor getExecutor() { + return executor; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + /** * tmp context to use when the thread switch to Dubbo thread. */ diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java index 058e0ad09c..61c86e2320 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java @@ -1,90 +1,95 @@ -/* - * 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.dubbo.rpc; - -import java.util.Map; - -/** - * Invocation. (API, Prototype, NonThreadSafe) - * - * @serial Don't change the class name and package name. - * @see org.apache.dubbo.rpc.Invoker#invoke(Invocation) - * @see org.apache.dubbo.rpc.RpcInvocation - */ -public interface Invocation { - - /** - * get method name. - * - * @return method name. - * @serial - */ - String getMethodName(); - - /** - * get parameter types. - * - * @return parameter types. - * @serial - */ - Class[] getParameterTypes(); - - /** - * get arguments. - * - * @return arguments. - * @serial - */ - Object[] getArguments(); - - /** - * get attachments. - * - * @return attachments. - * @serial - */ - Map getAttachments(); - - void setAttachment(String key, String value); - - void setAttachmentIfAbsent(String key, String value); - - /** - * get attachment by key. - * - * @return attachment value. - * @serial - */ - String getAttachment(String key); - - /** - * get attachment by key with default value. - * - * @return attachment value. - * @serial - */ - String getAttachment(String key, String defaultValue); - - /** - * get the invoker in current context. - * - * @return invoker. - * @transient - */ - Invoker getInvoker(); - +/* + * 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.dubbo.rpc; + +import java.util.Map; + +/** + * Invocation. (API, Prototype, NonThreadSafe) + * + * @serial Don't change the class name and package name. + * @see org.apache.dubbo.rpc.Invoker#invoke(Invocation) + * @see org.apache.dubbo.rpc.RpcInvocation + */ +public interface Invocation { + + /** + * get method name. + * + * @return method name. + * @serial + */ + String getMethodName(); + + /** + * get parameter types. + * + * @return parameter types. + * @serial + */ + Class[] getParameterTypes(); + + /** + * get arguments. + * + * @return arguments. + * @serial + */ + Object[] getArguments(); + + /** + * get attachments. + * + * @return attachments. + * @serial + */ + Map getAttachments(); + + void setAttachment(String key, Object value); + + void setAttachmentIfAbsent(String key, Object value); + + /** + * get attachment by key. + * + * @return attachment value. + * @serial + */ + Object getAttachment(String key); + + /** + * get attachment by key with default value. + * + * @return attachment value. + * @serial + */ + Object getAttachment(String key, Object defaultValue); + + /** + * get the invoker in current context. + * + * @return invoker. + * @transient + */ + Invoker getInvoker(); + + Object put(Object key, Object value); + + Object get(Object key); + + Map getAttributes(); } \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java index 3d6a524074..a97a0be61f 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/InvokeMode.java @@ -18,6 +18,6 @@ package org.apache.dubbo.rpc; public enum InvokeMode { - SYNC, ASYNC, FUTURE + SYNC, ASYNC, FUTURE; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java index 23f5c25ab7..0b872ea167 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Result.java @@ -86,37 +86,37 @@ public interface Result extends CompletionStage, Future, Seriali * * @return attachments. */ - Map getAttachments(); + Map getAttachments(); /** * Add the specified map to existing attachments in this instance. * * @param map */ - void addAttachments(Map map); + void addAttachments(Map map); /** * Replace the existing attachments with the specified param. * * @param map */ - void setAttachments(Map map); + void setAttachments(Map map); /** * get attachment by key. * * @return attachment value. */ - String getAttachment(String key); + Object getAttachment(String key); /** * get attachment by key with default value. * * @return attachment value. */ - String getAttachment(String key, String defaultValue); + Object getAttachment(String key, Object defaultValue); - void setAttachment(String key, String value); + void setAttachment(String key, Object value); /** * Returns the specified {@code valueIfAbsent} when not complete, or diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java index 1066c7d9a1..f5a484a23e 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java @@ -73,7 +73,7 @@ public class RpcContext { } }; - private final Map attachments = new HashMap(); + private final Map attachments = new HashMap(); private final Map values = new HashMap(); private List urls; @@ -105,7 +105,6 @@ public class RpcContext { private Object response; private AsyncContext asyncContext; - protected RpcContext() { } @@ -467,7 +466,7 @@ public class RpcContext { * @param key * @return attachment */ - public String getAttachment(String key) { + public Object getAttachment(String key) { return attachments.get(key); } @@ -478,7 +477,7 @@ public class RpcContext { * @param value * @return context */ - public RpcContext setAttachment(String key, String value) { + public RpcContext setAttachment(String key, Object value) { if (value == null) { attachments.remove(key); } else { @@ -503,7 +502,7 @@ public class RpcContext { * * @return attachments */ - public Map getAttachments() { + public Map getAttachments() { return attachments; } @@ -513,7 +512,7 @@ public class RpcContext { * @param attachment * @return context */ - public RpcContext setAttachments(Map attachment) { + public RpcContext setAttachments(Map attachment) { this.attachments.clear(); if (attachment != null && attachment.size() > 0) { this.attachments.putAll(attachment); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java index d972537371..aacb4ac144 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java @@ -17,7 +17,6 @@ package org.apache.dubbo.rpc; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.StringUtils; import java.io.Serializable; import java.lang.reflect.Method; @@ -48,7 +47,9 @@ public class RpcInvocation implements Invocation, Serializable { private Object[] arguments; - private Map attachments; + private Map attachments; + + private Map attributes = new HashMap(); private transient Invoker invoker; @@ -61,7 +62,7 @@ public class RpcInvocation implements Invocation, Serializable { public RpcInvocation(Invocation invocation, Invoker invoker) { this(invocation.getMethodName(), invocation.getParameterTypes(), - invocation.getArguments(), new HashMap(invocation.getAttachments()), + invocation.getArguments(), new HashMap(invocation.getAttachments()), invocation.getInvoker()); if (invoker != null) { URL url = invoker.getUrl(); @@ -96,24 +97,25 @@ public class RpcInvocation implements Invocation, Serializable { this(method, arguments, null); } - public RpcInvocation(Method method, Object[] arguments, Map attachment) { + public RpcInvocation(Method method, Object[] arguments, Map attachment, Map attributes) { this(method.getName(), method.getParameterTypes(), arguments, attachment, null); this.returnType = method.getReturnType(); + this.attributes = attributes == null ? new HashMap<>() : attributes; } public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments) { this(methodName, parameterTypes, arguments, null, null); } - public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments, Map attachments) { + public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments, Map attachments) { this(methodName, parameterTypes, arguments, attachments, null); } - public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments, Map attachments, Invoker invoker) { + public RpcInvocation(String methodName, Class[] parameterTypes, Object[] arguments, Map attachments, Invoker invoker) { this.methodName = methodName; this.parameterTypes = parameterTypes == null ? new Class[0] : parameterTypes; this.arguments = arguments == null ? new Object[0] : arguments; - this.attachments = attachments == null ? new HashMap() : attachments; + this.attachments = attachments == null ? new HashMap() : attachments; this.invoker = invoker; } @@ -126,6 +128,19 @@ public class RpcInvocation implements Invocation, Serializable { this.invoker = invoker; } + public Object put(Object key, Object value) { + return attributes.put(key, value); + } + + public Object get(Object key) { + return attributes.get(key); + } + + @Override + public Map getAttributes() { + return attributes; + } + @Override public String getMethodName() { return methodName; @@ -154,53 +169,53 @@ public class RpcInvocation implements Invocation, Serializable { } @Override - public Map getAttachments() { + public Map getAttachments() { return attachments; } - public void setAttachments(Map attachments) { - this.attachments = attachments == null ? new HashMap() : attachments; + public void setAttachments(Map attachments) { + this.attachments = attachments == null ? new HashMap() : attachments; } @Override - public void setAttachment(String key, String value) { + public void setAttachment(String key, Object value) { if (attachments == null) { - attachments = new HashMap(); + attachments = new HashMap(); } attachments.put(key, value); } @Override - public void setAttachmentIfAbsent(String key, String value) { + public void setAttachmentIfAbsent(String key, Object value) { if (attachments == null) { - attachments = new HashMap(); + attachments = new HashMap(); } if (!attachments.containsKey(key)) { attachments.put(key, value); } } - public void addAttachments(Map attachments) { + public void addAttachments(Map attachments) { if (attachments == null) { return; } if (this.attachments == null) { - this.attachments = new HashMap(); + this.attachments = new HashMap(); } this.attachments.putAll(attachments); } - public void addAttachmentsIfAbsent(Map attachments) { + public void addAttachmentsIfAbsent(Map attachments) { if (attachments == null) { return; } - for (Map.Entry entry : attachments.entrySet()) { + for (Map.Entry entry : attachments.entrySet()) { setAttachmentIfAbsent(entry.getKey(), entry.getValue()); } } @Override - public String getAttachment(String key) { + public Object getAttachment(String key) { if (attachments == null) { return null; } @@ -208,12 +223,12 @@ public class RpcInvocation implements Invocation, Serializable { } @Override - public String getAttachment(String key, String defaultValue) { + public Object getAttachment(String key, Object defaultValue) { if (attachments == null) { return defaultValue; } - String value = attachments.get(key); - if (StringUtils.isEmpty(value)) { + Object value = attachments.get(key); + if (null == value) { return defaultValue; } return value; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationInitListener.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationInitListener.java new file mode 100644 index 0000000000..f38acc4ed3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationInitListener.java @@ -0,0 +1,27 @@ +/* + * 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.dubbo.rpc.model; + +import org.apache.dubbo.common.extension.SPI; + +@SPI +public interface ApplicationInitListener { + /** + * init the application + */ + void init(); +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java index 33dcc908e1..59c2374f77 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java @@ -16,20 +16,23 @@ */ package org.apache.dubbo.rpc.model; +import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import java.util.Collection; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; /** * Represent a application which is using Dubbo and store basic metadata info for using * during the processing of RPC invoking. - * + *

    * ApplicationModel includes many ProviderModel which is about published services * and many Consumer Model which is about subscribed services. - * + *

    * adjust project structure in order to fully utilize the methods introduced here. */ public class ApplicationModel { @@ -47,6 +50,8 @@ public class ApplicationModel { private static String application; + private static AtomicBoolean INIT_FLAG = new AtomicBoolean(false); + public static Collection allConsumerModels() { return CONSUMED_SERVICES.values(); } @@ -63,6 +68,16 @@ public class ApplicationModel { return CONSUMED_SERVICES.get(serviceName); } + public static void init() { + if (INIT_FLAG.compareAndSet(false, true)) { + ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(ApplicationInitListener.class); + Set listenerNames = extensionLoader.getSupportedExtensions(); + for (String listenerName : listenerNames) { + extensionLoader.getExtension(listenerName).init(); + } + } + } + public static void initConsumerModel(String serviceName, ConsumerModel consumerModel) { if (CONSUMED_SERVICES.putIfAbsent(serviceName, consumerModel) != null) { LOGGER.warn("Already register the same consumer:" + serviceName); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java index 0798a50029..01f3696ef3 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java @@ -18,7 +18,8 @@ package org.apache.dubbo.rpc.model; import java.lang.reflect.Method; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import static org.apache.dubbo.rpc.Constants.$INVOKE; @@ -32,10 +33,9 @@ public class ConsumerMethodModel { private final String methodName; private final boolean generic; - private final AsyncMethodInfo asyncInfo; + private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); - - public ConsumerMethodModel(Method method, Map attributes) { + public ConsumerMethodModel(Method method) { this.method = method; this.parameterClasses = method.getParameterTypes(); this.returnClass = method.getReturnType(); @@ -43,24 +43,32 @@ public class ConsumerMethodModel { this.methodName = method.getName(); this.generic = methodName.equals($INVOKE) && parameterTypes != null && parameterTypes.length == 3; - if (attributes != null) { - asyncInfo = (AsyncMethodInfo) attributes.get(methodName); - } else { - asyncInfo = null; - } } public Method getMethod() { return method; } +// public ConcurrentMap getAttributeMap() { +// return attributeMap; +// } + + public void addAttribute(String key, Object value) { + this.attributeMap.put(key, value); + } + + public Object getAttribute(String key) { + return this.attributeMap.get(key); + } + + public Class getReturnClass() { return returnClass; } - public AsyncMethodInfo getAsyncInfo() { - return asyncInfo; - } +// public AsyncMethodInfo getAsyncInfo() { +// return (AsyncMethodInfo) attributeMap.get(Constants.ASYNC_KEY); +// } public String getMethodName() { return methodName; diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java index 4c990a5088..1a91ae1b75 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java @@ -16,10 +16,9 @@ */ package org.apache.dubbo.rpc.model; -import org.apache.dubbo.common.utils.Assert; - import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -29,47 +28,31 @@ import java.util.Optional; * Consumer Model which is about subscribed services. */ public class ConsumerModel { - private final Object proxyObject; - private final String serviceName; - private final Class serviceInterfaceClass; - + private final ServiceMetadata serviceMetadata; private final Map methodModels = new IdentityHashMap(); - /** - * This constructor create an instance of ConsumerModel and passed objects should not be null. - * If service name, service instance, proxy object,methods should not be null. If these are null - * then this constructor will throw {@link IllegalArgumentException} - * @param serviceName Name of the service. - * @param serviceInterfaceClass Service interface class. - * @param proxyObject Proxy object. - * @param methods Methods of service class - * @param attributes Attributes of methods. - */ - public ConsumerModel(String serviceName - , Class serviceInterfaceClass - , Object proxyObject - , Method[] methods - , Map attributes) { + public ConsumerModel(String serviceName, String group, String version, Class interfaceClass) { + this.serviceMetadata = new ServiceMetadata(serviceName, group, version, interfaceClass); - Assert.notEmptyString(serviceName, "Service name can't be null or blank"); - Assert.notNull(serviceInterfaceClass, "Service interface class can't null"); - Assert.notNull(proxyObject, "Proxy object can't be null"); - Assert.notNull(methods, "Methods can't be null"); - - this.serviceName = serviceName; - this.serviceInterfaceClass = serviceInterfaceClass; - this.proxyObject = proxyObject; + Method[] methods = interfaceClass.getMethods(); for (Method method : methods) { - methodModels.put(method, new ConsumerMethodModel(method, attributes)); + methodModels.put(method, new ConsumerMethodModel(method)); + } + } + + public ConsumerModel(ServiceMetadata serviceMetadata) { + this.serviceMetadata = serviceMetadata; + Method[] methods = serviceMetadata.getServiceType().getMethods(); + for (Method method : methods) { + methodModels.put(method, new ConsumerMethodModel(method)); } } /** - * Return the proxy object used by called while creating instance of ConsumerModel - * @return + * @return serviceMetadata */ - public Object getProxyObject() { - return proxyObject; + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; } /** @@ -93,6 +76,27 @@ public class ConsumerModel { return consumerMethodModelEntry.map(Map.Entry::getValue).orElse(null); } + /** + * @param method metodName + * @param argsType method arguments type + * @return + */ + public ConsumerMethodModel getMethodModel(String method, String[] argsType) { + Optional consumerMethodModel = methodModels.entrySet().stream() + .filter(entry -> entry.getKey().getName().equals(method)) + .map(Map.Entry::getValue).filter(methodModel -> Arrays.equals(argsType, methodModel.getParameterTypes())) + .findFirst(); + return consumerMethodModel.orElse(null); + } + + + /** + * @return + */ + public Class getServiceInterfaceClass() { + return serviceMetadata.getServiceType(); + } + /** * Return all method models for the current service * @@ -102,11 +106,7 @@ public class ConsumerModel { return new ArrayList(methodModels.values()); } - public Class getServiceInterfaceClass() { - return serviceInterfaceClass; - } - public String getServiceName() { - return serviceName; + return this.serviceMetadata.getServiceKey(); } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderMethodModel.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderMethodModel.java index d7b8a9bb87..eb6faa45c7 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderMethodModel.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderMethodModel.java @@ -17,19 +17,24 @@ package org.apache.dubbo.rpc.model; import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; public class ProviderMethodModel { - private transient final Method method; + private final Method method; private final String methodName; + private final Class[] parameterClasses; private final String[] methodArgTypes; - private final String serviceName; + private final Type[] genericParameterTypes; + private final ConcurrentMap attributeMap = new ConcurrentHashMap<>(); - - public ProviderMethodModel(Method method, String serviceName) { + public ProviderMethodModel(Method method) { this.method = method; - this.serviceName = serviceName; this.methodName = method.getName(); + this.parameterClasses = method.getParameterTypes(); this.methodArgTypes = getArgTypes(method); + this.genericParameterTypes = method.getGenericParameterTypes(); } public Method getMethod() { @@ -44,8 +49,8 @@ public class ProviderMethodModel { return methodArgTypes; } - public String getServiceName() { - return serviceName; + public ConcurrentMap getAttributeMap() { + return attributeMap; } private static String[] getArgTypes(Method method) { @@ -60,4 +65,12 @@ public class ProviderMethodModel { } return methodArgTypes; } + + public Class[] getParameterClasses() { + return parameterClasses; + } + + public Type[] getGenericParameterTypes() { + return genericParameterTypes; + } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java index 6fb9bebd7e..63f04bf361 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ProviderModel.java @@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.model; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,30 +28,35 @@ import java.util.Map; * ProviderModel which is about published services */ public class ProviderModel { - private final String serviceName; private final Object serviceInstance; - private final Class serviceInterfaceClass; + private final ServiceMetadata serviceMetadata; + private final String serivceKey; private final Map> methods = new HashMap>(); - public ProviderModel(String serviceName, Object serviceInstance, Class serviceInterfaceClass) { + public ProviderModel(String serviceName, String group, String version, Object serviceInstance, Class serviceInterfaceClass) { if (null == serviceInstance) { throw new IllegalArgumentException("Service[" + serviceName + "]Target is NULL."); } - this.serviceName = serviceName; this.serviceInstance = serviceInstance; - this.serviceInterfaceClass = serviceInterfaceClass; - - initMethod(); + this.serviceMetadata = new ServiceMetadata(serviceName, group, version, serviceInterfaceClass); + this.serivceKey = serviceMetadata.getServiceKey(); + initMethod(serviceInterfaceClass); } + public ProviderModel(Object serviceInstance, ServiceMetadata serviceMetadata) { + this.serviceInstance = serviceInstance; + this.serviceMetadata = serviceMetadata; + this.serivceKey = serviceMetadata.getServiceKey(); + initMethod(serviceMetadata.getServiceType()); + } public String getServiceName() { - return serviceName; + return this.serviceMetadata.getServiceKey(); } public Class getServiceInterfaceClass() { - return serviceInterfaceClass; + return this.serviceMetadata.getServiceType(); } public Object getServiceInstance() { @@ -77,20 +83,32 @@ public class ProviderModel { return null; } - private void initMethod() { - Method[] methodsToExport = null; - methodsToExport = this.serviceInterfaceClass.getMethods(); + public List getMethodModelList(String methodName) { + List resultList = methods.get(methodName); + return resultList == null ? Collections.emptyList() : resultList; + } + + + private void initMethod(Class serviceInterfaceClass) { + Method[] methodsToExport; + methodsToExport = serviceInterfaceClass.getMethods(); for (Method method : methodsToExport) { method.setAccessible(true); List methodModels = methods.get(method.getName()); if (methodModels == null) { - methodModels = new ArrayList(1); + methodModels = new ArrayList(); methods.put(method.getName(), methodModels); } - methodModels.add(new ProviderMethodModel(method, serviceName)); + methodModels.add(new ProviderMethodModel(method)); } } + /** + * @return serviceMetadata + */ + public ServiceMetadata getServiceMetadata() { + return serviceMetadata; + } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ServiceMetadata.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ServiceMetadata.java new file mode 100644 index 0000000000..5a09f45cce --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ServiceMetadata.java @@ -0,0 +1,130 @@ +/* + * 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.dubbo.rpc.model; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * data related to service level such as name, version, classloader of business service, + * security info, etc. Also with a AttributeMap for extension. + */ +public class ServiceMetadata { + + private String serviceKey; + private String serviceInterfaceName; + private String defaultGroup; + private String version; + private Class serviceType; + + private volatile String group; + + private Object target; + + /* will be transferred to remote side */ + private final Map attachments = new ConcurrentHashMap(); + /* used locally*/ + private final Map attributeMap = new ConcurrentHashMap(); + + public ServiceMetadata(String serviceInterfaceName, String group, String version, Class serviceType) { + this.serviceInterfaceName = serviceInterfaceName; + this.defaultGroup = group; + this.group = group; + this.version = version; + this.serviceKey = serviceInterfaceName + ":" + version; + this.serviceType = serviceType; + } + + public ServiceMetadata() { + } + + public String getServiceKey() { + return serviceInterfaceName + ":" + version; + } + + public Map getAttachments() { + return attachments; + } + + public Map getAttributeMap() { + return attributeMap; + } + + public Object getAttribute(String key) { + return attributeMap.get(key); + } + + public void addAttribute(String key, Object value) { + this.attributeMap.put(key, value); + } + + public void addAttachment(String key, Object value) { + this.attributeMap.put(key, value); + } + + public Class getServiceType() { + return serviceType; + } + + public String getServiceInterfaceName() { + return serviceInterfaceName; + } + + public String getDefaultGroup() { + return defaultGroup; + } + + public String getVersion() { + return version; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public void setServiceInterfaceName(String serviceInterfaceName) { + this.serviceInterfaceName = serviceInterfaceName; + } + + public void setDefaultGroup(String defaultGroup) { + this.defaultGroup = defaultGroup; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setServiceType(Class serviceType) { + this.serviceType = serviceType; + } + + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; + } + + public Object getTarget() { + return target; + } + + public void setTarget(Object target) { + this.target = target; + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java index 58cb4105f8..42f52e8dcd 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java @@ -18,13 +18,18 @@ package org.apache.dubbo.rpc.protocol; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.Version; +import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.threadpool.ThreadlessExecutor; +import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.rpc.AsyncRpcResult; +import org.apache.dubbo.rpc.FutureAdapter; import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.InvokeMode; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; @@ -36,6 +41,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -49,21 +55,21 @@ public abstract class AbstractInvoker implements Invoker { private final URL url; - private final Map attachment; + private final Map attachment; private volatile boolean available = true; private AtomicBoolean destroyed = new AtomicBoolean(false); public AbstractInvoker(Class type, URL url) { - this(type, url, (Map) null); + this(type, url, (Map) null); } public AbstractInvoker(Class type, URL url, String[] keys) { this(type, url, convertAttachment(url, keys)); } - public AbstractInvoker(Class type, URL url, Map attachment) { + public AbstractInvoker(Class type, URL url, Map attachment) { if (type == null) { throw new IllegalArgumentException("service type == null"); } @@ -75,11 +81,11 @@ public abstract class AbstractInvoker implements Invoker { this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment); } - private static Map convertAttachment(URL url, String[] keys) { + private static Map convertAttachment(URL url, String[] keys) { if (ArrayUtils.isEmpty(keys)) { return null; } - Map attachment = new HashMap(); + Map attachment = new HashMap(); for (String key : keys) { String value = url.getParameter(key); if (value != null && value.length() > 0) { @@ -137,11 +143,11 @@ public abstract class AbstractInvoker implements Invoker { if (CollectionUtils.isNotEmptyMap(attachment)) { invocation.addAttachmentsIfAbsent(attachment); } - Map contextAttachments = RpcContext.getContext().getAttachments(); + Map contextAttachments = RpcContext.getContext().getAttachments(); if (CollectionUtils.isNotEmptyMap(contextAttachments)) { /** * invocation.addAttachmentsIfAbsent(context){@link RpcInvocation#addAttachmentsIfAbsent(Map)}should not be used here, - * because the {@link RpcContext#setAttachment(String, String)} is passed in the Filter when the call is triggered + * because the {@link RpcContext#setAttachment(String, Object)} is passed in the Filter when the call is triggered * by the built-in retry mechanism of the Dubbo. The attachment to update RpcContext will no longer work, which is * a mistake in most cases (for example, through Filter to RpcContext output traceId and spanId and other information). */ @@ -151,26 +157,38 @@ public abstract class AbstractInvoker implements Invoker { invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation)); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); + AsyncRpcResult asyncResult; try { - return doInvoke(invocation); + asyncResult = (AsyncRpcResult) doInvoke(invocation); } catch (InvocationTargetException e) { // biz exception Throwable te = e.getTargetException(); if (te == null) { - return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); + asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); } else { if (te instanceof RpcException) { ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION); } - return AsyncRpcResult.newDefaultAsyncResult(null, te, invocation); + asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, te, invocation); } } catch (RpcException e) { if (e.isBiz()) { - return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); + asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); } else { throw e; } } catch (Throwable e) { - return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); + asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation); + } + RpcContext.getContext().setFuture(new FutureAdapter(asyncResult, inv)); + return asyncResult; + } + + protected ExecutorService getCallbackExecutor(URL url, Invocation inv) { + ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension().createExecutorIfAbsent(url); + if (InvokeMode.SYNC == RpcUtils.getInvokeMode(getUrl(), inv)) { + return new ThreadlessExecutor(sharedExecutor); + } else { + return sharedExecutor; } } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java index 2d70a9ed8e..e0ca6ab5a1 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java @@ -1,203 +1,200 @@ -/* - * 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.dubbo.rpc.support; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.ReflectUtils; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.InvokeMode; -import org.apache.dubbo.rpc.RpcInvocation; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; - -import static org.apache.dubbo.rpc.Constants.$INVOKE; -import static org.apache.dubbo.rpc.Constants.$INVOKE_ASYNC; -import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; -import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; -import static org.apache.dubbo.rpc.Constants.ID_KEY; -import static org.apache.dubbo.rpc.Constants.RETURN_KEY; -/** - * RpcUtils - */ -public class RpcUtils { - - private static final Logger logger = LoggerFactory.getLogger(RpcUtils.class); - private static final AtomicLong INVOKE_ID = new AtomicLong(0); - - public static Class getReturnType(Invocation invocation) { - try { - if (invocation != null && invocation.getInvoker() != null - && invocation.getInvoker().getUrl() != null - && !invocation.getMethodName().startsWith("$")) { - String service = invocation.getInvoker().getUrl().getServiceInterface(); - if (StringUtils.isNotEmpty(service)) { - Class invokerInterface = invocation.getInvoker().getInterface(); - Class cls = invokerInterface != null ? ReflectUtils.forName(invokerInterface.getClassLoader(), service) - : ReflectUtils.forName(service); - Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes()); - if (method.getReturnType() == void.class) { - return null; - } - return method.getReturnType(); - } - } - } catch (Throwable t) { - logger.warn(t.getMessage(), t); - } - return null; - } - - public static Type[] getReturnTypes(Invocation invocation) { - try { - if (invocation != null && invocation.getInvoker() != null - && invocation.getInvoker().getUrl() != null - && !invocation.getMethodName().startsWith("$")) { - String service = invocation.getInvoker().getUrl().getServiceInterface(); - if (StringUtils.isNotEmpty(service)) { - Class invokerInterface = invocation.getInvoker().getInterface(); - Class cls = invokerInterface != null ? ReflectUtils.forName(invokerInterface.getClassLoader(), service) - : ReflectUtils.forName(service); - Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes()); - if (method.getReturnType() == void.class) { - return null; - } - return ReflectUtils.getReturnTypes(method); - } - } - } catch (Throwable t) { - logger.warn(t.getMessage(), t); - } - return null; - } - - public static Long getInvocationId(Invocation inv) { - String id = inv.getAttachment(ID_KEY); - return id == null ? null : new Long(id); - } - - /** - * Idempotent operation: invocation id will be added in async operation by default - * - * @param url - * @param inv - */ - public static void attachInvocationIdIfAsync(URL url, Invocation inv) { - if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null && inv instanceof RpcInvocation) { - ((RpcInvocation) inv).setAttachment(ID_KEY, String.valueOf(INVOKE_ID.getAndIncrement())); - } - } - - private static boolean isAttachInvocationId(URL url, Invocation invocation) { - String value = url.getMethodParameter(invocation.getMethodName(), AUTO_ATTACH_INVOCATIONID_KEY); - if (value == null) { - // add invocationid in async operation by default - return isAsync(url, invocation); - } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { - return true; - } else { - return false; - } - } - - public static String getMethodName(Invocation invocation) { - if ($INVOKE.equals(invocation.getMethodName()) - && invocation.getArguments() != null - && invocation.getArguments().length > 0 - && invocation.getArguments()[0] instanceof String) { - return (String) invocation.getArguments()[0]; - } - return invocation.getMethodName(); - } - - public static Object[] getArguments(Invocation invocation) { - if ($INVOKE.equals(invocation.getMethodName()) - && invocation.getArguments() != null - && invocation.getArguments().length > 2 - && invocation.getArguments()[2] instanceof Object[]) { - return (Object[]) invocation.getArguments()[2]; - } - return invocation.getArguments(); - } - - public static Class[] getParameterTypes(Invocation invocation) { - if ($INVOKE.equals(invocation.getMethodName()) - && invocation.getArguments() != null - && invocation.getArguments().length > 1 - && invocation.getArguments()[1] instanceof String[]) { - String[] types = (String[]) invocation.getArguments()[1]; - if (types == null) { - return new Class[0]; - } - Class[] parameterTypes = new Class[types.length]; - for (int i = 0; i < types.length; i++) { - parameterTypes[i] = ReflectUtils.forName(types[0]); - } - return parameterTypes; - } - return invocation.getParameterTypes(); - } - - public static boolean isAsync(URL url, Invocation inv) { - boolean isAsync; - if (Boolean.TRUE.toString().equals(inv.getAttachment(ASYNC_KEY))) { - isAsync = true; - } else { - isAsync = url.getMethodParameter(getMethodName(inv), ASYNC_KEY, false); - } - return isAsync; - } - - public static boolean isReturnTypeFuture(Invocation inv) { - Class clazz; - if (inv instanceof RpcInvocation) { - clazz = ((RpcInvocation) inv).getReturnType(); - } else { - clazz = getReturnType(inv); - } - return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv); - } - - public static InvokeMode getInvokeMode(URL url, Invocation inv) { - if (isReturnTypeFuture(inv)) { - return InvokeMode.FUTURE; - } else if (isAsync(url, inv)) { - return InvokeMode.ASYNC; - } else { - return InvokeMode.SYNC; - } - } - - public static boolean isGenericAsync(Invocation inv) { - return $INVOKE_ASYNC.equals(inv.getMethodName()); - } - - public static boolean isOneway(URL url, Invocation inv) { - boolean isOneway; - if (Boolean.FALSE.toString().equals(inv.getAttachment(RETURN_KEY))) { - isOneway = true; - } else { - isOneway = !url.getMethodParameter(getMethodName(inv), RETURN_KEY, true); - } - return isOneway; - } -} +/* + * 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.dubbo.rpc.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.InvokeMode; +import org.apache.dubbo.rpc.RpcInvocation; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +import static org.apache.dubbo.rpc.Constants.$INVOKE; +import static org.apache.dubbo.rpc.Constants.$INVOKE_ASYNC; +import static org.apache.dubbo.rpc.Constants.ASYNC_KEY; +import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; +import static org.apache.dubbo.rpc.Constants.ID_KEY; +import static org.apache.dubbo.rpc.Constants.RETURN_KEY; +/** + * RpcUtils + */ +public class RpcUtils { + + private static final Logger logger = LoggerFactory.getLogger(RpcUtils.class); + private static final AtomicLong INVOKE_ID = new AtomicLong(0); + + public static Class getReturnType(Invocation invocation) { + try { + if (invocation != null && invocation.getInvoker() != null + && invocation.getInvoker().getUrl() != null + && !invocation.getMethodName().startsWith("$")) { + String service = invocation.getInvoker().getUrl().getServiceInterface(); + if (StringUtils.isNotEmpty(service)) { + Class invokerInterface = invocation.getInvoker().getInterface(); + Class cls = invokerInterface != null ? ReflectUtils.forName(invokerInterface.getClassLoader(), service) + : ReflectUtils.forName(service); + Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes()); + if (method.getReturnType() == void.class) { + return null; + } + return method.getReturnType(); + } + } + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + return null; + } + + public static Type[] getReturnTypes(Invocation invocation) { + try { + if (invocation != null && invocation.getInvoker() != null + && invocation.getInvoker().getUrl() != null + && !invocation.getMethodName().startsWith("$")) { + String service = invocation.getInvoker().getUrl().getServiceInterface(); + if (StringUtils.isNotEmpty(service)) { + Class invokerInterface = invocation.getInvoker().getInterface(); + Class cls = invokerInterface != null ? ReflectUtils.forName(invokerInterface.getClassLoader(), service) + : ReflectUtils.forName(service); + Method method = cls.getMethod(invocation.getMethodName(), invocation.getParameterTypes()); + if (method.getReturnType() == void.class) { + return null; + } + return ReflectUtils.getReturnTypes(method); + } + } + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + return null; + } + + public static Long getInvocationId(Invocation inv) { + String id = (String)inv.getAttachment(ID_KEY); + return id == null ? null : new Long(id); + } + + /** + * Idempotent operation: invocation id will be added in async operation by default + * + * @param url + * @param inv + */ + public static void attachInvocationIdIfAsync(URL url, Invocation inv) { + if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null && inv instanceof RpcInvocation) { + ((RpcInvocation) inv).setAttachment(ID_KEY, String.valueOf(INVOKE_ID.getAndIncrement())); + } + } + + private static boolean isAttachInvocationId(URL url, Invocation invocation) { + String value = url.getMethodParameter(invocation.getMethodName(), AUTO_ATTACH_INVOCATIONID_KEY); + if (value == null) { + // add invocationid in async operation by default + return isAsync(url, invocation); + } else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { + return true; + } else { + return false; + } + } + + public static String getMethodName(Invocation invocation) { + if ($INVOKE.equals(invocation.getMethodName()) + && invocation.getArguments() != null + && invocation.getArguments().length > 0 + && invocation.getArguments()[0] instanceof String) { + return (String) invocation.getArguments()[0]; + } + return invocation.getMethodName(); + } + + public static Object[] getArguments(Invocation invocation) { + if ($INVOKE.equals(invocation.getMethodName()) + && invocation.getArguments() != null + && invocation.getArguments().length > 2 + && invocation.getArguments()[2] instanceof Object[]) { + return (Object[]) invocation.getArguments()[2]; + } + return invocation.getArguments(); + } + + public static Class[] getParameterTypes(Invocation invocation) { + if ($INVOKE.equals(invocation.getMethodName()) + && invocation.getArguments() != null + && invocation.getArguments().length > 1 + && invocation.getArguments()[1] instanceof String[]) { + String[] types = (String[]) invocation.getArguments()[1]; + if (types == null) { + return new Class[0]; + } + Class[] parameterTypes = new Class[types.length]; + for (int i = 0; i < types.length; i++) { + parameterTypes[i] = ReflectUtils.forName(types[0]); + } + return parameterTypes; + } + return invocation.getParameterTypes(); + } + + public static boolean isAsync(URL url, Invocation inv) { + boolean isAsync; + if (Boolean.TRUE.toString().equals(inv.getAttachment(ASYNC_KEY))) { + isAsync = true; + } else { + isAsync = url.getMethodParameter(getMethodName(inv), ASYNC_KEY, false); + } + return isAsync; + } + + public static boolean isReturnTypeFuture(Invocation inv) { + Class clazz = getReturnType(inv); + return (clazz != null && CompletableFuture.class.isAssignableFrom(clazz)) || isGenericAsync(inv); + } + + public static boolean isGenericAsync(Invocation inv) { + return $INVOKE_ASYNC.equals(inv.getMethodName()); + public static InvokeMode getInvokeMode(URL url, Invocation inv) { + if (isReturnTypeFuture(inv)) { + return InvokeMode.FUTURE; + } else if (isAsync(url, inv)) { + return InvokeMode.ASYNC; + } else { + return InvokeMode.SYNC; + } + } + + public static boolean isGenericAsync(Invocation inv) { + return Constants.$INVOKE_ASYNC.equals(inv.getMethodName()); + } + + public static boolean isOneway(URL url, Invocation inv) { + boolean isOneway; + if (Boolean.FALSE.toString().equals(inv.getAttachment(RETURN_KEY))) { + isOneway = true; + } else { + isOneway = !url.getMethodParameter(getMethodName(inv), RETURN_KEY, true); + } + return isOneway; + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java index c9154d9e90..df06aec202 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/RpcContextTest.java @@ -86,7 +86,7 @@ public class RpcContextTest { public void testAttachments() { RpcContext context = RpcContext.getContext(); - Map map = new HashMap(); + Map map = new HashMap(); map.put("_11", "1111"); map.put("_22", "2222"); map.put(".33", "3333"); diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java index e479445ba6..c13218dbaa 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java @@ -1,82 +1,97 @@ -/* - * 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.dubbo.rpc.support; - -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.Invoker; - -import java.util.HashMap; -import java.util.Map; - -import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; -import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY; -import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; - -/** - * MockInvocation.java - */ -public class MockInvocation implements Invocation { - - public String getMethodName() { - return "echo"; - } - - public Class[] getParameterTypes() { - return new Class[]{String.class}; - } - - public Object[] getArguments() { - return new Object[]{"aa"}; - } - - public Map getAttachments() { - Map attachments = new HashMap(); - attachments.put(PATH_KEY, "dubbo"); - attachments.put(GROUP_KEY, "dubbo"); - attachments.put(VERSION_KEY, "1.0.0"); - attachments.put(DUBBO_VERSION_KEY, "1.0.0"); - attachments.put(TOKEN_KEY, "sfag"); - attachments.put(TIMEOUT_KEY, "1000"); - return attachments; - } - - @Override - public void setAttachment(String key, String value) { - - } - - @Override - public void setAttachmentIfAbsent(String key, String value) { - - } - - public Invoker getInvoker() { - return null; - } - - public String getAttachment(String key) { - return getAttachments().get(key); - } - - public String getAttachment(String key, String defaultValue) { - return getAttachments().get(key); - } - -} +/* + * 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.dubbo.rpc.support; + +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.rpc.Constants.TOKEN_KEY; + +/** + * MockInvocation.java + */ +public class MockInvocation implements Invocation { + + public String getMethodName() { + return "echo"; + } + + public Class[] getParameterTypes() { + return new Class[]{String.class}; + } + + public Object[] getArguments() { + return new Object[]{"aa"}; + } + + public Map getAttachments() { + Map attachments = new HashMap(); + attachments.put(PATH_KEY, "dubbo"); + attachments.put(GROUP_KEY, "dubbo"); + attachments.put(VERSION_KEY, "1.0.0"); + attachments.put(DUBBO_VERSION_KEY, "1.0.0"); + attachments.put(TOKEN_KEY, "sfag"); + attachments.put(TIMEOUT_KEY, "1000"); + return attachments; + } + + @Override + public void setAttachment(String key, Object value) { + + } + + @Override + public void setAttachmentIfAbsent(String key, Object value) { + + } + + public Invoker getInvoker() { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + public Object getAttachment(String key) { + return getAttachments().get(key); + } + + public Object getAttachment(String key, Object defaultValue) { + return getAttachments().get(key); + } + +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java index dc51234652..fe025af755 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java @@ -1,152 +1,152 @@ -/* - * 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.dubbo.rpc.support; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcInvocation; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.ParameterizedType; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; - -public class RpcUtilsTest { - - /** - * regular scenario: async invocation in URL - * verify: 1. whether invocationId is set correctly, 2. idempotent or not - */ - @Test - public void testAttachInvocationIdIfAsync_normal() { - URL url = URL.valueOf("dubbo://localhost/?test.async=true"); - Map attachments = new HashMap(); - attachments.put("aa", "bb"); - Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}, attachments); - RpcUtils.attachInvocationIdIfAsync(url, inv); - long id1 = RpcUtils.getInvocationId(inv); - RpcUtils.attachInvocationIdIfAsync(url, inv); - long id2 = RpcUtils.getInvocationId(inv); - assertEquals(id1, id2); // verify if it's idempotent - assertTrue(id1 >= 0); - assertEquals("bb", attachments.get("aa")); - } - - /** - * scenario: sync invocation, no attachment added by default - * verify: no id attribute added in attachment - */ - @Test - public void testAttachInvocationIdIfAsync_sync() { - URL url = URL.valueOf("dubbo://localhost/"); - Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); - RpcUtils.attachInvocationIdIfAsync(url, inv); - assertNull(RpcUtils.getInvocationId(inv)); - } - - /** - * scenario: async invocation, add attachment by default - * verify: no error report when the original attachment is null - */ - @Test - public void testAttachInvocationIdIfAsync_nullAttachments() { - URL url = URL.valueOf("dubbo://localhost/?test.async=true"); - Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); - RpcUtils.attachInvocationIdIfAsync(url, inv); - assertTrue(RpcUtils.getInvocationId(inv) >= 0L); - } - - /** - * scenario: explicitly configure to not add attachment - * verify: no id attribute added in attachment - */ - @Test - public void testAttachInvocationIdIfAsync_forceNotAttache() { - URL url = URL.valueOf("dubbo://localhost/?test.async=true&" + AUTO_ATTACH_INVOCATIONID_KEY + "=false"); - Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); - RpcUtils.attachInvocationIdIfAsync(url, inv); - assertNull(RpcUtils.getInvocationId(inv)); - } - - /** - * scenario: explicitly configure to add attachment - * verify: id attribute added in attachment - */ - @Test - public void testAttachInvocationIdIfAsync_forceAttache() { - URL url = URL.valueOf("dubbo://localhost/?" + AUTO_ATTACH_INVOCATIONID_KEY + "=true"); - Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); - RpcUtils.attachInvocationIdIfAsync(url, inv); - assertNotNull(RpcUtils.getInvocationId(inv)); - } - - @Test - public void testGetReturnTypes() throws Exception { - Invoker invoker = mock(Invoker.class); - given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService")); - Invocation inv = new RpcInvocation("testReturnType", new Class[]{String.class}, null, null, invoker); - - java.lang.reflect.Type[] types = RpcUtils.getReturnTypes(inv); - Assertions.assertEquals(2, types.length); - Assertions.assertEquals(String.class, types[0]); - Assertions.assertEquals(String.class, types[1]); - - Invocation inv1 = new RpcInvocation("testReturnType1", new Class[]{String.class}, null, null, invoker); - java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1); - Assertions.assertEquals(2, types1.length); - Assertions.assertEquals(List.class, types1[0]); - Assertions.assertEquals(DemoService.class.getMethod("testReturnType1", new Class[]{String.class}).getGenericReturnType(), types1[1]); - - Invocation inv2 = new RpcInvocation("testReturnType2", new Class[]{String.class}, null, null, invoker); - java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2); - Assertions.assertEquals(2, types2.length); - Assertions.assertEquals(String.class, types2[0]); - Assertions.assertEquals(String.class, types2[1]); - - Invocation inv3 = new RpcInvocation("testReturnType3", new Class[]{String.class}, null, null, invoker); - java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3); - Assertions.assertEquals(2, types3.length); - Assertions.assertEquals(List.class, types3[0]); - java.lang.reflect.Type genericReturnType3 = DemoService.class.getMethod("testReturnType3", new Class[]{String.class}).getGenericReturnType(); - Assertions.assertEquals(((ParameterizedType) genericReturnType3).getActualTypeArguments()[0], types3[1]); - - Invocation inv4 = new RpcInvocation("testReturnType4", new Class[]{String.class}, null, null, invoker); - java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4); - Assertions.assertEquals(2, types4.length); - Assertions.assertNull(types4[0]); - Assertions.assertNull(types4[1]); - - Invocation inv5 = new RpcInvocation("testReturnType5", new Class[]{String.class}, null, null, invoker); - java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5); - Assertions.assertEquals(2, types5.length); - Assertions.assertEquals(Map.class, types5[0]); - java.lang.reflect.Type genericReturnType5 = DemoService.class.getMethod("testReturnType5", new Class[]{String.class}).getGenericReturnType(); - Assertions.assertEquals(((ParameterizedType) genericReturnType5).getActualTypeArguments()[0], types5[1]); - } -} +/* + * 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.dubbo.rpc.support; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcInvocation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.ParameterizedType; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; + +public class RpcUtilsTest { + + /** + * regular scenario: async invocation in URL + * verify: 1. whether invocationId is set correctly, 2. idempotent or not + */ + @Test + public void testAttachInvocationIdIfAsync_normal() { + URL url = URL.valueOf("dubbo://localhost/?test.async=true"); + Map attachments = new HashMap(); + attachments.put("aa", "bb"); + Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}, attachments); + RpcUtils.attachInvocationIdIfAsync(url, inv); + long id1 = RpcUtils.getInvocationId(inv); + RpcUtils.attachInvocationIdIfAsync(url, inv); + long id2 = RpcUtils.getInvocationId(inv); + assertEquals(id1, id2); // verify if it's idempotent + assertTrue(id1 >= 0); + assertEquals("bb", attachments.get("aa")); + } + + /** + * scenario: sync invocation, no attachment added by default + * verify: no id attribute added in attachment + */ + @Test + public void testAttachInvocationIdIfAsync_sync() { + URL url = URL.valueOf("dubbo://localhost/"); + Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); + RpcUtils.attachInvocationIdIfAsync(url, inv); + assertNull(RpcUtils.getInvocationId(inv)); + } + + /** + * scenario: async invocation, add attachment by default + * verify: no error report when the original attachment is null + */ + @Test + public void testAttachInvocationIdIfAsync_nullAttachments() { + URL url = URL.valueOf("dubbo://localhost/?test.async=true"); + Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); + RpcUtils.attachInvocationIdIfAsync(url, inv); + assertTrue(RpcUtils.getInvocationId(inv) >= 0L); + } + + /** + * scenario: explicitly configure to not add attachment + * verify: no id attribute added in attachment + */ + @Test + public void testAttachInvocationIdIfAsync_forceNotAttache() { + URL url = URL.valueOf("dubbo://localhost/?test.async=true&" + AUTO_ATTACH_INVOCATIONID_KEY + "=false"); + Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); + RpcUtils.attachInvocationIdIfAsync(url, inv); + assertNull(RpcUtils.getInvocationId(inv)); + } + + /** + * scenario: explicitly configure to add attachment + * verify: id attribute added in attachment + */ + @Test + public void testAttachInvocationIdIfAsync_forceAttache() { + URL url = URL.valueOf("dubbo://localhost/?" + AUTO_ATTACH_INVOCATIONID_KEY + "=true"); + Invocation inv = new RpcInvocation("test", new Class[]{}, new String[]{}); + RpcUtils.attachInvocationIdIfAsync(url, inv); + assertNotNull(RpcUtils.getInvocationId(inv)); + } + + @Test + public void testGetReturnTypes() throws Exception { + Invoker invoker = mock(Invoker.class); + given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService")); + Invocation inv = new RpcInvocation("testReturnType", new Class[]{String.class}, null, null, invoker); + + java.lang.reflect.Type[] types = RpcUtils.getReturnTypes(inv); + Assertions.assertEquals(2, types.length); + Assertions.assertEquals(String.class, types[0]); + Assertions.assertEquals(String.class, types[1]); + + Invocation inv1 = new RpcInvocation("testReturnType1", new Class[]{String.class}, null, null, invoker); + java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1); + Assertions.assertEquals(2, types1.length); + Assertions.assertEquals(List.class, types1[0]); + Assertions.assertEquals(DemoService.class.getMethod("testReturnType1", new Class[]{String.class}).getGenericReturnType(), types1[1]); + + Invocation inv2 = new RpcInvocation("testReturnType2", new Class[]{String.class}, null, null, invoker); + java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2); + Assertions.assertEquals(2, types2.length); + Assertions.assertEquals(String.class, types2[0]); + Assertions.assertEquals(String.class, types2[1]); + + Invocation inv3 = new RpcInvocation("testReturnType3", new Class[]{String.class}, null, null, invoker); + java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3); + Assertions.assertEquals(2, types3.length); + Assertions.assertEquals(List.class, types3[0]); + java.lang.reflect.Type genericReturnType3 = DemoService.class.getMethod("testReturnType3", new Class[]{String.class}).getGenericReturnType(); + Assertions.assertEquals(((ParameterizedType) genericReturnType3).getActualTypeArguments()[0], types3[1]); + + Invocation inv4 = new RpcInvocation("testReturnType4", new Class[]{String.class}, null, null, invoker); + java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4); + Assertions.assertEquals(2, types4.length); + Assertions.assertNull(types4[0]); + Assertions.assertNull(types4[1]); + + Invocation inv5 = new RpcInvocation("testReturnType5", new Class[]{String.class}, null, null, invoker); + java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5); + Assertions.assertEquals(2, types5.length); + Assertions.assertEquals(Map.class, types5[0]); + java.lang.reflect.Type genericReturnType5 = DemoService.class.getMethod("testReturnType5", new Class[]{String.class}).getGenericReturnType(); + Assertions.assertEquals(((ParameterizedType) genericReturnType5).getActualTypeArguments()[0], types5[1]); + } +} diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java index 4344baf533..c0975301ad 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java @@ -288,14 +288,14 @@ class CallbackServiceCodec { switch (callbackstatus) { case CallbackServiceCodec.CALLBACK_CREATE: try { - return referOrDestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true); + return referOrDestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt((String) inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true); } catch (Exception e) { logger.error(e.getMessage(), e); throw new IOException(StringUtils.toString(e)); } case CallbackServiceCodec.CALLBACK_DESTROY: try { - return referOrDestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), false); + return referOrDestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt((String) inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), false); } catch (Exception e) { throw new IOException(StringUtils.toString(e)); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index 69a0629c4e..353501e6de 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -125,9 +125,9 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec Map map = (Map) in.readObject(Map.class); if (map != null && map.size() > 0) { - Map attachment = getAttachments(); + Map attachment = getAttachments(); if (attachment == null) { - attachment = new HashMap(); + attachment = new HashMap(); } attachment.putAll(map); setAttachments(attachment); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java index 9515a1ba51..232f8301e8 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java @@ -72,6 +72,7 @@ public class DecodeableRpcResult extends AppResponse implements Codec, Decodeabl @Override public Object decode(Channel channel, InputStream input) throws IOException { + log.debug("Decoding in thread -- " + Thread.currentThread().getName()); ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType) .deserialize(channel.getUrl(), input); @@ -153,7 +154,7 @@ public class DecodeableRpcResult extends AppResponse implements Codec, Decodeabl private void handleAttachment(ObjectInput in) throws IOException { try { - setAttachments((Map) in.readObject(Map.class)); + setAttachments((Map) in.readObject(Map.class)); } catch (ClassNotFoundException e) { rethrow(e); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java index f9134379ad..858aecee67 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboCodec.java @@ -175,8 +175,8 @@ public class DubboCodec extends ExchangeCodec { RpcInvocation inv = (RpcInvocation) data; out.writeUTF(version); - out.writeUTF(inv.getAttachment(PATH_KEY)); - out.writeUTF(inv.getAttachment(VERSION_KEY)); + out.writeUTF((String) inv.getAttachment(PATH_KEY)); + out.writeUTF((String) inv.getAttachment(VERSION_KEY)); out.writeUTF(inv.getMethodName()); out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes())); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java index 1a9640c796..0c1631d67a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java @@ -243,7 +243,7 @@ public class DubboProtocol extends AbstractProtocol { boolean isCallBackServiceInvoke = false; boolean isStubServiceInvoke = false; int port = channel.getLocalAddress().getPort(); - String path = inv.getAttachments().get(PATH_KEY); + String path = (String) inv.getAttachments().get(PATH_KEY); // if it's callback service on client side isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(STUB_EVENT_KEY)); @@ -258,7 +258,7 @@ public class DubboProtocol extends AbstractProtocol { inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString()); } - String serviceKey = serviceKey(port, path, inv.getAttachments().get(VERSION_KEY), inv.getAttachments().get(GROUP_KEY)); + String serviceKey = serviceKey(port, path, (String) inv.getAttachments().get(VERSION_KEY), (String) inv.getAttachments().get(GROUP_KEY)); DubboExporter exporter = (DubboExporter) exporterMap.get(serviceKey); if (exporter == null) { diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java index a495775d11..77eae2cefc 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java @@ -29,6 +29,7 @@ import org.apache.dubbo.remoting.exchange.Exchangers; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -114,6 +115,20 @@ final class LazyConnectExchangeClient implements ExchangeClient { return client.request(request, timeout); } + @Override + public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { + warning(); + initClient(); + return client.request(request, executor); + } + + @Override + public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { + warning(); + initClient(); + return client.request(request, timeout, executor); + } + /** * If {@link #REQUEST_WITH_WARNING_KEY} is configured, then warn once every 5000 invocations. */ diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java index bda87cae7f..fcc4f8078e 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java @@ -27,6 +27,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeHandler; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.dubbo.remoting.Constants.RECONNECT_KEY; @@ -80,6 +81,16 @@ final class ReferenceCountExchangeClient implements ExchangeClient { return client.request(request, timeout); } + @Override + public CompletableFuture request(Object request, ExecutorService executor) throws RemotingException { + return client.request(request, executor); + } + + @Override + public CompletableFuture request(Object request, int timeout, ExecutorService executor) throws RemotingException { + return client.request(request, timeout, executor); + } + @Override public boolean isConnected() { return client.isConnected(); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java index f992b06c54..8f5f860047 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java @@ -111,8 +111,9 @@ public class ImplicitCallBackTest { ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = new ConsumerMethodModel.AsyncMethodInfo(); asyncMethodInfo.setOnthrowInstance(notify); asyncMethodInfo.setOnthrowMethod(onThrowMethod); - attitudes.put("get", asyncMethodInfo); - ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), new ConsumerModel(consumerUrl.getServiceKey(), IDemoService.class, demoProxy, IDemoService.class.getMethods(), attitudes)); + ConsumerModel consumerModel = new ConsumerModel(consumerUrl.getServiceInterface(), "Dubbo", "1.0.0", IDemoService.class); + consumerModel.getMethodModel("get").addAttribute(Constants.ASYNC_KEY, asyncMethodInfo); + ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), consumerModel); } //================================================================================================ @@ -122,8 +123,9 @@ public class ImplicitCallBackTest { ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = new ConsumerMethodModel.AsyncMethodInfo(); asyncMethodInfo.setOnreturnInstance(notify); asyncMethodInfo.setOnreturnMethod(onReturnMethod); - attitudes.put("get", asyncMethodInfo); - ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), new ConsumerModel(consumerUrl.getServiceKey(), IDemoService.class, demoProxy, IDemoService.class.getMethods(), attitudes)); + ConsumerModel consumerModel = new ConsumerModel(consumerUrl.getServiceInterface(), "Dubbo", "1.0.0", IDemoService.class); + consumerModel.getMethodModel("get").addAttribute(Constants.ASYNC_KEY, asyncMethodInfo); + ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), consumerModel); } public void initImplicitCallBackURL_onlyOninvoke() throws Exception { @@ -131,8 +133,9 @@ public class ImplicitCallBackTest { ConsumerMethodModel.AsyncMethodInfo asyncMethodInfo = new ConsumerMethodModel.AsyncMethodInfo(); asyncMethodInfo.setOninvokeInstance(notify); asyncMethodInfo.setOninvokeMethod(onInvokeMethod); - attitudes.put("get", asyncMethodInfo); - ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), new ConsumerModel(consumerUrl.getServiceKey(), IDemoService.class, demoProxy, IDemoService.class.getMethods(), attitudes)); + ConsumerModel consumerModel = new ConsumerModel(consumerUrl.getServiceInterface(), "Dubbo", "1.0.0", IDemoService.class); + consumerModel.getMethodModel("get").addAttribute(Constants.ASYNC_KEY, asyncMethodInfo); + ApplicationModel.initConsumerModel(consumerUrl.getServiceKey(), consumerModel); } @Test diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java index 4512d29a6a..ab36f14a13 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java @@ -27,6 +27,7 @@ import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl; import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -68,7 +69,7 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String result = invoke.telnet(mockChannel, "echo(\"ok\")"); @@ -83,8 +84,8 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")"); assertTrue(result.contains("result: \"ok\"")); @@ -98,8 +99,9 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + try { invoke.telnet(mockChannel, "sayHello(null)"); } catch (Exception ex) { @@ -114,8 +116,8 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = invoke.telnet(mockChannel, "getType(\"High\")"); assertTrue(result.contains("result: \"High\"")); @@ -130,8 +132,9 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + String result = invoke.telnet(mockChannel, "getPerson({\"name\":\"zhangsan\",\"age\":12,\"class\":\"org.apache.dubbo.rpc.protocol.dubbo.support.Person\"})"); assertTrue(result.contains("result: 12")); } @@ -144,8 +147,9 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + String param = "{\"name\":\"Dubbo\",\"age\":8}"; String result = invoke.telnet(mockChannel, "getPerson(" + param + ")"); assertTrue(result.contains("Please use the select command to select the method you want to invoke. eg: select 1")); @@ -161,8 +165,9 @@ public class InvokerTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); - ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); + ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); + String param = "{\"name\":\"Dubbo\",\"age\":8},{\"name\":\"Apache\",\"age\":20}"; String result = invoke.telnet(mockChannel, "getPerson(" + param + ")"); assertTrue(result.contains("result: 28")); diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java index 23306a5c64..1b2e33daeb 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java @@ -66,7 +66,7 @@ public class ListTelnetHandlerTest { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = list.telnet(mockChannel, "-l DemoService"); @@ -80,7 +80,7 @@ public class ListTelnetHandlerTest { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = list.telnet(mockChannel, "DemoService"); @@ -94,11 +94,11 @@ public class ListTelnetHandlerTest { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn(null); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = list.telnet(mockChannel, ""); - assertEquals("PROVIDER:\r\norg.apache.dubbo.rpc.protocol.dubbo.support.DemoService\r\n", result); + assertEquals("PROVIDER:\r\norg.apache.dubbo.rpc.protocol.dubbo.support.DemoService:1.0.0\r\n", result); } @Test @@ -106,11 +106,11 @@ public class ListTelnetHandlerTest { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn(null); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = list.telnet(mockChannel, "-l"); - assertEquals("PROVIDER:\r\norg.apache.dubbo.rpc.protocol.dubbo.support.DemoService -> published: N\r\n", result); + assertEquals("PROVIDER:\r\norg.apache.dubbo.rpc.protocol.dubbo.support.DemoService:1.0.0 -> published: N\r\n", result); } @Test @@ -118,12 +118,12 @@ public class ListTelnetHandlerTest { mockChannel = mock(Channel.class); given(mockChannel.getAttribute("telnet.service")).willReturn("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService"); - ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", "Dubbo", "1.0.0", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel("org.apache.dubbo.rpc.protocol.dubbo.support.DemoService", providerModel); String result = list.telnet(mockChannel, ""); assertTrue(result.startsWith("Use default service org.apache.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n" + - "org.apache.dubbo.rpc.protocol.dubbo.support.DemoService (as provider):\r\n")); + "org.apache.dubbo.rpc.protocol.dubbo.support.DemoService:1.0.0 (as provider):\r\n")); for (Method method : DemoService.class.getMethods()) { assertTrue(result.contains(method.getName())); } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java index df8bf470ad..222f43a35a 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/telnet/SelectTelnetHandlerTest.java @@ -25,6 +25,7 @@ import org.apache.dubbo.rpc.model.ProviderModel; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoService; import org.apache.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl; import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -71,7 +72,7 @@ public class SelectTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), "", "", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String result = select.telnet(mockChannel, "1"); @@ -86,7 +87,7 @@ public class SelectTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), "", "", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String result = select.telnet(mockChannel, "index"); @@ -107,7 +108,7 @@ public class SelectTelnetHandlerTest { given(mockChannel.getLocalAddress()).willReturn(NetUtils.toAddress("127.0.0.1:5555")); given(mockChannel.getRemoteAddress()).willReturn(NetUtils.toAddress("127.0.0.1:20886")); - ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), new DemoServiceImpl(), DemoService.class); + ProviderModel providerModel = new ProviderModel(DemoService.class.getName(), "", "", new DemoServiceImpl(), DemoService.class); ApplicationModel.initProviderModel(DemoService.class.getName(), providerModel); String result = select.telnet(mockChannel, null); diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/DubboHessianURLConnectionFactory.java b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/DubboHessianURLConnectionFactory.java index e56841414b..27acc29a5e 100644 --- a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/DubboHessianURLConnectionFactory.java +++ b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/DubboHessianURLConnectionFactory.java @@ -33,7 +33,7 @@ public class DubboHessianURLConnectionFactory extends HessianURLConnectionFactor HessianConnection connection = super.open(url); RpcContext context = RpcContext.getContext(); for (String key : context.getAttachments().keySet()) { - connection.addHeader(Constants.DEFAULT_EXCHANGER + key, context.getAttachment(key)); + connection.addHeader(Constants.DEFAULT_EXCHANGER + key, (String) context.getAttachment(key)); } return connection; diff --git a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java index b51e6d48ef..0ce28897da 100644 --- a/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java +++ b/dubbo-rpc/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java @@ -1,56 +1,55 @@ -/* - * 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.dubbo.rpc.protocol.hessian; - -import org.apache.dubbo.remoting.Constants; -import org.apache.dubbo.rpc.RpcContext; - -import com.caucho.hessian.client.HessianConnection; -import com.caucho.hessian.client.HessianConnectionFactory; -import com.caucho.hessian.client.HessianProxyFactory; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClientBuilder; - -import java.net.URL; - -/** - * HttpClientConnectionFactory - */ -public class HttpClientConnectionFactory implements HessianConnectionFactory { - - private HttpClient httpClient; - - @Override - public void setHessianProxyFactory(HessianProxyFactory factory) { - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout((int) factory.getConnectTimeout()) - .setSocketTimeout((int) factory.getReadTimeout()) - .build(); - httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build(); - } - - @Override - public HessianConnection open(URL url) { - HttpClientConnection httpClientConnection = new HttpClientConnection(httpClient, url); - RpcContext context = RpcContext.getContext(); - for (String key : context.getAttachments().keySet()) { - httpClientConnection.addHeader(Constants.DEFAULT_EXCHANGER + key, context.getAttachment(key)); - } - return httpClientConnection; - } -} +/* + * 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.dubbo.rpc.protocol.hessian; + +import com.caucho.hessian.client.HessianConnection; +import com.caucho.hessian.client.HessianConnectionFactory; +import com.caucho.hessian.client.HessianProxyFactory; +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClientBuilder; + +import java.net.URL; + +/** + * HttpClientConnectionFactory + */ +public class HttpClientConnectionFactory implements HessianConnectionFactory { + + private HttpClient httpClient; + + @Override + public void setHessianProxyFactory(HessianProxyFactory factory) { + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout((int) factory.getConnectTimeout()) + .setSocketTimeout((int) factory.getReadTimeout()) + .build(); + httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build(); + } + + @Override + public HessianConnection open(URL url) { + HttpClientConnection httpClientConnection = new HttpClientConnection(httpClient, url); + RpcContext context = RpcContext.getContext(); + for (String key : context.getAttachments().keySet()) { + httpClientConnection.addHeader(Constants.DEFAULT_EXCHANGER + key, (String) context.getAttachment(key)); + } + return httpClientConnection; + } +} diff --git a/dubbo-rpc/dubbo-rpc-http-invoker/src/main/java/org/apache/dubbo/rpc/protocol/httpinvoker/HttpRemoteInvocation.java b/dubbo-rpc/dubbo-rpc-http-invoker/src/main/java/org/apache/dubbo/rpc/protocol/httpinvoker/HttpRemoteInvocation.java index cab3f6ea27..fca51031c7 100644 --- a/dubbo-rpc/dubbo-rpc-http-invoker/src/main/java/org/apache/dubbo/rpc/protocol/httpinvoker/HttpRemoteInvocation.java +++ b/dubbo-rpc/dubbo-rpc-http-invoker/src/main/java/org/apache/dubbo/rpc/protocol/httpinvoker/HttpRemoteInvocation.java @@ -36,14 +36,14 @@ public class HttpRemoteInvocation extends RemoteInvocation { public HttpRemoteInvocation(MethodInvocation methodInvocation) { super(methodInvocation); - addAttribute(DUBBO_ATTACHMENTS_ATTR_NAME, new HashMap(RpcContext.getContext().getAttachments())); + addAttribute(DUBBO_ATTACHMENTS_ATTR_NAME, new HashMap(RpcContext.getContext().getAttachments())); } @Override public Object invoke(Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { RpcContext context = RpcContext.getContext(); - context.setAttachments((Map) getAttribute(DUBBO_ATTACHMENTS_ATTR_NAME)); + context.setAttachments((Map) getAttribute(DUBBO_ATTACHMENTS_ATTR_NAME)); String generic = (String) getAttribute(GENERIC_KEY); if (StringUtils.isNotEmpty(generic)) { diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml b/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml new file mode 100644 index 0000000000..c5b4e89038 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml @@ -0,0 +1,61 @@ + + + + dubbo-rpc + org.apache.dubbo + ${revision} + + 4.0.0 + + dubbo-rpc-jsonrpc + + The JSON-RPC module of dubbo project + + + false + + + + + org.apache.dubbo + dubbo-rpc-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-remoting-http + ${project.parent.version} + + + org.springframework + spring-context + + + com.github.briandilley.jsonrpc4j + jsonrpc4j + + + javax.portlet + portlet-api + + + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java index 40a09da620..341d04f138 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java @@ -137,7 +137,7 @@ public class RestProtocol extends AbstractProxyProtocol { } @Override - protected T doRefer(Class serviceType, URL url) throws RpcException { + protected T getFrameworkProxy(Class serviceType, URL url) throws RpcException { // TODO more configs to add PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); diff --git a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java index 735c21b591..9166ef6fca 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcContextFilter.java @@ -69,16 +69,18 @@ public class RpcContextFilter implements ContainerRequestFilter, ClientRequestFi @Override public void filter(ClientRequestContext requestContext) throws IOException { int size = 0; - for (Map.Entry entry : RpcContext.getContext().getAttachments().entrySet()) { + for (Map.Entry entry : RpcContext.getContext().getAttachments().entrySet()) { String key = entry.getKey(); - String value = entry.getValue(); + Object value = entry.getValue(); if (illegalForRest(key) || illegalForRest(value)) { throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " must not contain ',' or '=' when using rest protocol"); } + String stringValue = (String) value; + // TODO for now we don't consider the differences of encoding and server limit if (value != null) { - size += value.getBytes(StandardCharsets.UTF_8).length; + size += stringValue.getBytes(StandardCharsets.UTF_8).length; } if (size > MAX_HEADER_SIZE) { throw new IllegalArgumentException("The attachments of " + RpcContext.class.getSimpleName() + " is too big"); @@ -95,9 +97,9 @@ public class RpcContextFilter implements ContainerRequestFilter, ClientRequestFi * @param v string value * @return true for illegal */ - private boolean illegalForRest(String v) { - if (StringUtils.isNotEmpty(v)) { - return v.contains(",") || v.contains("="); + private boolean illegalForRest(Object v) { + if (v instanceof String && StringUtils.isNotEmpty((String) v)) { + return ((String) v).contains(",") || ((String) v).contains("="); } return false; } diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java index a1682af280..b7a713dc2a 100644 --- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java +++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java @@ -56,7 +56,7 @@ public class RestProtocolTest { public void testRestProtocol() { URL url = URL.valueOf("rest://127.0.0.1:5342/rest/say?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); DemoServiceImpl server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(url.getPathKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(url.getPathKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(url.getPathKey(), providerModel); Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url)); @@ -76,7 +76,7 @@ public class RestProtocolTest { DemoServiceImpl server = new DemoServiceImpl(); Assertions.assertFalse(server.isCalled()); URL url = URL.valueOf("rest://127.0.0.1:5341/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService"); - ProviderModel providerModel = new ProviderModel(url.getPathKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(url.getPathKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(url.getPathKey(), providerModel); Exporter exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url)); @@ -94,7 +94,7 @@ public class RestProtocolTest { @Test public void testExport() { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); RpcContext.getContext().setAttachment("timeout", "200"); @@ -111,7 +111,7 @@ public class RestProtocolTest { @Test public void testNettyServer() { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); URL nettyUrl = exportUrl.addParameter(SERVER_KEY, "netty"); @@ -129,7 +129,7 @@ public class RestProtocolTest { public void testServletWithoutWebConfig() { Assertions.assertThrows(RpcException.class, () -> { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getPathKey(), providerModel); URL servletUrl = exportUrl.addParameter(SERVER_KEY, "servlet"); @@ -142,7 +142,7 @@ public class RestProtocolTest { public void testErrorHandler() { Assertions.assertThrows(RpcException.class, () -> { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); URL nettyUrl = exportUrl.addParameter(SERVER_KEY, "netty"); @@ -157,7 +157,7 @@ public class RestProtocolTest { @Test public void testInvoke() { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); @@ -172,7 +172,7 @@ public class RestProtocolTest { @Test public void testFilter() { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); URL nettyUrl = exportUrl.addParameter(SERVER_KEY, "netty") @@ -191,7 +191,7 @@ public class RestProtocolTest { @Test public void testRpcContextFilter() { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); // use RpcContextFilter @@ -215,7 +215,7 @@ public class RestProtocolTest { public void testRegFail() { Assertions.assertThrows(RuntimeException.class, () -> { DemoService server = new DemoServiceImpl(); - ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), server, DemoService.class); + ProviderModel providerModel = new ProviderModel(exportUrl.getServiceKey(), "", "", server, DemoService.class); ApplicationModel.initProviderModel(exportUrl.getServiceKey(), providerModel); URL nettyUrl = exportUrl.addParameter(EXTENSION_KEY, "com.not.existing.Filter"); diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java index d988938507..d0f712331a 100644 --- a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java +++ b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiProtocol.java @@ -72,7 +72,7 @@ public class RmiProtocol extends AbstractProxyProtocol { @Override @SuppressWarnings("unchecked") - protected T doRefer(final Class serviceType, final URL url) throws RpcException { + protected T getFrameworkProxy(final Class serviceType, final URL url) throws RpcException { final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean(); final String generic = url.getParameter(GENERIC_KEY); final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class); diff --git a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiRemoteInvocation.java b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiRemoteInvocation.java index a8ead53e96..aa83141dc3 100644 --- a/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiRemoteInvocation.java +++ b/dubbo-rpc/dubbo-rpc-rmi/src/main/java/org/apache/dubbo/rpc/protocol/rmi/RmiRemoteInvocation.java @@ -50,7 +50,7 @@ public class RmiRemoteInvocation extends RemoteInvocation { public Object invoke(Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { RpcContext context = RpcContext.getContext(); - context.setAttachments((Map) getAttribute(DUBBO_ATTACHMENTS_ATTR_NAME)); + context.setAttachments((Map) getAttribute(DUBBO_ATTACHMENTS_ATTR_NAME)); String generic = (String) getAttribute(GENERIC_KEY); if (StringUtils.isNotEmpty(generic)) { context.setAttachment(GENERIC_KEY, generic); diff --git a/dubbo-rpc/dubbo-rpc-rsocket/pom.xml b/dubbo-rpc/dubbo-rpc-rsocket/pom.xml new file mode 100644 index 0000000000..f11823452b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + org.apache.dubbo + dubbo-rpc + ${revision} + + dubbo-rpc-rsocket + jar + ${project.artifactId} + The default rpc module of dubbo project + + false + + + + org.springframework + spring-context + 4.3.16.RELEASE + + + org.apache.dubbo + dubbo-registry-multicast + ${project.version} + + + io.rsocket + rsocket-core + 0.11.14 + + + io.rsocket + rsocket-transport-netty + 0.11.14 + + + com.alibaba + fastjson + 1.2.54 + + + org.apache.dubbo + dubbo-rpc-api + ${project.parent.version} + + + + org.apache.dubbo + dubbo-remoting-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-config-api + ${project.version} + + + org.apache.dubbo + dubbo-config-spring + ${project.version} + + + org.apache.dubbo + dubbo-container-api + ${project.parent.version} + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-servlet + + + + + org.apache.dubbo + dubbo-serialization-hessian2 + ${project.parent.version} + test + + + org.apache.dubbo + dubbo-serialization-jdk + ${project.parent.version} + test + + + + + + + + + + + + + + + + + + + diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/FutureSubscriber.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/FutureSubscriber.java new file mode 100644 index 0000000000..6ec7162ec3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/FutureSubscriber.java @@ -0,0 +1,98 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.rpc.AppResponse; + +import io.rsocket.Payload; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class FutureSubscriber extends CompletableFuture implements Subscriber { + + private final Serialization serialization; + + private final Class retType; + + public FutureSubscriber(Serialization serialization, Class retType) { + this.serialization = serialization; + this.retType = retType; + } + + + @Override + public void onSubscribe(Subscription subscription) { + subscription.request(1); + } + + @Override + public void onNext(Payload payload) { + try { + AppResponse appResponse = new AppResponse(); + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = serialization.deserialize(null, dataInputStream); + + int flag = in.readByte(); + if ((flag & RSocketConstants.FLAG_ERROR) != 0) { + Throwable t = (Throwable) in.readObject(); + appResponse.setException(t); + } else { + Object value = null; + if ((flag & RSocketConstants.FLAG_NULL_VALUE) == 0) { + if (retType == null) { + value = in.readObject(); + } else { + value = in.readObject(retType); + } + appResponse.setValue(value); + } + } + + if ((flag & RSocketConstants.FLAG_HAS_ATTACHMENT) != 0) { + Map attachment = in.readObject(Map.class); + appResponse.setAttachments(attachment); + + } + + this.complete(appResponse); + + + } catch (Throwable t) { + this.completeExceptionally(t); + } + } + + @Override + public void onError(Throwable throwable) { + this.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/MetadataCodec.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/MetadataCodec.java new file mode 100644 index 0000000000..8a62597644 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/MetadataCodec.java @@ -0,0 +1,36 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import com.alibaba.fastjson.JSON; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +public class MetadataCodec { + + public static Map decodeMetadata(byte[] bytes) throws IOException { + return JSON.parseObject(new String(bytes, StandardCharsets.UTF_8), Map.class); + } + + public static byte[] encodeMetadata(Map metadata) throws IOException { + String jsonStr = JSON.toJSONString(metadata); + return jsonStr.getBytes(StandardCharsets.UTF_8); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketConstants.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketConstants.java new file mode 100644 index 0000000000..a252dbd4b6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketConstants.java @@ -0,0 +1,32 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +public class RSocketConstants { + + public static final String SERVICE_NAME_KEY = "_service_name"; + public static final String SERVICE_VERSION_KEY = "_service_version"; + public static final String METHOD_NAME_KEY = "_method_name"; + public static final String PARAM_TYPE_KEY = "_param_type"; + public static final String SERIALIZE_TYPE_KEY = "_serialize_type"; + public static final String TIMEOUT_KEY = "_timeout"; + + + public static final int FLAG_ERROR = 0x01; + public static final int FLAG_NULL_VALUE = 0x02; + public static final int FLAG_HAS_ATTACHMENT = 0x04; +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketExporter.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketExporter.java new file mode 100644 index 0000000000..3ebaea2660 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketExporter.java @@ -0,0 +1,43 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.AbstractExporter; + +import java.util.Map; + +public class RSocketExporter extends AbstractExporter { + + private final String key; + + private final Map> exporterMap; + + public RSocketExporter(Invoker invoker, String key, Map> exporterMap) { + super(invoker); + this.key = key; + this.exporterMap = exporterMap; + } + + @Override + public void unexport() { + super.unexport(); + exporterMap.remove(key); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketInvoker.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketInvoker.java new file mode 100644 index 0000000000..753643a1f8 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketInvoker.java @@ -0,0 +1,263 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.serialize.Cleanable; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.AtomicPositiveInteger; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.AsyncRpcResult; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.AbstractInvoker; +import org.apache.dubbo.rpc.support.RpcUtils; + +import io.rsocket.Payload; +import io.rsocket.RSocket; +import io.rsocket.util.DefaultPayload; +import reactor.core.Exceptions; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; + +public class RSocketInvoker extends AbstractInvoker { + + private final RSocket[] clients; + + private final AtomicPositiveInteger index = new AtomicPositiveInteger(); + + private final String version; + + private final ReentrantLock destroyLock = new ReentrantLock(); + + private final Set> invokers; + + private final Serialization serialization; + + public RSocketInvoker(Class serviceType, URL url, RSocket[] clients, Set> invokers) { + super(serviceType, url, new String[]{Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY}); + this.clients = clients; + // get version. + this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0"); + this.invokers = invokers; + + this.serialization = CodecSupport.getSerialization(getUrl()); + } + + @Override + protected Result doInvoke(final Invocation invocation) throws Throwable { + RpcInvocation inv = (RpcInvocation) invocation; + final String methodName = RpcUtils.getMethodName(invocation); + inv.setAttachment(Constants.PATH_KEY, getUrl().getPath()); + inv.setAttachment(Constants.VERSION_KEY, version); + + RSocket currentClient; + if (clients.length == 1) { + currentClient = clients[0]; + } else { + currentClient = clients[index.getAndIncrement() % clients.length]; + } + try { + //TODO support timeout + int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); + + Class retType = RpcUtils.getReturnType(invocation); + + RpcContext.getContext().setFuture(null); + //encode inv: metadata and data(arg,attachment) + Payload requestPayload = encodeInvocation(invocation); + + if (retType != null && retType.isAssignableFrom(Mono.class)) { + Mono responseMono = currentClient.requestResponse(requestPayload); + Mono bizMono = responseMono.map(new Function() { + @Override + public Object apply(Payload payload) { + return decodeData(payload); + } + }); + return AsyncRpcResult.newDefaultAsyncResult(bizMono, invocation); + } else if (retType != null && retType.isAssignableFrom(Flux.class)) { + return AsyncRpcResult.newDefaultAsyncResult(requestStream(currentClient, requestPayload), invocation); + } else { + //request-reponse + Mono responseMono = currentClient.requestResponse(requestPayload); + FutureSubscriber futureSubscriber = new FutureSubscriber(serialization, retType); + responseMono.subscribe(futureSubscriber); + return AsyncRpcResult.newDefaultAsyncResult(futureSubscriber.get(), invocation); + } + + //TODO support stream arg + } catch (Throwable t) { + throw new RpcException(t); + } + } + + + private Flux requestStream(RSocket currentClient, Payload requestPayload) { + Flux responseFlux = currentClient.requestStream(requestPayload); + Flux retFlux = responseFlux.map(new Function() { + + @Override + public Object apply(Payload payload) { + Object o = decodeData(payload); + payload.release(); + return o; + } + }); + + return retFlux; + } + + + private Object decodeData(Payload payload) { + try { + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = serialization.deserialize(null, dataInputStream); + //TODO save the copy + int flag = in.readByte(); + if ((flag & RSocketConstants.FLAG_ERROR) != 0) { + Throwable t = (Throwable) in.readObject(); + throw t; + } else { + return in.readObject(); + } + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + + @Override + public boolean isAvailable() { + if (!super.isAvailable()) { + return false; + } + for (RSocket client : clients) { + if (client.availability() > 0) { + return true; + } + } + return false; + } + + @Override + public void destroy() { + // in order to avoid closing a client multiple times, a counter is used in case of connection per jvm, every + // time when client.close() is called, counter counts down once, and when counter reaches zero, client will be + // closed. + if (super.isDestroyed()) { + return; + } else { + // double check to avoid dup close + destroyLock.lock(); + try { + if (super.isDestroyed()) { + return; + } + super.destroy(); + if (invokers != null) { + invokers.remove(this); + } + for (RSocket client : clients) { + try { + client.dispose(); + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + } + + } finally { + destroyLock.unlock(); + } + } + } + + private Payload encodeInvocation(Invocation invocation) throws IOException { + //process stream args + RpcInvocation inv = (RpcInvocation) invocation; + Class[] parameterTypes = invocation.getParameterTypes(); + Object[] args = inv.getArguments(); + if (args != null) { + for (int i = 0; i < args.length; i++) { + if(args[i]!=null) { + Class argClass = args[i].getClass(); + if (Mono.class.isAssignableFrom(argClass)) { + long id = ResourceDirectory.mountResource(args[i]); + args[i] = new ResourceInfo(id, ResourceInfo.RESOURCE_TYPE_MONO); + parameterTypes[i] = ResourceInfo.class; + } else if (Flux.class.isAssignableFrom(argClass)) { + long id = ResourceDirectory.mountResource(args[i]); + args[i] = new ResourceInfo(id, ResourceInfo.RESOURCE_TYPE_FLUX); + parameterTypes[i] = ResourceInfo.class; + } + } + } + } + + //metadata + Map metadataMap = new HashMap(); + metadataMap.put(RSocketConstants.SERVICE_NAME_KEY, invocation.getAttachment(Constants.PATH_KEY)); + metadataMap.put(RSocketConstants.SERVICE_VERSION_KEY, invocation.getAttachment(Constants.VERSION_KEY)); + metadataMap.put(RSocketConstants.METHOD_NAME_KEY, invocation.getMethodName()); + metadataMap.put(RSocketConstants.SERIALIZE_TYPE_KEY, (Byte) serialization.getContentTypeId()); + metadataMap.put(RSocketConstants.PARAM_TYPE_KEY, ReflectUtils.getDesc(parameterTypes)); + byte[] metadata = MetadataCodec.encodeMetadata(metadataMap); + + + //data + ByteArrayOutputStream dataOutputStream = new ByteArrayOutputStream(); + Serialization serialization = CodecSupport.getSerialization(getUrl()); + ObjectOutput out = serialization.serialize(getUrl(), dataOutputStream); + if (args != null) { + for (int i = 0; i < args.length; i++) { + out.writeObject(args[i]); + } + } + out.writeObject(RpcUtils.getNecessaryAttachments(inv)); + + //clean + out.flushBuffer(); + if (out instanceof Cleanable) { + ((Cleanable) out).cleanup(); + } + byte[] data = dataOutputStream.toByteArray(); + + + return DefaultPayload.create(data, metadata); + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocol.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocol.java new file mode 100644 index 0000000000..756a7a25ba --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocol.java @@ -0,0 +1,727 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import io.rsocket.AbstractRSocket; +import io.rsocket.ConnectionSetupPayload; +import io.rsocket.Payload; +import io.rsocket.RSocket; +import io.rsocket.RSocketFactory; +import io.rsocket.SocketAcceptor; +import io.rsocket.transport.netty.client.TcpClientTransport; +import io.rsocket.transport.netty.server.CloseableChannel; +import io.rsocket.transport.netty.server.TcpServerTransport; +import io.rsocket.util.DefaultPayload; +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.transport.CodecSupport; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Protocol; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.AbstractProtocol; +import org.apache.dubbo.rpc.support.RpcUtils; +import org.reactivestreams.Publisher; +import reactor.core.Exceptions; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +public class RSocketProtocol extends AbstractProtocol { + + public static final String NAME = "rsocket"; + public static final int DEFAULT_PORT = 30880; + private static final Logger log = LoggerFactory.getLogger(RSocketProtocol.class); + private static RSocketProtocol INSTANCE; + + // + private final Map serverMap = new ConcurrentHashMap(); + + // + private final Map referenceClientMap = new ConcurrentHashMap(); + + private final ConcurrentMap locks = new ConcurrentHashMap(); + + public RSocketProtocol() { + INSTANCE = this; + } + + public static RSocketProtocol getRSocketProtocol() { + if (INSTANCE == null) { + ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(RSocketProtocol.NAME); // load + } + return INSTANCE; + } + + public Collection> getExporters() { + return Collections.unmodifiableCollection(exporterMap.values()); + } + + Map> getExporterMap() { + return exporterMap; + } + + Invoker getInvoker(int port, Map metadataMap) throws RemotingException { + String path = (String) metadataMap.get(RSocketConstants.SERVICE_NAME_KEY); + String serviceKey = serviceKey(port, path, (String) metadataMap.get(RSocketConstants.SERVICE_VERSION_KEY), (String) metadataMap.get(Constants.GROUP_KEY)); + RSocketExporter exporter = (RSocketExporter) exporterMap.get(serviceKey); + if (exporter == null) { + //throw new Throwable("Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv); + throw new RuntimeException("Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch "); + } + + return exporter.getInvoker(); + } + + public Collection> getInvokers() { + return Collections.unmodifiableCollection(invokers); + } + + @Override + public int getDefaultPort() { + return DEFAULT_PORT; + } + + @Override + public Exporter export(Invoker invoker) throws RpcException { + URL url = invoker.getUrl(); + + // export service. + String key = serviceKey(url); + RSocketExporter exporter = new RSocketExporter(invoker, key, exporterMap); + exporterMap.put(key, exporter); + + openServer(url); + return exporter; + } + + private void openServer(URL url) { + String key = url.getAddress(); + //client can export a service which's only for server to invoke + boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true); + if (isServer) { + CloseableChannel server = serverMap.get(key); + if (server == null) { + synchronized (this) { + server = serverMap.get(key); + if (server == null) { + serverMap.put(key, createServer(url)); + } + } + } + } + } + + private CloseableChannel createServer(URL url) { + try { + String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost()); + int bindPort = url.getParameter(Constants.BIND_PORT_KEY, url.getPort()); + if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) { + bindIp = Constants.ANYHOST_VALUE; + } + return RSocketFactory.receive() + .acceptor(new SocketAcceptorImpl(bindPort)) + .transport(TcpServerTransport.create(bindIp, bindPort)) + .start() + .block(); + } catch (Throwable e) { + throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e); + } + } + + @Override + protected Invoker doRefer(Class serviceType, URL url) throws RpcException { + // create rpc invoker. + RSocketInvoker invoker = new RSocketInvoker(serviceType, url, getClients(url), invokers); + invokers.add(invoker); + return invoker; + } + + private RSocket[] getClients(URL url) { + // whether to share connection + boolean service_share_connect = false; + int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0); + // if not configured, connection is shared, otherwise, one connection for one service + if (connections == 0) { + service_share_connect = true; + connections = 1; + } + + RSocket[] clients = new RSocket[connections]; + for (int i = 0; i < clients.length; i++) { + if (service_share_connect) { + clients[i] = getSharedClient(url); + } else { + clients[i] = initClient(url); + } + } + return clients; + } + + /** + * Get shared connection + */ + private RSocket getSharedClient(URL url) { + String key = url.getAddress(); + RSocket client = referenceClientMap.get(key); + if (client != null) { + return client; + } + + locks.putIfAbsent(key, new Object()); + synchronized (locks.get(key)) { + if (referenceClientMap.containsKey(key)) { + return referenceClientMap.get(key); + } + + client = initClient(url); + referenceClientMap.put(key, client); + locks.remove(key); + return client; + } + } + + /** + * Create new connection + */ + private RSocket initClient(URL url) { + try { + InetSocketAddress serverAddress = new InetSocketAddress(NetUtils.filterLocalHost(url.getHost()), url.getPort()); + RSocket client = RSocketFactory.connect().keepAliveTickPeriod(Duration.ZERO).keepAliveAckTimeout(Duration.ZERO).acceptor( + rSocket -> + new AbstractRSocket() { + public Mono requestResponse(Payload payload) { + try { + ByteBuffer metadataBuffer = payload.getMetadata(); + byte[] metadataBytes = new byte[metadataBuffer.remaining()]; + metadataBuffer.get(metadataBytes, metadataBuffer.position(), metadataBuffer.remaining()); + Map metadataMap = MetadataCodec.decodeMetadata(metadataBytes); + Byte serializeId = ((Integer) metadataMap.get(RSocketConstants.SERIALIZE_TYPE_KEY)).byteValue(); + + + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = CodecSupport.getSerializationById(serializeId).deserialize(null, dataInputStream); + long id = in.readLong(); + + Mono mono = ResourceDirectory.unmountMono(id); + return mono.map(new Function() { + @Override + public Payload apply(Object o) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) 0); + out.writeObject(o); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload responsePayload = DefaultPayload.create(bos.toByteArray()); + return responsePayload; + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + }).onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) RSocketConstants.FLAG_ERROR); + out.writeObject(throwable); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload errorPayload = DefaultPayload.create(bos.toByteArray()); + return Flux.just(errorPayload); + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + }); + + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public Flux requestStream(Payload payload) { + try { + ByteBuffer metadataBuffer = payload.getMetadata(); + byte[] metadataBytes = new byte[metadataBuffer.remaining()]; + metadataBuffer.get(metadataBytes, metadataBuffer.position(), metadataBuffer.remaining()); + Map metadataMap = MetadataCodec.decodeMetadata(metadataBytes); + Byte serializeId = ((Integer) metadataMap.get(RSocketConstants.SERIALIZE_TYPE_KEY)).byteValue(); + + + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = CodecSupport.getSerializationById(serializeId).deserialize(null, dataInputStream); + long id = in.readLong(); + + Flux flux = ResourceDirectory.unmountFlux(id); + return flux.map(new Function() { + @Override + public Payload apply(Object o) { + try { + return encodeData(o, serializeId); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + }).onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + try { + Payload errorPayload = encodeError(throwable, serializeId); + return Flux.just(errorPayload); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + }); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + private Payload encodeData(Object data, byte serializeId) throws Throwable { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) 0); + out.writeObject(data); + out.flushBuffer(); + bos.flush(); + bos.close(); + return DefaultPayload.create(bos.toByteArray()); + } + + private Payload encodeError(Throwable throwable, byte serializeId) throws Throwable { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) RSocketConstants.FLAG_ERROR); + out.writeObject(throwable); + out.flushBuffer(); + bos.flush(); + bos.close(); + return DefaultPayload.create(bos.toByteArray()); + } + + }) + .transport(TcpClientTransport.create(serverAddress)) + .start() + .block(); + return client; + } catch (Throwable e) { + throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e); + } + + } + + @Override + public void destroy() { + for (String key : new ArrayList(serverMap.keySet())) { + CloseableChannel server = serverMap.remove(key); + if (server != null) { + try { + if (logger.isInfoEnabled()) { + logger.info("Close dubbo server: " + server.address()); + } + server.dispose(); + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + } + } + + for (String key : new ArrayList(referenceClientMap.keySet())) { + RSocket client = referenceClientMap.remove(key); + if (client != null) { + try { +// if (logger.isInfoEnabled()) { +// logger.info("Close dubbo connect: " + client. + "-->" + client.getRemoteAddress()); +// } + client.dispose(); + } catch (Throwable t) { + logger.warn(t.getMessage(), t); + } + } + } + super.destroy(); + } + + + //server process logic + private class SocketAcceptorImpl implements SocketAcceptor { + + private final int port; + + public SocketAcceptorImpl(int port) { + this.port = port; + } + + @Override + public Mono accept(ConnectionSetupPayload setupPayload, RSocket reactiveSocket) { + return Mono.just( + new AbstractRSocket() { + public Mono requestResponse(Payload payload) { + try { + Map metadata = decodeMetadata(payload); + Byte serializeId = ((Integer) metadata.get(RSocketConstants.SERIALIZE_TYPE_KEY)).byteValue(); + Invocation inv = decodeInvocation(payload, metadata, serializeId); + + Result result = inv.getInvoker().invoke(inv); + + Class retType = RpcUtils.getReturnType(inv); + //ok + if (retType != null && Mono.class.isAssignableFrom(retType)) { + Throwable th = result.getException(); + if (th == null) { + Mono bizMono = (Mono) result.getValue(); + Mono retMono = bizMono.map(new Function() { + @Override + public Payload apply(Object o) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) 0); + out.writeObject(o); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload responsePayload = DefaultPayload.create(bos.toByteArray()); + return responsePayload; + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + }).onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) RSocketConstants.FLAG_ERROR); + out.writeObject(throwable); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload errorPayload = DefaultPayload.create(bos.toByteArray()); + return Flux.just(errorPayload); + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + }); + + return retMono; + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) RSocketConstants.FLAG_ERROR); + out.writeObject(th); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload errorPayload = DefaultPayload.create(bos.toByteArray()); + return Mono.just(errorPayload); + } + + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + int flag = RSocketConstants.FLAG_HAS_ATTACHMENT; + + Throwable th = result.getException(); + if (th == null) { + Object ret = result.getValue(); + if (ret == null) { + flag |= RSocketConstants.FLAG_NULL_VALUE; + out.writeByte((byte) flag); + } else { + out.writeByte((byte) flag); + out.writeObject(ret); + } + } else { + flag |= RSocketConstants.FLAG_ERROR; + out.writeByte((byte) flag); + out.writeObject(th); + } + out.writeObject(result.getAttachments()); + out.flushBuffer(); + bos.flush(); + bos.close(); + + Payload responsePayload = DefaultPayload.create(bos.toByteArray()); + return Mono.just(responsePayload); + } + } catch (Throwable t) { + //application error + return Mono.error(t); + } finally { + payload.release(); + } + } + + public Flux requestStream(Payload payload) { + try { + Map metadata = decodeMetadata(payload); + Byte serializeId = ((Integer) metadata.get(RSocketConstants.SERIALIZE_TYPE_KEY)).byteValue(); + Invocation inv = decodeInvocation(payload, metadata, serializeId); + + Result result = inv.getInvoker().invoke(inv); + //Class retType = RpcUtils.getReturnType(inv); + + Throwable th = result.getException(); + if (th != null) { + Payload errorPayload = encodeError(th, serializeId); + return Flux.just(errorPayload); + } + + Flux flux = (Flux) result.getValue(); + Flux retFlux = flux.map(new Function() { + @Override + public Payload apply(Object o) { + try { + return encodeData(o, serializeId); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + }).onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + try { + Payload errorPayload = encodeError(throwable, serializeId); + return Flux.just(errorPayload); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + }); + return retFlux; + } catch (Throwable t) { + return Flux.error(t); + } finally { + payload.release(); + } + } + + private Payload encodeData(Object data, byte serializeId) throws Throwable { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) 0); + out.writeObject(data); + out.flushBuffer(); + bos.flush(); + bos.close(); + return DefaultPayload.create(bos.toByteArray()); + } + + private Payload encodeError(Throwable throwable, byte serializeId) throws Throwable { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeByte((byte) RSocketConstants.FLAG_ERROR); + out.writeObject(throwable); + out.flushBuffer(); + bos.flush(); + bos.close(); + return DefaultPayload.create(bos.toByteArray()); + } + + private Map decodeMetadata(Payload payload) throws IOException { + ByteBuffer metadataBuffer = payload.getMetadata(); + byte[] metadataBytes = new byte[metadataBuffer.remaining()]; + metadataBuffer.get(metadataBytes, metadataBuffer.position(), metadataBuffer.remaining()); + return MetadataCodec.decodeMetadata(metadataBytes); + } + + private Invocation decodeInvocation(Payload payload, Map metadata, Byte serializeId) throws RemotingException, IOException, ClassNotFoundException { + Invoker invoker = getInvoker(port, metadata); + + String serviceName = (String) metadata.get(RSocketConstants.SERVICE_NAME_KEY); + String version = (String) metadata.get(RSocketConstants.SERVICE_VERSION_KEY); + String methodName = (String) metadata.get(RSocketConstants.METHOD_NAME_KEY); + String paramType = (String) metadata.get(RSocketConstants.PARAM_TYPE_KEY); + + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + + + //TODO how to get remote address + //RpcContext rpcContext = RpcContext.getContext(); + //rpcContext.setRemoteAddress(channel.getRemoteAddress()); + + + RpcInvocation inv = new RpcInvocation(); + inv.setInvoker(invoker); + inv.setAttachment(Constants.PATH_KEY, serviceName); + inv.setAttachment(Constants.VERSION_KEY, version); + inv.setMethodName(methodName); + + + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = CodecSupport.getSerializationById(serializeId).deserialize(null, dataInputStream); + + Object[] args; + Class[] pts; + String desc = paramType; + if (desc.length() == 0) { + pts = new Class[0]; + args = new Object[0]; + } else { + pts = ReflectUtils.desc2classArray(desc); + args = new Object[pts.length]; + for (int i = 0; i < args.length; i++) { + try { + args[i] = in.readObject(pts[i]); + } catch (Exception e) { + if (log.isWarnEnabled()) { + log.warn("Decode argument failed: " + e.getMessage(), e); + } + } + } + } + + //process stream args + for (int i = 0; i < pts.length; i++) { + if (ResourceInfo.class.isAssignableFrom(pts[i])) { + ResourceInfo resourceInfo = (ResourceInfo) args[i]; + if (resourceInfo.getType() == ResourceInfo.RESOURCE_TYPE_MONO) { + pts[i] = Mono.class; + args[i] = getMonoProxy(resourceInfo.getId(), serializeId, reactiveSocket); + } else { + pts[i] = Flux.class; + args[i] = getFluxProxy(resourceInfo.getId(), serializeId, reactiveSocket); + } + } + } + + inv.setParameterTypes(pts); + inv.setArguments(args); + Map map = (Map) in.readObject(Map.class); + if (map != null && map.size() > 0) { + inv.addAttachments(map); + } + return inv; + } + }); + } + + private Mono getMonoProxy(long id, Byte serializeId, RSocket rSocket) throws IOException { + Map metadataMap = new HashMap(); + metadataMap.put(RSocketConstants.SERIALIZE_TYPE_KEY, serializeId); + byte[] metadata = MetadataCodec.encodeMetadata(metadataMap); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeLong(id); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload payload = DefaultPayload.create(bos.toByteArray(), metadata); + + Mono payloads = rSocket.requestResponse(payload); + Mono streamArg = payloads.map(new Function() { + @Override + public Object apply(Payload payload) { + return decodeData(serializeId, payload); + } + }); + return streamArg; + } + + private Flux getFluxProxy(long id, Byte serializeId, RSocket rSocket) throws IOException { + Map metadataMap = new HashMap(); + metadataMap.put(RSocketConstants.SERIALIZE_TYPE_KEY, serializeId); + byte[] metadata = MetadataCodec.encodeMetadata(metadataMap); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = CodecSupport.getSerializationById(serializeId).serialize(null, bos); + out.writeLong(id); + out.flushBuffer(); + bos.flush(); + bos.close(); + Payload payload = DefaultPayload.create(bos.toByteArray(), metadata); + + Flux payloads = rSocket.requestStream(payload); + Flux streamArg = payloads.map(new Function() { + @Override + public Object apply(Payload payload) { + return decodeData(serializeId, payload); + } + }); + return streamArg; + } + + private Object decodeData(Byte serializeId, Payload payload) { + try { + Serialization serialization = CodecSupport.getSerializationById(serializeId); + //TODO save the copy + ByteBuffer dataBuffer = payload.getData(); + byte[] dataBytes = new byte[dataBuffer.remaining()]; + dataBuffer.get(dataBytes, dataBuffer.position(), dataBuffer.remaining()); + InputStream dataInputStream = new ByteArrayInputStream(dataBytes); + ObjectInput in = serialization.deserialize(null, dataInputStream); + int flag = in.readByte(); + if ((flag & RSocketConstants.FLAG_ERROR) != 0) { + Throwable t = (Throwable) in.readObject(); + throw t; + } else { + return in.readObject(); + } + } catch (Throwable t) { + throw Exceptions.propagate(t); + } + } + + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceDirectory.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceDirectory.java new file mode 100644 index 0000000000..c1b66ac88d --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceDirectory.java @@ -0,0 +1,62 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +public class ResourceDirectory { + + private static AtomicLong idGen = new AtomicLong(1); + + private static ConcurrentHashMap id2ResourceMap = new ConcurrentHashMap(); + + + public static long mountResource(Object resource) { + long id = idGen.getAndIncrement(); + id2ResourceMap.put(id, resource); + return id; + } + + public static Object unmountResource(long id) { + return id2ResourceMap.get(id); + } + + public static long mountMono(Mono mono) { + long id = idGen.getAndIncrement(); + id2ResourceMap.put(id, mono); + return id; + } + + public static long mountFlux(Flux flux) { + long id = idGen.getAndIncrement(); + id2ResourceMap.put(id, flux); + return id; + } + + public static Mono unmountMono(long id) { + return (Mono) id2ResourceMap.get(id); + } + + public static Flux unmountFlux(long id) { + return (Flux) id2ResourceMap.get(id); + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceInfo.java b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceInfo.java new file mode 100644 index 0000000000..1c1275bd38 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/java/org/apache/dubbo/rpc/protocol/rsocket/ResourceInfo.java @@ -0,0 +1,41 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import java.io.Serializable; + +public class ResourceInfo implements Serializable { + + public static final byte RESOURCE_TYPE_MONO = 1; + public static final byte RESOURCE_TYPE_FLUX = 2; + + private final long id; + private final byte type; + + public ResourceInfo(long id, byte type) { + this.id = id; + this.type = type; + } + + public long getId() { + return id; + } + + public byte getType() { + return type; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-rsocket/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol new file mode 100644 index 0000000000..4f03810c97 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol @@ -0,0 +1 @@ +rsocket=org.apache.dubbo.rpc.protocol.rsocket.RSocketProtocol \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ConsumerDemo.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ConsumerDemo.java new file mode 100644 index 0000000000..d73bbae624 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ConsumerDemo.java @@ -0,0 +1,51 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.apache.dubbo.rpc.service.DemoService; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import reactor.core.publisher.Mono; + +import java.util.function.Consumer; + +public class ConsumerDemo { + + public static void main(String[] args) { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/dubbo-rsocket-consumer.xml"}); + context.start(); + DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy + + while (true) { + try { + Thread.sleep(1000); + Mono resultMono = demoService.requestMono("world"); // call remote method + resultMono.doOnNext(new Consumer() { + @Override + public void accept(String s) { + System.out.println(s); // get result + } + }).block(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + + + } + + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ProviderDemo.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ProviderDemo.java new file mode 100644 index 0000000000..2e7466ddbf --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/ProviderDemo.java @@ -0,0 +1,29 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class ProviderDemo { + + public static void main(String[] args) throws Exception { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/dubbo-rsocket-provider.xml"}); + context.start(); + System.in.read(); // press any key to exit + } + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocolTest.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocolTest.java new file mode 100644 index 0000000000..4d834a111c --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/protocol/rsocket/RSocketProtocolTest.java @@ -0,0 +1,260 @@ +/* + * 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.dubbo.rpc.protocol.rsocket; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +import org.apache.dubbo.rpc.Protocol; +import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.service.DemoException; +import org.apache.dubbo.rpc.service.DemoService; +import org.apache.dubbo.rpc.service.DemoServiceImpl; +import org.apache.dubbo.rpc.service.EchoService; +import org.apache.dubbo.rpc.service.RemoteService; +import org.apache.dubbo.rpc.service.RemoteServiceImpl; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RSocketProtocolTest { + + private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + + @AfterAll + public static void after() { + RSocketProtocol.getRSocketProtocol().destroy(); + } + + @Test + public void testDemoProtocol() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + assertEquals(service.getSize(new String[]{"", "", ""}), 3); + } + + @Test + public void testDubboProtocol() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + + assertEquals(service.getSize(null), -1); + assertEquals(service.getSize(new String[]{"", "", ""}), 3); + + + Map map = new HashMap(); + map.put("aa", "bb"); + Set set = service.keys(map); + assertEquals(set.size(), 1); + assertEquals(set.iterator().next(), "aa"); + service.invoke("rsocket://127.0.0.1:9010/" + DemoService.class.getName() + "", "invoke"); + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < 1024 * 32 + 32; i++) + buf.append('A'); + System.out.println(service.stringLength(buf.toString())); + + // cast to EchoService + EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + assertEquals(echo.$echo(buf.toString()), buf.toString()); + assertEquals(echo.$echo("test"), "test"); + assertEquals(echo.$echo("abcdefg"), "abcdefg"); + assertEquals(echo.$echo(1234), 1234); + } + + + @Test + public void testDubboProtocolThrowable() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + try { + service.errorTest("mike"); + } catch (Throwable t) { + assertEquals(t.getClass(), ArithmeticException.class); + } + } + + @Test + public void testDubboProtocolMultiService() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + + RemoteService remote = new RemoteServiceImpl(); + protocol.export(proxy.getInvoker(remote, RemoteService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + RemoteService.class.getName()))); + remote = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("rsocket://127.0.0.1:9010/" + RemoteService.class.getName()).addParameter("timeout", 3000l))); + + service.sayHello("world"); + + // test netty client + assertEquals("world", service.echo("world")); + assertEquals("hello world", remote.sayHello("world")); + + + EchoService serviceEcho = (EchoService) service; + assertEquals(serviceEcho.$echo("test"), "test"); + + EchoService remoteEecho = (EchoService) remote; + assertEquals(remoteEecho.$echo("ok"), "ok"); + } + + + @Test + public void testRequestMono() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + Mono result = service.requestMono("mike"); + + result.doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(s, "hello mike"); + System.out.println(s); + } + }).block(); + + Mono result2 = service.requestMonoOnError("mike"); + result2.onErrorResume(DemoException.class, new Function>() { + @Override + public Mono apply(DemoException e) { + return Mono.just(e.getClass().getName()); + } + }).doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(DemoException.class.getName(), s); + } + }).block(); + + Mono result3 = service.requestMonoBizError("mike"); + result3.onErrorResume(ArithmeticException.class, new Function>() { + @Override + public Mono apply(ArithmeticException e) { + return Mono.just(e.getClass().getName()); + } + }).doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(ArithmeticException.class.getName(), s); + } + }).block(); + + } + + @Test + public void testRequestFlux() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + + { + Flux result = service.requestFlux("mike"); + result.doOnNext(new Consumer() { + @Override + public void accept(String s) { + System.out.println(s); + } + }).blockLast(); + } + + + { + Flux result2 = service.requestFluxOnError("mike"); + result2.onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + return Flux.just(throwable.getClass().getName()); + } + }).takeLast(1).doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(DemoException.class.getName(), s); + } + }).blockLast(); + } + + { + Flux result3 = service.requestFluxBizError("mike"); + result3.onErrorResume(new Function>() { + @Override + public Publisher apply(Throwable throwable) { + return Flux.just(throwable.getClass().getName()); + } + }).takeLast(1).doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(ArithmeticException.class.getName(), s); + } + }).blockLast(); + } + } + + + @Test + public void testRequestMonoWithMonoArg() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + + Mono result = service.requestMonoWithMonoArg(Mono.just("A"), Mono.just("B")); + result.doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(s, "A B"); + System.out.println(s); + } + }).block(); + } + + + @Test + public void testRequestFluxWithFluxArg() throws Exception { + DemoService service = new DemoServiceImpl(); + protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()))); + service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rsocket://127.0.0.1:9020/" + DemoService.class.getName()).addParameter("timeout", 3000l))); + + { + Flux result = service.requestFluxWithFluxArg(Flux.just("A","B","C"), Flux.just("1","2","3")); + result.doOnNext(new Consumer() { + @Override + public void accept(String s) { + System.out.println(s); + } + }).takeLast(1).doOnNext(new Consumer() { + @Override + public void accept(String s) { + assertEquals(s, "C 3"); + } + }).blockLast(); + } + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoException.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoException.java new file mode 100644 index 0000000000..33f3a2ea8e --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoException.java @@ -0,0 +1,40 @@ +/* + * 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.dubbo.rpc.service; + +public class DemoException extends Exception { + + private static final long serialVersionUID = -8213943026163641747L; + + public DemoException() { + super(); + } + + public DemoException(String message, Throwable cause) { + super(message, cause); + } + + public DemoException(String message) { + super(message); + } + + public DemoException(Throwable cause) { + super(cause); + } + +} + diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoService.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoService.java new file mode 100644 index 0000000000..8f7a3ddc5a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoService.java @@ -0,0 +1,69 @@ +/* + * 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.dubbo.rpc.service; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Map; +import java.util.Set; + +public interface DemoService { + String sayHello(String name); + + Set keys(Map map); + + String echo(String text); + + Map echo(Map map); + + long timestamp(); + + String getThreadName(); + + int getSize(String[] strs); + + int getSize(Object[] os); + + Object invoke(String service, String method) throws Exception; + + int stringLength(String str); + + byte getbyte(byte arg); + + long add(int a, long b); + + String errorTest(String name); + + Mono requestMono(String name); + + Mono requestMonoOnError(String name); + + Mono requestMonoBizError(String name); + + Flux requestFlux(String name); + + Flux requestFluxOnError(String name); + + Flux requestFluxBizError(String name); + + Mono requestMonoWithMonoArg(Mono m1, Mono m2); + + Flux requestFluxWithFluxArg(Flux f1, Flux f2); + + +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoServiceImpl.java new file mode 100644 index 0000000000..1ba6d39a7b --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/DemoServiceImpl.java @@ -0,0 +1,174 @@ +/* + * 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.dubbo.rpc.service; + +import org.apache.dubbo.rpc.RpcContext; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.Mono; + +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +public class DemoServiceImpl implements DemoService { + public DemoServiceImpl() { + super(); + } + + public String sayHello(String name) { + return "hello " + name; + } + + public String echo(String text) { + return text; + } + + public Map echo(Map map) { + return map; + } + + public long timestamp() { + return System.currentTimeMillis(); + } + + public String getThreadName() { + return Thread.currentThread().getName(); + } + + public int getSize(String[] strs) { + if (strs == null) + return -1; + return strs.length; + } + + public int getSize(Object[] os) { + if (os == null) + return -1; + return os.length; + } + + public Object invoke(String service, String method) throws Exception { + System.out.println("RpcContext.getContext().getRemoteHost()=" + RpcContext.getContext().getRemoteHost()); + return service + ":" + method; + } + + public int stringLength(String str) { + return str.length(); + } + + + public byte getbyte(byte arg) { + return arg; + } + + + public Set keys(Map map) { + return map == null ? null : map.keySet(); + } + + + public long add(int a, long b) { + return a + b; + } + + @Override + public String errorTest(String name) { + int a = 1 / 0; + return null; + } + + public Mono requestMono(String name) { + return Mono.just("hello " + name); + } + + public Mono requestMonoOnError(String name) { + return Mono.error(new DemoException(name)); + } + + public Mono requestMonoBizError(String name) { + int a = 1 / 0; + return Mono.just("hello " + name); + } + + @Override + public Flux requestFlux(String name) { + + return Flux.create(new Consumer>() { + @Override + public void accept(FluxSink fluxSink) { + for (int i = 0; i < 5; i++) { + fluxSink.next(name + " " + i); + } + fluxSink.complete(); + } + }); + + } + + @Override + public Flux requestFluxOnError(String name) { + + return Flux.create(new Consumer>() { + @Override + public void accept(FluxSink fluxSink) { + for (int i = 0; i < 5; i++) { + fluxSink.next(name + " " + i); + } + fluxSink.error(new DemoException()); + } + }); + + } + + @Override + public Flux requestFluxBizError(String name) { + int a = 1 / 0; + return Flux.create(new Consumer>() { + @Override + public void accept(FluxSink fluxSink) { + for (int i = 0; i < 5; i++) { + fluxSink.next(name + " " + i); + } + fluxSink.error(new DemoException()); + } + }); + } + + @Override + public Mono requestMonoWithMonoArg(Mono m1, Mono m2) { + return m1.zipWith(m2, new BiFunction() { + @Override + public String apply(String s, String s2) { + return s+" "+s2; + } + }); + } + + @Override + public Flux requestFluxWithFluxArg(Flux f1, Flux f2) { + return f1.zipWith(f2, new BiFunction() { + @Override + public String apply(String s, String s2) { + return s+" "+s2; + } + }); + } + +} + diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteService.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteService.java new file mode 100644 index 0000000000..d3e21dca1e --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteService.java @@ -0,0 +1,23 @@ +/* + * 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.dubbo.rpc.service; + +import java.rmi.RemoteException; + +public interface RemoteService { + String sayHello(String name) throws RemoteException; +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteServiceImpl.java b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteServiceImpl.java new file mode 100644 index 0000000000..afb5788bde --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/java/org/apache/dubbo/rpc/service/RemoteServiceImpl.java @@ -0,0 +1,26 @@ +/* + * 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.dubbo.rpc.service; + +import java.rmi.RemoteException; + +public class RemoteServiceImpl implements RemoteService { + @Override + public String sayHello(String name) throws RemoteException { + return "hello " + name; + } +} diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/log4j.xml b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/log4j.xml new file mode 100644 index 0000000000..3c5d2ba218 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/log4j.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-consumer.xml b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-consumer.xml new file mode 100644 index 0000000000..f0f25cf923 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-consumer.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-provider.xml b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-provider.xml new file mode 100644 index 0000000000..e84fb70824 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-rsocket/src/test/resources/spring/dubbo-rsocket-provider.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java index 5f58ae7e22..912282c434 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodec.java @@ -397,7 +397,7 @@ public class ThriftCodec implements Codec2 { int seqId = nextSeqId(); - String serviceName = inv.getAttachment(INTERFACE_KEY); + String serviceName = (String) inv.getAttachment(INTERFACE_KEY); if (StringUtils.isEmpty(serviceName)) { throw new IllegalArgumentException("Could not find service name in attachment with key " @@ -491,7 +491,7 @@ public class ThriftCodec implements Codec2 { // service name protocol.writeString(serviceName); // path - protocol.writeString(inv.getAttachment(PATH_KEY)); + protocol.writeString((String) inv.getAttachment(PATH_KEY)); // dubbo request id protocol.writeI64(request.getId()); protocol.getTransport().flush(); diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java index c36213bd75..c922e6a626 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftInvoker.java @@ -18,7 +18,6 @@ package org.apache.dubbo.rpc.protocol.thrift; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.AtomicPositiveInteger; -import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.TimeoutException; import org.apache.dubbo.remoting.exchange.ExchangeClient; diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java index da8bec6a65..85e793dd66 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java @@ -20,7 +20,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.remoting.Channel; -import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.Transporter; import org.apache.dubbo.remoting.exchange.ExchangeChannel; @@ -70,7 +69,7 @@ public class ThriftProtocol extends AbstractProtocol { if (msg instanceof Invocation) { Invocation inv = (Invocation) msg; - String path = inv.getAttachments().get(PATH_KEY); + String path = (String) inv.getAttachments().get(PATH_KEY); String serviceKey = serviceKey(channel.getLocalAddress().getPort(), path, null, null); DubboExporter exporter = (DubboExporter) exporterMap.get(serviceKey); diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java index a8c66a71f8..5bb0eaca24 100644 --- a/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java +++ b/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift/ThriftCodecTest.java @@ -133,7 +133,7 @@ public class ThriftCodecTest { Request request = createRequest(); - DefaultFuture future = DefaultFuture.newFuture(channel, request, 10); + DefaultFuture future = DefaultFuture.newFuture(channel, request, 10, null); TMessage message = new TMessage("echoString", TMessageType.REPLY, ThriftCodec.getSeqId()); @@ -210,7 +210,7 @@ public class ThriftCodecTest { Request request = createRequest(); - DefaultFuture future = DefaultFuture.newFuture(channel, request, 10); + DefaultFuture future = DefaultFuture.newFuture(channel, request, 10, null); TMessage message = new TMessage("echoString", TMessageType.EXCEPTION, ThriftCodec.getSeqId()); @@ -402,10 +402,10 @@ public class ThriftCodecTest { protocol.writeI16(Short.MAX_VALUE); protocol.writeByte(ThriftCodec.VERSION); protocol.writeString( - ((RpcInvocation) request.getData()) + (String) ((RpcInvocation) request.getData()) .getAttachment(INTERFACE_KEY)); protocol.writeString( - ((RpcInvocation) request.getData()) + (String) ((RpcInvocation) request.getData()) .getAttachment(PATH_KEY)); protocol.writeI64(request.getId()); protocol.getTransport().flush(); diff --git a/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java b/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java index 08dc57cfdf..b208f28111 100644 --- a/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java +++ b/dubbo-rpc/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java @@ -110,7 +110,7 @@ public class WebServiceProtocol extends AbstractProxyProtocol { @Override @SuppressWarnings("unchecked") - protected T doRefer(final Class serviceType, final URL url) throws RpcException { + protected T getFrameworkProxy(final Class serviceType, final URL url) throws RpcException { ClientProxyFactoryBean proxyFactoryBean = new ClientProxyFactoryBean(); proxyFactoryBean.setAddress(url.setProtocol("http").toIdentityString()); proxyFactoryBean.setServiceClass(serviceType);