From 7e18549f02d6647026d143ff594033ced91a3e69 Mon Sep 17 00:00:00 2001 From: Ingo Weiss Date: Mon, 26 Nov 2018 15:35:40 +0000 Subject: [PATCH] [EJBCLIENT-311] Add timeout for discovering the destination of an EJB request Issue: https://issues.jboss.org/browse/EJBCLIENT-311 --- .../client/DiscoveryEJBClientInterceptor.java | 9 +- .../test/RemoteDiscoveryTimeoutTestCase.java | 129 ++++++++++++++++++ .../no-protocol-jboss-ejb-client.properties | 28 ++++ 3 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/jboss/ejb/client/test/RemoteDiscoveryTimeoutTestCase.java create mode 100644 src/test/resources/no-protocol-jboss-ejb-client.properties diff --git a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java index 979119618..1852e2a18 100644 --- a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java +++ b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java @@ -40,6 +40,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import javax.ejb.NoSuchEJBException; @@ -56,6 +57,7 @@ import org.wildfly.discovery.ServiceURL; import org.wildfly.discovery.ServicesQueue; import org.wildfly.naming.client.NamingProvider; +import org.wildfly.security.manager.WildFlySecurityManager; /** * The EJB client interceptor responsible for discovering the destination of a request. If a destination is already @@ -71,7 +73,8 @@ public final class DiscoveryEJBClientInterceptor implements EJBClientInterceptor private static final String[] NO_STRINGS = new String[0]; private static final boolean WILDFLY_TESTSUITE_HACK = Boolean.getBoolean("org.jboss.ejb.client.wildfly-testsuite-hack"); - + // This provides a way timeout a discovery, avoiding blocking on some edge cases. See EJBCLIENT-311. + private static final long DISCOVERY_TIMEOUT = Long.parseLong(WildFlySecurityManager.getPropertyPrivileged("org.jboss.ejb.client.discovery.timeout", "0")); /** * This interceptor's priority. @@ -369,7 +372,7 @@ private List doFirstMatchDiscovery(AbstractInvocationContext context, final Set set = context.getAttachment(BL_KEY); try (final ServicesQueue queue = discover(filterSpec)) { ServiceURL serviceURL; - while ((serviceURL = queue.takeService()) != null) { + while ((serviceURL = queue.takeService(DISCOVERY_TIMEOUT, TimeUnit.SECONDS)) != null) { final URI location = serviceURL.getLocationURI(); if (set == null || ! set.contains(location)) { // Got a match! See if there's a node affinity to set for the invocation. @@ -433,7 +436,7 @@ private List doAnyDiscovery(AbstractInvocationContext context, final int nodeless = 0; try (final ServicesQueue queue = discover(filterSpec)) { ServiceURL serviceURL; - while ((serviceURL = queue.takeService()) != null) { + while ((serviceURL = queue.takeService(DISCOVERY_TIMEOUT, TimeUnit.SECONDS)) != null) { final URI location = serviceURL.getLocationURI(); if (blacklist == null || ! blacklist.contains(location)) { // Got a match! See if there's a node affinity to set for the invocation. diff --git a/src/test/java/org/jboss/ejb/client/test/RemoteDiscoveryTimeoutTestCase.java b/src/test/java/org/jboss/ejb/client/test/RemoteDiscoveryTimeoutTestCase.java new file mode 100644 index 000000000..4c6c69614 --- /dev/null +++ b/src/test/java/org/jboss/ejb/client/test/RemoteDiscoveryTimeoutTestCase.java @@ -0,0 +1,129 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed 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.jboss.ejb.client.test; + +import javax.ejb.NoSuchEJBException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import org.jboss.ejb.client.EJBClient; +import org.jboss.ejb.client.EJBClientConnection; +import org.jboss.ejb.client.EJBClientContext; +import org.jboss.ejb.client.StatelessEJBLocator; +import org.jboss.ejb.client.URIAffinity; +import org.jboss.ejb.client.legacy.JBossEJBProperties; +import org.jboss.ejb.client.test.common.DummyServer; +import org.jboss.ejb.client.test.common.Echo; +import org.jboss.ejb.client.test.common.EchoBean; +import org.jboss.logging.Logger; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @author Ingo Weiss + */ +public class RemoteDiscoveryTimeoutTestCase { + private static final Logger logger = Logger.getLogger(RemoteDiscoveryTimeoutTestCase.class); + private static final String PROPERTIES_FILE = "no-protocol-jboss-ejb-client.properties"; + + private DummyServer server; + private boolean serverStarted = false; + + // module + private static final String APP_NAME = "my-foo-app"; + private static final String MODULE_NAME = "my-bar-module"; + private static final String DISTINCT_NAME = ""; + + private static final String SERVER_NAME = "test-server"; + + /** + * Do any general setup here + * @throws Exception + */ + @BeforeClass + public static void beforeClass() throws Exception { + // trigger the static init of the correct properties file - this also depends on running in forkMode=always + JBossEJBProperties ejbProperties = JBossEJBProperties.fromClassPath(RemoteDiscoveryTimeoutTestCase.class.getClassLoader(), PROPERTIES_FILE); + JBossEJBProperties.getContextManager().setGlobalDefault(ejbProperties); + } + + /** + * Do any test specific setup here + */ + @Before + public void beforeTest() throws Exception { + // start a server + server = new DummyServer("localhost", 6999, SERVER_NAME); + server.start(); + serverStarted = true; + logger.info("Started server ..."); + + server.register(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getSimpleName(), new EchoBean()); + logger.info("Registered module ..."); + } + + /** + * Do any test-specific tear down here. + */ + @After + public void afterTest() { + server.unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getName()); + logger.info("Unregistered module ..."); + + if (serverStarted) { + try { + this.server.stop(); + } catch (Throwable t) { + logger.info("Could not stop server", t); + } + } + logger.info("Stopped server ..."); + } + + /** + * Test a failed client discovery + */ + @Test + public void testClientDiscoveryTimeout() throws InterruptedException { + logger.info("Testing client discovery timeout of 10 seconds"); + System.setProperty("org.jboss.ejb.client.discovery.timeout", "10"); + String errorMessage = ""; + + try { + // create a proxy for invocation + final StatelessEJBLocator statelessEJBLocator = new StatelessEJBLocator<> + (Echo.class, APP_NAME, MODULE_NAME, Echo.class.getSimpleName(), DISTINCT_NAME); + final Echo proxy = EJBClient.createProxy(statelessEJBLocator); + Assert.assertNotNull("Received a null proxy", proxy); + logger.info("Created proxy for Echo: " + proxy.toString()); + + logger.info("Invoking on proxy..."); + // Invoke on the proxy. This should fail in 10 seconds or else it'll hang. + final String message = "hello!"; + final String echo = proxy.echo(message); + } catch (NoSuchEJBException nsee) { + errorMessage = nsee.getMessage(); + } + + Assert.assertTrue("Wrong error message. Expected EJBCLIENT000079.", errorMessage.contains("EJBCLIENT000079")); + } +} diff --git a/src/test/resources/no-protocol-jboss-ejb-client.properties b/src/test/resources/no-protocol-jboss-ejb-client.properties new file mode 100644 index 000000000..15de79fac --- /dev/null +++ b/src/test/resources/no-protocol-jboss-ejb-client.properties @@ -0,0 +1,28 @@ +# +# JBoss, Home of Professional Open Source. +# Copyright 2019 Red Hat, Inc., and individual contributors +# as indicated by the @author tags. +# +# Licensed 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. +# + +remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false + +remote.connections=one + +# connection to a node at protocol://host:port +remote.connection.one.host=localhost +remote.connection.one.port=6999 +remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false +remote.connection.one.username=test +remote.connection.one.password=test