From ef7dbed3cb78c2d70b64e81e7b1f786651615cc1 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Thu, 12 Oct 2017 16:49:51 -0500 Subject: [PATCH 1/4] Improve adaptability to PRNG output --- .../ejb/client/test/TransactionTestCase.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java b/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java index 88378a4a1..7b73c556f 100644 --- a/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java +++ b/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java @@ -17,8 +17,10 @@ */ package org.jboss.ejb.client.test; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -41,6 +43,8 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.wildfly.naming.client.WildFlyInitialContextFactory; import org.wildfly.naming.client.WildFlyRootContext; import org.wildfly.naming.client.util.FastHashtable; @@ -57,7 +61,6 @@ * @author Richard Achmatowicz */ public class TransactionTestCase { - private static final Logger logger = Logger.getLogger(TransactionTestCase.class); private static ContextTransactionManager txManager; private static ContextTransactionSynchronizationRegistry txSyncRegistry; @@ -212,7 +215,7 @@ private void verifyNonTxBehavior(boolean stateful, boolean sticky) throws Except txManager.begin(); HashMap replies = new HashMap<>(); String id = null; - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 30; i++) { Echo echo = (Echo) context.lookup("ejb:" + APP_NAME + "/" + MODULE_NAME + "/" + EchoBean.class.getSimpleName() + "!" + Echo.class.getName() + (stateful ? "?stateful" : "")); @@ -223,7 +226,9 @@ private void verifyNonTxBehavior(boolean stateful, boolean sticky) throws Except // Everything should clump to one node under this transaction Assert.assertEquals(sticky ? 1 : 3, replies.size()); - //Assert.assertEquals(20, replies.values().iterator().next().intValue()); + if (sticky) { + Assert.assertEquals(30, replies.values().iterator().next().intValue()); + } ids.add(id); txManager.commit(); } @@ -247,7 +252,7 @@ public void testTransactionPreference() throws Exception { HashSet id1s = new HashSet<>(); HashSet id2s = new HashSet<>(); - for (int attempts = 0; attempts < 40; attempts++) { + for (int attempts = 0; attempts < 80; attempts++) { txManager.begin(); HashMap replies = new HashMap<>(); String id1 = null; @@ -290,7 +295,7 @@ public void testTransactionPreference() throws Exception { txManager.commit(); } - // After 20 tries, we should have hit every server for each app + // After 80 tries, we should have hit every server for each app Assert.assertEquals(Stream.of("server1", "server3", "server4").collect(Collectors.toSet()), id1s); Assert.assertEquals(Stream.of("server2", "server3", "server4").collect(Collectors.toSet()), id2s); } From b140c5326cd5ae69d0d5c06eeb9f30215c33ed76 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Thu, 12 Oct 2017 16:50:32 -0500 Subject: [PATCH 2/4] Fix worker/socket leak in tests --- src/test/java/org/jboss/ejb/client/test/common/DummyServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/jboss/ejb/client/test/common/DummyServer.java b/src/test/java/org/jboss/ejb/client/test/common/DummyServer.java index 052999c79..801abb642 100644 --- a/src/test/java/org/jboss/ejb/client/test/common/DummyServer.java +++ b/src/test/java/org/jboss/ejb/client/test/common/DummyServer.java @@ -99,7 +99,7 @@ public void start() throws Exception { final OptionMap options = OptionMap.EMPTY; EndpointBuilder endpointBuilder = Endpoint.builder(); endpointBuilder.setEndpointName(this.endpointName); - endpointBuilder.buildXnioWorker(Xnio.getInstance()).populateFromOptions(options).build(); + endpointBuilder.buildXnioWorker(Xnio.getInstance()).populateFromOptions(options); endpoint = endpointBuilder.build(); From 0c11fe0409057b6c762e9426500b7aeb332ed30d Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Tue, 10 Oct 2017 16:35:05 -0500 Subject: [PATCH 3/4] EJBCLIENT-285 Reuse seed connection auth config for cluster topology created connections --- pom.xml | 2 +- .../java/org/jboss/ejb/_private/Logs.java | 3 + .../ejb/client/AbstractInvocationContext.java | 17 ++++- .../client/DiscoveryEJBClientInterceptor.java | 68 +++++++++++++++---- .../remote/DiscoveredNodeRegistry.java | 3 +- .../ejb/protocol/remote/EJBClientChannel.java | 2 +- .../protocol/remote/RemoteEJBReceiver.java | 17 ++++- .../remote/RemotingEJBDiscoveryProvider.java | 65 +++++++++++++++--- .../test/ClusteredInvocationTestCase.java | 5 +- .../clustered-jboss-ejb-client.properties | 4 +- 10 files changed, 155 insertions(+), 31 deletions(-) diff --git a/pom.xml b/pom.xml index 74ab5100e..edda8400e 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 1.2.0.Final 1.0.4.Final 1.0.0.Final - 1.1.0.Final + 1.1.5.Final 1.0.2.Final 1.4.Final diff --git a/src/main/java/org/jboss/ejb/_private/Logs.java b/src/main/java/org/jboss/ejb/_private/Logs.java index e3c09d704..071821ef8 100644 --- a/src/main/java/org/jboss/ejb/_private/Logs.java +++ b/src/main/java/org/jboss/ejb/_private/Logs.java @@ -416,6 +416,9 @@ public interface Logs extends BasicLogger { @Message(id = 509, value = "Unexpected exception processing EJB request") void unexpectedException(@Cause Throwable t); + @Message(id = 510, value = "Failed to configure SSL context") + IOException failedToConfigureSslContext(@Cause Throwable cause); + // Remote messages; no ID for brevity but should be translated @Message(value = "No such EJB: %s") diff --git a/src/main/java/org/jboss/ejb/client/AbstractInvocationContext.java b/src/main/java/org/jboss/ejb/client/AbstractInvocationContext.java index e7e55c912..cd40f89da 100644 --- a/src/main/java/org/jboss/ejb/client/AbstractInvocationContext.java +++ b/src/main/java/org/jboss/ejb/client/AbstractInvocationContext.java @@ -23,8 +23,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import javax.transaction.Transaction; - import org.wildfly.common.Assert; import org.wildfly.transaction.client.AbstractTransaction; @@ -41,6 +39,21 @@ public abstract class AbstractInvocationContext extends Attachable { private Affinity weakAffinity = Affinity.NONE; private URI destination; private Affinity targetAffinity; + private String initialCluster; + + /** + * Gets the initial cluster assignment by discovery, if any + * + * @return the initial cluster if assigned + */ + public String getInitialCluster() { + return initialCluster; + } + + void setInitialCluster(String initialCluster) { + this.initialCluster = initialCluster; + } + private Map contextData; private AbstractTransaction transaction; diff --git a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java index b199c2168..5b58cfb00 100644 --- a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java +++ b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java @@ -33,11 +33,13 @@ import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; import javax.ejb.NoSuchEJBException; @@ -336,17 +338,7 @@ private List doFirstMatchDiscovery(AbstractInvocationContext context, if (fallbackFilterSpec != null) { assert context.getLocator().getAffinity() instanceof ClusterAffinity; Logs.INVOCATION.tracef("Performed first-match discovery, no match, falling back to cluster discovery"); - final List problems2 = doClusterDiscovery(context, fallbackFilterSpec); - if (problems2.isEmpty()) { - return problems; - } else if (problems.isEmpty()) { - return problems2; - } else { - final ArrayList problems3 = new ArrayList<>(problems.size() + problems2.size()); - problems3.addAll(problems); - problems3.addAll(problems2); - return problems3; - } + return merge(problems, doClusterDiscovery(context, fallbackFilterSpec)); } else { // no match! Logs.INVOCATION.tracef("Performed first-match discovery, no match"); @@ -354,6 +346,19 @@ private List doFirstMatchDiscovery(AbstractInvocationContext context, return problems; } + private static List merge(List problems, List problems2) { + if (problems2.isEmpty()) { + return problems; + } else if (problems.isEmpty()) { + return problems2; + } else { + final ArrayList problems3 = new ArrayList<>(problems.size() + problems2.size()); + problems3.addAll(problems); + problems3.addAll(problems2); + return problems3; + } + } + private List doAnyDiscovery(AbstractInvocationContext context, final FilterSpec filterSpec, final EJBLocator locator) { Logs.INVOCATION.tracef("Performing any discovery(locator = %s, weak affinity = %s, filter spec = %s)", context.getLocator(), context.getWeakAffinity(), filterSpec); final List problems; @@ -361,6 +366,8 @@ private List doAnyDiscovery(AbstractInvocationContext context, final final Set blacklist = context.getAttachment(BL_KEY); final Map nodes = new HashMap<>(); final Map uris = new HashMap<>(); + final Map> clusterAssociations = new HashMap<>(); + int nodeless = 0; try (final ServicesQueue queue = discover(filterSpec)) { ServiceURL serviceURL; @@ -382,7 +389,24 @@ private List doAnyDiscovery(AbstractInvocationContext context, final nodeless++; } } - context.setDestination(location); + + // Handle multiple cluster specifications per entry, and also multiple entries with + // cluster specifications that refer to the same URI. Currently multi-membership is + // represented in the latter form, however, handle the first form as well, just in + // case this changes in the future. + final List clusters = serviceURL.getAttributeValues(FILTER_ATTR_CLUSTER); + if (clusters != null) { + for (AttributeValue cluster : clusters) { + List list = clusterAssociations.putIfAbsent(location, Collections.singletonList(cluster.toString())); + if (list != null) { + if (!(list instanceof ArrayList)) { + list = new ArrayList<>(list); + clusterAssociations.put(location, list); + } + list.add(cluster.toString()); + } + } + } } } problems = queue.getProblems(); @@ -430,11 +454,31 @@ private List doAnyDiscovery(AbstractInvocationContext context, final Logs.INVOCATION.tracef("Performed first-match discovery, nodes > 1, URI selector used(target affinity(node) = %s, destination = %s)", nodeName, location); } + // TODO DeploymentNodeSelector should be enhanced to handle URIs that are members of more than one cluster + + // Clients typically do not have an auth policy for nodes which are dynamically discovered + // from cluster topology info. Anytime such a node is selected, we must register the + // associated cluster with the invocation, so that an effective auth config can be + // determined. Randomly pick a cluster if there is more than one. + selectCluster(context, clusterAssociations, location); context.setDestination(location); if (nodeName != null) context.setTargetAffinity(new NodeAffinity(nodeName)); return problems; } + private void selectCluster(AbstractInvocationContext context, Map> clusterAssociations, URI location) { + List associations = clusterAssociations.get(location); + String cluster = null; + if (associations != null) { + cluster = (associations.size() == 1) ? associations.get(0) : + associations.get(ThreadLocalRandom.current().nextInt(associations.size())); + + } + if (cluster != null) { + context.setInitialCluster(cluster); + } + } + private List doClusterDiscovery(AbstractInvocationContext context, final FilterSpec filterSpec) { Logs.INVOCATION.tracef("Performing cluster discovery(locator = %s, weak affinity = %s, filter spec = %s)", context.getLocator(), context.getWeakAffinity(), filterSpec); Map nodes = new HashMap<>(); diff --git a/src/main/java/org/jboss/ejb/protocol/remote/DiscoveredNodeRegistry.java b/src/main/java/org/jboss/ejb/protocol/remote/DiscoveredNodeRegistry.java index 237c9a859..1a771598b 100644 --- a/src/main/java/org/jboss/ejb/protocol/remote/DiscoveredNodeRegistry.java +++ b/src/main/java/org/jboss/ejb/protocol/remote/DiscoveredNodeRegistry.java @@ -18,6 +18,7 @@ package org.jboss.ejb.protocol.remote; +import java.net.URI; import java.util.List; /** @@ -29,7 +30,7 @@ interface DiscoveredNodeRegistry { List getAllNodeInformation(); - void addNode(String clusterName, String nodeName); + void addNode(String clusterName, String nodeName, URI registeredBy); void removeNode(String clusterName, String nodeName); diff --git a/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java b/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java index f1f92c657..a291de906 100644 --- a/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java +++ b/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java @@ -234,7 +234,7 @@ private void processMessage(final MessageInputStream message) { int memberCount = StreamUtils.readPackedSignedInt32(message); for (int j = 0; j < memberCount; j ++) { final String nodeName = message.readUTF(); - discoveredNodeRegistry.addNode(clusterName, nodeName); + discoveredNodeRegistry.addNode(clusterName, nodeName, channel.getConnection().getPeerURI()); final NodeInformation nodeInformation = discoveredNodeRegistry.getNodeInformation(nodeName); Logs.INVOCATION.debugf("Received CLUSTER_TOPOLOGY(%x) message, registering cluster %s to node %s", msg, clusterName, nodeName); diff --git a/src/main/java/org/jboss/ejb/protocol/remote/RemoteEJBReceiver.java b/src/main/java/org/jboss/ejb/protocol/remote/RemoteEJBReceiver.java index d5fed4050..35507f351 100644 --- a/src/main/java/org/jboss/ejb/protocol/remote/RemoteEJBReceiver.java +++ b/src/main/java/org/jboss/ejb/protocol/remote/RemoteEJBReceiver.java @@ -28,7 +28,10 @@ import javax.ejb.CreateException; +import org.jboss.ejb.client.AbstractInvocationContext; +import org.jboss.ejb.client.Affinity; import org.jboss.ejb.client.AttachmentKey; +import org.jboss.ejb.client.ClusterAffinity; import org.jboss.ejb.client.EJBReceiver; import org.jboss.ejb.client.EJBReceiverContext; import org.jboss.ejb.client.EJBReceiverInvocationContext; @@ -122,7 +125,7 @@ EJBClientChannel getClientChannel(final Connection connection) throws IOExceptio protected void processInvocation(final EJBReceiverInvocationContext receiverContext) throws Exception { final AuthenticationContext authenticationContext = receiverContext.getAuthenticationContext(); - final IoFuture futureConnection = getConnection(receiverContext.getClientInvocationContext().getDestination(), authenticationContext); + final IoFuture futureConnection = getConnection(receiverContext.getClientInvocationContext(), receiverContext.getClientInvocationContext().getDestination(), authenticationContext); // this actually causes the invocation to move forward futureConnection.addNotifier(notifier, receiverContext); } @@ -140,7 +143,7 @@ protected SessionID createSession(final EJBReceiverSessionCreationContext contex final StatelessEJBLocator statelessLocator = context.getClientInvocationContext().getLocator().asStateless(); final AuthenticationContext authenticationContext = context.getAuthenticationContext(); try { - IoFuture futureConnection = getConnection(context.getClientInvocationContext().getDestination(), authenticationContext); + IoFuture futureConnection = getConnection(context.getClientInvocationContext(), context.getClientInvocationContext().getDestination(), authenticationContext); final ConnectionPeerIdentity identity = futureConnection.getInterruptibly(); final EJBClientChannel ejbClientChannel = getClientChannel(identity.getConnection()); final StatefulEJBLocator result = ejbClientChannel.openSession(statelessLocator, identity, context.getClientInvocationContext()); @@ -170,7 +173,15 @@ protected boolean isConnected(final URI uri) { } } - private IoFuture getConnection(final URI target, @NotNull AuthenticationContext authenticationContext) throws Exception { + private IoFuture getConnection(final AbstractInvocationContext context, final URI target, @NotNull AuthenticationContext authenticationContext) throws Exception { + Affinity affinity = context.getLocator().getAffinity(); + String cluster = (affinity instanceof ClusterAffinity) ? ((ClusterAffinity) affinity).getClusterName() : context.getInitialCluster(); + + if (cluster != null) { + return doPrivileged((PrivilegedAction>) () -> + discoveredNodeRegistry.getConnectedIdentityUsingClusterEffective(Endpoint.getCurrent(), target, "ejb", "jboss", authenticationContext, cluster)); + } + return doPrivileged((PrivilegedAction>) () -> Endpoint.getCurrent().getConnectedIdentity(target, "ejb", "jboss", authenticationContext)); } } diff --git a/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java b/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java index e29fcb866..20fe6a169 100644 --- a/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java +++ b/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java @@ -30,9 +30,11 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.security.GeneralSecurityException; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -40,12 +42,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import javax.net.ssl.SSLContext; + import org.jboss.ejb._private.Logs; import org.jboss.ejb.client.EJBClientConnection; import org.jboss.ejb.client.EJBClientContext; import org.jboss.ejb.client.EJBModuleIdentifier; import org.jboss.remoting3.ConnectionPeerIdentity; import org.jboss.remoting3.Endpoint; +import org.wildfly.common.Assert; import org.wildfly.common.net.CidrAddressTable; import org.wildfly.common.net.Inet; import org.wildfly.discovery.AllFilterSpec; @@ -57,7 +62,10 @@ import org.wildfly.discovery.spi.DiscoveryProvider; import org.wildfly.discovery.spi.DiscoveryRequest; import org.wildfly.discovery.spi.DiscoveryResult; +import org.wildfly.security.auth.client.AuthenticationConfiguration; import org.wildfly.security.auth.client.AuthenticationContext; +import org.wildfly.security.auth.client.AuthenticationContextConfigurationClient; +import org.xnio.FailedIoFuture; import org.xnio.IoFuture; import org.xnio.OptionMap; @@ -68,12 +76,18 @@ */ final class RemotingEJBDiscoveryProvider implements DiscoveryProvider, DiscoveredNodeRegistry { + static final AuthenticationContextConfigurationClient AUTH_CONFIGURATION_CLIENT = doPrivileged(AuthenticationContextConfigurationClient.ACTION); + private final ConcurrentHashMap nodes = new ConcurrentHashMap<>(); private final Set failedDestinations = Collections.newSetFromMap(new ConcurrentHashMap()); private final ConcurrentHashMap> clusterNodes = new ConcurrentHashMap<>(); + private final ConcurrentHashMap effectiveAuthURIs = new ConcurrentHashMap<>(); + + + public RemotingEJBDiscoveryProvider() { Endpoint.getCurrent(); //this will blow up if remoting is not present, preventing this from being registered } @@ -86,7 +100,8 @@ public List getAllNodeInformation() { return new ArrayList<>(nodes.values()); } - public void addNode(final String clusterName, final String nodeName) { + public void addNode(final String clusterName, final String nodeName, URI registeredBy) { + effectiveAuthURIs.putIfAbsent(clusterName, registeredBy); clusterNodes.computeIfAbsent(clusterName, ignored -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(nodeName); } @@ -97,6 +112,7 @@ public void removeNode(final String clusterName, final String nodeName) { public void removeCluster(final String clusterName) { final Set removed = clusterNodes.remove(clusterName); if (removed != null) removed.clear(); + effectiveAuthURIs.remove(clusterName); } public DiscoveryRequest discover(final ServiceType serviceType, final FilterSpec filterSpec, final DiscoveryResult result) { @@ -132,7 +148,7 @@ public DiscoveryRequest discover(final ServiceType serviceType, final FilterSpec } ok = true; Logs.INVOCATION.tracef("EJB discovery provider: attempting to connect to configured connection %s", uri); - discoveryAttempt.connectAndDiscover(uri); + discoveryAttempt.connectAndDiscover(uri, null); } // also establish cluster nodes if known for (Map.Entry> entry : clusterNodes.entrySet()) { @@ -167,7 +183,7 @@ public DiscoveryRequest discover(final ServiceType serviceType, final FilterSpec if (! failedDestinations.contains(uri)) { maxConnections--; Logs.INVOCATION.tracef("EJB discovery provider: attempting to connect to cluster %s connection %s", clusterName, uri); - discoveryAttempt.connectAndDiscover(uri); + discoveryAttempt.connectAndDiscover(uri, clusterName); ok = true; continue nodeLoop; } @@ -190,7 +206,7 @@ public DiscoveryRequest discover(final ServiceType serviceType, final FilterSpec } URI destination = connection.getDestination(); Logs.INVOCATION.tracef("EJB discovery provider: attempting to connect to connection %s", destination); - discoveryAttempt.connectAndDiscover(destination); + discoveryAttempt.connectAndDiscover(destination, null); } } @@ -273,6 +289,32 @@ public String handle(final AllFilterSpec filterSpec, final Void parameter) throw } }; + IoFuture getConnectedIdentityUsingClusterEffective(Endpoint endpoint, URI destination, String abstractType, String abstractTypeAuthority, AuthenticationContext context, String clusterName) { + Assert.checkNotNullParam("destination", destination); + Assert.checkNotNullParam("context", context); + + URI effectiveAuth = clusterName != null ? effectiveAuthURIs.get(clusterName) : null; + if (effectiveAuth == null) { + effectiveAuth = destination; + } + + final AuthenticationContextConfigurationClient client = AUTH_CONFIGURATION_CLIENT; + final SSLContext sslContext; + try { + sslContext = client.getSSLContext(destination, context); + } catch (GeneralSecurityException e) { + return new FailedIoFuture<>(Logs.REMOTING.failedToConfigureSslContext(e)); + } + final AuthenticationConfiguration authenticationConfiguration = client.getAuthenticationConfiguration(effectiveAuth, context, -1, abstractType, abstractTypeAuthority); + return endpoint.getConnectedIdentity(destination, sslContext, clearOverrides(authenticationConfiguration)); + } + + // TODO remove this hack once ELY-1399 is fully completed, and nothing else + // (e.g. naming) registers an override + private AuthenticationConfiguration clearOverrides(AuthenticationConfiguration config) { + return config.useProtocol(null).useHost(null).usePort(-1); + } + final class DiscoveryAttempt implements DiscoveryRequest, DiscoveryResult { private final ServiceType serviceType; private final FilterSpec filterSpec; @@ -330,14 +372,14 @@ public void handleDone(final EJBClientChannel clientChannel, final URI destinati }; } - void connectAndDiscover(URI uri) { + void connectAndDiscover(URI uri, String clusterEffective) { final String scheme = uri.getScheme(); if (scheme == null || ! ejbReceiver.getRemoteTransportProvider().supportsProtocol(scheme) || ! endpoint.isValidUriScheme(scheme)) { countDown(); return; } outstandingCount.getAndIncrement(); - final IoFuture future = doPrivileged((PrivilegedAction>) () -> endpoint.getConnectedIdentity(uri, "ejb", "jboss", authenticationContext)); + final IoFuture future = doPrivileged((PrivilegedAction>) () -> getConnectedIdentityUsingClusterEffective(endpoint, uri, "ejb", "jboss", authenticationContext, clusterEffective)); onCancel(future::cancel); future.addNotifier(outerNotifier, uri); } @@ -375,6 +417,7 @@ void countDown() { } else { // everything failed. We have to reconnect everything. Set everything = new HashSet<>(); + Map effectiveAuthMappings = new HashMap<>(); for (EJBClientConnection connection : ejbReceiver.getReceiverContext().getClientContext().getConfiguredConnections()) { if (connection.isForDiscovery()) { everything.add(connection.getDestination()); @@ -400,7 +443,13 @@ void countDown() { hostName = Inet.toOptimalString(destinationAddress); } } - everything.add(new URI(protocol, null, hostName, destination.getPort(), null, null, null)); + URI location = new URI(protocol, null, hostName, destination.getPort(), null, null, null); + String cluster = effectiveAuthMappings.get(location); + if (cluster != null) { + effectiveAuthMappings.put(location, cluster); + } + + everything.add(location); continue outer; } catch (URISyntaxException e) { // ignore URI and try the next one @@ -414,7 +463,7 @@ void countDown() { phase2 = true; outstandingCount.incrementAndGet(); for (URI uri : everything) { - connectAndDiscover(uri); + connectAndDiscover(uri, effectiveAuthMappings.get(uri)); } countDown(); } diff --git a/src/test/java/org/jboss/ejb/client/test/ClusteredInvocationTestCase.java b/src/test/java/org/jboss/ejb/client/test/ClusteredInvocationTestCase.java index bb1787921..2dcc41990 100644 --- a/src/test/java/org/jboss/ejb/client/test/ClusteredInvocationTestCase.java +++ b/src/test/java/org/jboss/ejb/client/test/ClusteredInvocationTestCase.java @@ -158,7 +158,10 @@ public void testClusteredSLSBInvocation() { logger.info("Invoking on proxy..."); // invoke on the proxy (use a ClusterAffinity for now) final String message = "hello!"; - final String echo = proxy.echo(message); + String echo = null; + for (int i = 0; i < 10; i++) { + echo = proxy.echo(message); + } Assert.assertEquals("Got an unexpected echo", echo, message); } diff --git a/src/test/resources/clustered-jboss-ejb-client.properties b/src/test/resources/clustered-jboss-ejb-client.properties index 2240c5a16..475712fa5 100644 --- a/src/test/resources/clustered-jboss-ejb-client.properties +++ b/src/test/resources/clustered-jboss-ejb-client.properties @@ -42,5 +42,5 @@ remote.connection.two.realm=default remote.clusters=ejb remote.cluster.ejb.node.node1.username=test remote.cluster.ejb.node.node1.password=test -remote.cluster.ejb.node.node2.username=test -remote.cluster.ejb.node.node2.password=test \ No newline at end of file +#remote.cluster.ejb.node.node2.username=test +#remote.cluster.ejb.node.node2.password=test \ No newline at end of file From e34281511cebea32d2ef25fbba27628a61a6b630 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Fri, 13 Oct 2017 11:35:03 -0500 Subject: [PATCH 4/4] EJBCLIENT-284 Configured (Deployment|Cluster)NodeSelector isn't used --- .../java/org/jboss/ejb/_private/Logs.java | 3 + .../client/DiscoveryEJBClientInterceptor.java | 3 +- .../jboss/ejb/client/EJBClientContext.java | 4 + .../legacy/LegacyPropertiesConfiguration.java | 18 ++ .../test/ClusterNodeSelectorTestCase.java | 251 ++++++++++++++++++ .../test/DeploymentNodeSelectorTestCase.java | 200 ++++++++++++++ .../ejb/client/test/TransactionTestCase.java | 3 +- ...-node-selector-jboss-ejb-client.properties | 47 ++++ ...-node-selector-jboss-ejb-client.properties | 42 +++ 9 files changed, 568 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/jboss/ejb/client/test/ClusterNodeSelectorTestCase.java create mode 100644 src/test/java/org/jboss/ejb/client/test/DeploymentNodeSelectorTestCase.java create mode 100644 src/test/resources/cluster-node-selector-jboss-ejb-client.properties create mode 100644 src/test/resources/deployment-node-selector-jboss-ejb-client.properties diff --git a/src/main/java/org/jboss/ejb/_private/Logs.java b/src/main/java/org/jboss/ejb/_private/Logs.java index 071821ef8..5bed5794c 100644 --- a/src/main/java/org/jboss/ejb/_private/Logs.java +++ b/src/main/java/org/jboss/ejb/_private/Logs.java @@ -329,6 +329,9 @@ public interface Logs extends BasicLogger { @Message(id = 80, value = "Request not sent") IllegalStateException requestNotSent(); + @Message(id = 81, value = "Failed to instantiate cluster node selector class \"%s\"") + IllegalArgumentException cannotInstantiateClustertNodeSelector(String name, @Cause ReflectiveOperationException e); + // Proxy API errors @Message(id = 100, value = "Object '%s' is not a valid proxy object") diff --git a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java index 5b58cfb00..aec0478f4 100644 --- a/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java +++ b/src/main/java/org/jboss/ejb/client/DiscoveryEJBClientInterceptor.java @@ -429,8 +429,7 @@ private List doAnyDiscovery(AbstractInvocationContext context, final Logs.INVOCATION.tracef("Performed first-match discovery(target affinity(node) = %s, destination = %s)", nodeName, location); } else if (nodeless == 0) { // use the deployment node selector - // todo: configure on client context - DeploymentNodeSelector selector = DeploymentNodeSelector.RANDOM; + DeploymentNodeSelector selector = context.getClientContext().getDeploymentNodeSelector(); nodeName = selector.selectNode(nodes.values().toArray(NO_STRINGS), locator.getAppName(), locator.getModuleName(), locator.getDistinctName()); if (nodeName == null) { throw Logs.INVOCATION.selectorReturnedNull(selector); diff --git a/src/main/java/org/jboss/ejb/client/EJBClientContext.java b/src/main/java/org/jboss/ejb/client/EJBClientContext.java index da1c8d309..3661a12f4 100644 --- a/src/main/java/org/jboss/ejb/client/EJBClientContext.java +++ b/src/main/java/org/jboss/ejb/client/EJBClientContext.java @@ -575,6 +575,10 @@ ClusterNodeSelector getClusterNodeSelector() { return clusterNodeSelector; } + DeploymentNodeSelector getDeploymentNodeSelector() { + return deploymentNodeSelector; + } + static final class ClassInterceptor { private final String className; private final EJBClientInterceptorInformation interceptor; diff --git a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java index 667707b81..c4a8424fc 100644 --- a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java +++ b/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java @@ -20,9 +20,11 @@ import java.net.URI; import java.util.List; +import java.util.Map; import java.util.Properties; import org.jboss.ejb._private.Logs; +import org.jboss.ejb.client.ClusterNodeSelector; import org.jboss.ejb.client.DeploymentNodeSelector; import org.jboss.ejb.client.EJBClientConnection; import org.jboss.ejb.client.EJBClientContext; @@ -72,6 +74,22 @@ public static void configure(final EJBClientContext.Builder builder) { builder.setDeploymentNodeSelector(deploymentNodeSelector); } + Map clusters = properties.getClusterConfigurations(); + if (clusters != null) { + for (JBossEJBProperties.ClusterConfiguration cluster : clusters.values()) { + ExceptionSupplier selectorSupplier = cluster.getClusterNodeSelectorSupplier(); + if (selectorSupplier != null) { + try { + builder.setClusterNodeSelector(selectorSupplier.get()); + } catch (ReflectiveOperationException e) { + throw Logs.MAIN.cannotInstantiateClustertNodeSelector(cluster.getClusterNodeSelectorClassName(), e); + } + // We only support one selector currently + break; + } + } + } + if (properties.getInvocationTimeout() != -1L) { builder.setInvocationTimeout(properties.getInvocationTimeout()); } diff --git a/src/test/java/org/jboss/ejb/client/test/ClusterNodeSelectorTestCase.java b/src/test/java/org/jboss/ejb/client/test/ClusterNodeSelectorTestCase.java new file mode 100644 index 000000000..8c6fee8d6 --- /dev/null +++ b/src/test/java/org/jboss/ejb/client/test/ClusterNodeSelectorTestCase.java @@ -0,0 +1,251 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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 java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.jboss.ejb.client.ClusterAffinity; +import org.jboss.ejb.client.ClusterNodeSelector; +import org.jboss.ejb.client.EJBClient; +import org.jboss.ejb.client.EJBClientCluster; +import org.jboss.ejb.client.EJBClientConnection; +import org.jboss.ejb.client.EJBClientContext; +import org.jboss.ejb.client.StatefulEJBLocator; +import org.jboss.ejb.client.StatelessEJBLocator; +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.ejb.server.ClusterTopologyListener.ClusterInfo; +import org.jboss.ejb.server.ClusterTopologyListener.NodeInfo; +import org.jboss.logging.Logger; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Tests usage of ClusterNodeSelector + * + * @author Jason T. Greene + * @author Richard Achmatowicz + */ +public class ClusterNodeSelectorTestCase { + + private static final Logger logger = Logger.getLogger(ClusterNodeSelectorTestCase.class); + private static final String PROPERTIES_FILE = "cluster-node-selector-jboss-ejb-client.properties"; + + // servers + private static final String SERVER1_NAME = "node1"; + private static final String SERVER2_NAME = "node2"; + + private DummyServer[] servers = new DummyServer[2]; + private static String[] serverNames = {SERVER1_NAME, SERVER2_NAME}; + private boolean[] serversStarted = new boolean[2] ; + + // 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 = ""; + + // cluster + // note: node names and server names should match! + private static final String CLUSTER_NAME = "ejb"; + private static final String NODE1_NAME = "node1"; + private static final String NODE2_NAME = "node2"; + + private static final NodeInfo NODE1 = DummyServer.getNodeInfo(NODE1_NAME, "localhost",6999,"0.0.0.0",0); + private static final NodeInfo NODE2 = DummyServer.getNodeInfo(NODE2_NAME, "localhost",7099,"0.0.0.0",0); + private static final ClusterInfo CLUSTER = DummyServer.getClusterInfo(CLUSTER_NAME, NODE1, NODE2); + + public static class TestSelector implements ClusterNodeSelector { + private static volatile String PICK_NODE = null; + + @Override + public String selectNode(String clusterName, String[] connectedNodes, String[] totalAvailableNodes) { + if (PICK_NODE != null) { + return PICK_NODE; + } + return connectedNodes[0]; + } + } + + + /** + * 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(SimpleInvocationTestCase.class.getClassLoader(), PROPERTIES_FILE); + JBossEJBProperties.getContextManager().setGlobalDefault(ejbProperties); + + // Launch callback if needed + ClassCallback.beforeClassCallback(); + } + + /** + * Do any test specific setup here + */ + @Before + public void beforeTest() throws Exception { + + // start a server + servers[0] = new DummyServer("localhost", 6999, serverNames[0]); + servers[0].start(); + serversStarted[0] = true; + logger.info("Started server " + serverNames[0]); + + // start a server + servers[1] = new DummyServer("localhost", 7099, serverNames[1]); + servers[1].start(); + serversStarted[1] = true; + logger.info("Started server " + serverNames[1]); + + // deploy modules + servers[0].register(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getSimpleName(), new EchoBean(NODE1_NAME)); + logger.info("Registered module on server " + servers[0]); + + servers[1].register(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getSimpleName(), new EchoBean(NODE2_NAME)); + logger.info("Registered module on server " + servers[1]); + + // define clusters + servers[0].addCluster(CLUSTER); + logger.info("Added node to cluster " + CLUSTER_NAME + ": server " + servers[1]); + servers[1].addCluster(CLUSTER); + logger.info("Added node to cluster " + CLUSTER_NAME +": server " + servers[1]); + } + + @Test + public void testConfiguredConnections() { + EJBClientContext context = EJBClientContext.getCurrent(); + List connections = context.getConfiguredConnections(); + + Assert.assertEquals("Number of configured connections for this context is incorrect", 2, connections.size()); + for (EJBClientConnection connection : connections) { + logger.info("found connection: destination = " + connection.getDestination() + ", forDiscovery = " + connection.isForDiscovery()); + } + + Collection clusters = context.getInitialConfiguredClusters(); + for (EJBClientCluster cluster: clusters) { + logger.info("found cluster: name = " + cluster.getName()); + } + } + + /** + * Test a basic invocation on clustered SLSB + */ + @Test + public void testClusteredSLSBInvocation() { + logger.info("Testing invocation on SLSB proxy with ClusterAffinity"); + + // 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); + + EJBClient.setStrongAffinity(proxy, new ClusterAffinity("ejb")); + Assert.assertNotNull("Received a null proxy", proxy); + logger.info("Created proxy for Echo: " + proxy.toString()); + + logger.info("Invoking on proxy..."); + + TestSelector.PICK_NODE = NODE1_NAME; + for (int i = 0; i < 10; i++) { + Assert.assertEquals(NODE1_NAME, proxy.whoAreYou()); + } + + TestSelector.PICK_NODE = NODE2_NAME; + for (int i = 0; i < 10; i++) { + Assert.assertEquals(NODE2_NAME, proxy.whoAreYou()); + } + } + + /** + * Test a basic invocation on clustered SFSB + */ + @Test + public void testClusteredSFSBInvocation() throws Exception { + logger.info("Testing invocation on SFSB proxy with ClusterAffinity"); + + TestSelector.PICK_NODE = NODE2_NAME; + // create a proxy for invocation + final StatelessEJBLocator statelessEJBLocator = new StatelessEJBLocator(Echo.class, APP_NAME, MODULE_NAME, Echo.class.getSimpleName(), DISTINCT_NAME); + StatefulEJBLocator statefulEJBLocator = null; + statefulEJBLocator = EJBClient.createSession(statelessEJBLocator.withNewAffinity(new ClusterAffinity("ejb"))); + + Echo proxy = EJBClient.createProxy(statefulEJBLocator); + Assert.assertNotNull("Received a null proxy", proxy); + for (int i = 0; i < 10; i++) { + Assert.assertEquals(NODE2_NAME, proxy.whoAreYou()); + } + + TestSelector.PICK_NODE = NODE1_NAME; + statefulEJBLocator = EJBClient.createSession(statelessEJBLocator.withNewAffinity(new ClusterAffinity("ejb"))); + proxy = EJBClient.createProxy(statefulEJBLocator); + + for (int i = 0; i < 10; i++) { + Assert.assertEquals(NODE1_NAME, proxy.whoAreYou()); + } + } + + /** + * Do any test-specific tear down here. + */ + @After + public void afterTest() { + servers[0].unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getName()); + logger.info("Unregistered module from " + serverNames[0]); + + servers[1].unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getName()); + logger.info("Unregistered module from " + serverNames[1]); + + servers[0].removeCluster(CLUSTER_NAME); + servers[1].removeCluster(CLUSTER_NAME); + + if (serversStarted[0]) { + try { + this.servers[0].stop(); + } catch (Throwable t) { + logger.info("Could not stop server", t); + } + } + logger.info("Stopped server " + serverNames[0]); + + if (serversStarted[1]) { + try { + this.servers[1].stop(); + } catch (Throwable t) { + logger.info("Could not stop server", t); + } + } + logger.info("Stopped server " + serverNames[1]); + } + + /** + * Do any general tear down here. + */ + @AfterClass + public static void afterClass() { + } + +} diff --git a/src/test/java/org/jboss/ejb/client/test/DeploymentNodeSelectorTestCase.java b/src/test/java/org/jboss/ejb/client/test/DeploymentNodeSelectorTestCase.java new file mode 100644 index 000000000..a374384a4 --- /dev/null +++ b/src/test/java/org/jboss/ejb/client/test/DeploymentNodeSelectorTestCase.java @@ -0,0 +1,200 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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 java.util.HashMap; +import java.util.HashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; + +import com.arjuna.ats.internal.jbossatx.jta.jca.XATerminator; +import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple; +import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple; +import com.arjuna.ats.jta.common.JTAEnvironmentBean; +import com.arjuna.ats.jta.common.jtaPropertyManager; +import org.jboss.ejb.client.ClusterAffinity; +import org.jboss.ejb.client.DeploymentNodeSelector; +import org.jboss.ejb.client.EJBClient; +import org.jboss.ejb.client.StatefulEJBLocator; +import org.jboss.ejb.client.StatelessEJBLocator; +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.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.naming.client.WildFlyInitialContextFactory; +import org.wildfly.naming.client.WildFlyRootContext; +import org.wildfly.naming.client.util.FastHashtable; +import org.wildfly.transaction.client.ContextTransactionManager; +import org.wildfly.transaction.client.ContextTransactionSynchronizationRegistry; +import org.wildfly.transaction.client.LocalTransactionContext; +import org.wildfly.transaction.client.LocalUserTransaction; +import org.wildfly.transaction.client.RemoteTransactionContext; +import org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider; + +/** + * Tests DeploymentNodeSelector + * + * @author Jason T. Greene + * @author Richard Achmatowicz + */ +public class DeploymentNodeSelectorTestCase { + private static final Logger logger = Logger.getLogger(DeploymentNodeSelectorTestCase.class); + private static ContextTransactionManager txManager; + private static ContextTransactionSynchronizationRegistry txSyncRegistry; + + private DummyServer server1, server2, server3, server4; + 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 SERVER1_NAME = "server1"; + private static final String SERVER2_NAME = "server2"; + + /** + * Do any general setup here + * @throws Exception + */ + @BeforeClass + public static void beforeClass() throws Exception { + String PROPERTIES_FILE = "deployment-node-selector-jboss-ejb-client.properties"; + + // trigger the static init of the correct properties file - this also depends on running in forkMode=always + JBossEJBProperties ejbProperties = JBossEJBProperties.fromClassPath(SimpleInvocationTestCase.class.getClassLoader(), PROPERTIES_FILE); + JBossEJBProperties.getContextManager().setGlobalDefault(ejbProperties); + + // Launch callback if needed + ClassCallback.beforeClassCallback(); + } + + /** + * Do any test specific setup here + */ + @Before + public void beforeTest() throws Exception { + // start a server + server1 = new DummyServer("localhost", 6999, SERVER1_NAME, true); + server2 = new DummyServer("localhost", 7999, SERVER2_NAME, true); + server1.start(); + server2.start(); + serverStarted = true; + logger.info("Started servers ..."); + + server1.register(APP_NAME, MODULE_NAME, DISTINCT_NAME, EchoBean.class.getSimpleName(), new EchoBean(SERVER1_NAME)); + server2.register(APP_NAME, MODULE_NAME, DISTINCT_NAME, EchoBean.class.getSimpleName(), new EchoBean(SERVER2_NAME)); + } + + public static class TestSelector implements DeploymentNodeSelector { + private static volatile String PICK_NODE = null; + + @Override + public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) { + if (PICK_NODE != null) { + return PICK_NODE; + } + return eligibleNodes[0]; + } + } + + @Test + public void testSLSBInvocation() { + // create a proxy for invocation + final StatelessEJBLocator statelessEJBLocator = new StatelessEJBLocator(Echo.class, APP_NAME, MODULE_NAME, EchoBean.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..."); + + TestSelector.PICK_NODE = SERVER1_NAME; + for (int i = 0; i < 10; i++) { + Assert.assertEquals(SERVER1_NAME, proxy.whoAreYou()); + } + + TestSelector.PICK_NODE = SERVER2_NAME; + for (int i = 0; i < 10; i++) { + Assert.assertEquals(SERVER2_NAME, proxy.whoAreYou()); + } + } + + /** + * Test a basic invocation on clustered SFSB + */ + @Test + public void testSFSBInvocation() throws Exception { + TestSelector.PICK_NODE = SERVER2_NAME; + // create a proxy for invocation + final StatelessEJBLocator statelessEJBLocator = new StatelessEJBLocator(Echo.class, APP_NAME, MODULE_NAME, EchoBean.class.getSimpleName(), DISTINCT_NAME); + StatefulEJBLocator statefulEJBLocator = null; + statefulEJBLocator = EJBClient.createSession(statelessEJBLocator); + + Echo proxy = EJBClient.createProxy(statefulEJBLocator); + Assert.assertNotNull("Received a null proxy", proxy); + for (int i = 0; i < 10; i++) { + Assert.assertEquals(SERVER2_NAME, proxy.whoAreYou()); + } + + TestSelector.PICK_NODE = SERVER1_NAME; + statefulEJBLocator = EJBClient.createSession(statelessEJBLocator); + proxy = EJBClient.createProxy(statefulEJBLocator); + for (int i = 0; i < 10; i++) { + Assert.assertEquals(SERVER1_NAME, proxy.whoAreYou()); + } + } + /** + * Do any test-specific tear down here. + */ + @After + public void afterTest() { + server1.unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getName()); + server2.unregister(APP_NAME, MODULE_NAME, DISTINCT_NAME, Echo.class.getName()); + logger.info("Unregistered module ..."); + + if (serverStarted) { + try { + this.server1.stop(); + this.server2.stop(); + } catch (Throwable t) { + logger.info("Could not stop server", t); + } + } + logger.info("Stopped server ..."); + } + + /** + * Do any general tear down here. + */ + @AfterClass + public static void afterClass() { + } + +} diff --git a/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java b/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java index 7b73c556f..5f7965311 100644 --- a/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java +++ b/src/test/java/org/jboss/ejb/client/test/TransactionTestCase.java @@ -56,8 +56,9 @@ import org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider; /** - * Tests basic invocation of a bean deployed on a single server node. + * Tests transaction stickiness * + * @author Jason T. Greene * @author Richard Achmatowicz */ public class TransactionTestCase { diff --git a/src/test/resources/cluster-node-selector-jboss-ejb-client.properties b/src/test/resources/cluster-node-selector-jboss-ejb-client.properties new file mode 100644 index 000000000..97fc7793e --- /dev/null +++ b/src/test/resources/cluster-node-selector-jboss-ejb-client.properties @@ -0,0 +1,47 @@ +# +# JBoss, Home of Professional Open Source. +# Copyright 2010 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,two + +# connection to a node at protocol://host:port +remote.connection.one.host=localhost +remote.connection.one.port=6999 +remote.connection.one.protocol=remote +remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false +remote.connection.one.username=test +remote.connection.one.password=test +remote.connection.one.realm=default + +# connection to a node at protocol://host:port +remote.connection.two.host=localhost +remote.connection.two.port=7099 +remote.connection.two.protocol=remote +remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false +remote.connection.two.username=test +remote.connection.two.password=test +remote.connection.two.realm=default + +# the nodes are clustered in a cluster called ejb and have names node1, node2 +remote.clusters=ejb +remote.cluster.ejb.node.node1.username=test +remote.cluster.ejb.node.node1.password=test +remote.cluster.ejb.clusternode.selector=org.jboss.ejb.client.test.ClusterNodeSelectorTestCase$TestSelector +#remote.cluster.ejb.node.node2.username=test +#remote.cluster.ejb.node.node2.password=test \ No newline at end of file diff --git a/src/test/resources/deployment-node-selector-jboss-ejb-client.properties b/src/test/resources/deployment-node-selector-jboss-ejb-client.properties new file mode 100644 index 000000000..6d5c40c59 --- /dev/null +++ b/src/test/resources/deployment-node-selector-jboss-ejb-client.properties @@ -0,0 +1,42 @@ +# +# JBoss, Home of Professional Open Source. +# Copyright 2010 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,two + +deployment.node.selector=org.jboss.ejb.client.test.DeploymentNodeSelectorTestCase$TestSelector + +# connection to a node at protocol://host:port +remote.connection.one.host=localhost +remote.connection.one.port=6999 +remote.connection.one.protocol=remote +remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false +remote.connection.one.username=test +remote.connection.one.password=test +remote.connection.one.realm=default + +# connection to a node at protocol://host:port +remote.connection.two.host=localhost +remote.connection.two.port=7999 +remote.connection.two.protocol=remote +remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false +remote.connection.two.username=test +remote.connection.two.password=test +remote.connection.two.realm=default +