Skip to content

Commit

Permalink
[WFLY-11489] Correct assignment of EJB client affinties on server side.
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard Achmatowicz committed May 14, 2019
1 parent 9881122 commit dbeb346
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 10 deletions.
36 changes: 26 additions & 10 deletions ejb3/src/main/java/org/jboss/as/ejb3/remote/AssociationImpl.java
Expand Up @@ -46,6 +46,7 @@
import org.jboss.ejb.client.EJBLocator;
import org.jboss.ejb.client.EJBMethodLocator;
import org.jboss.ejb.client.EJBModuleIdentifier;
import org.jboss.ejb.client.NodeAffinity;
import org.jboss.ejb.client.SessionID;
import org.jboss.ejb.client.StatefulEJBLocator;
import org.jboss.ejb.server.Association;
Expand Down Expand Up @@ -247,19 +248,22 @@ public SecurityIdentity getSecurityIdentity() {
private void updateAffinities(InvocationRequest invocationRequest, Map<String, Object> attachments, EJBLocator<?> ejbLocator, ComponentView componentView) {
Affinity legacyAffinity = null;
Affinity weakAffinity = null;
Affinity clusterAffinity = getClusterAffinity();
Affinity strongAffinity = null;

if (ejbLocator.isStateful() && componentView.getComponent() instanceof StatefulSessionComponent) {
final StatefulSessionComponent statefulSessionComponent = (StatefulSessionComponent) componentView.getComponent();
strongAffinity = getStrongAffinity(statefulSessionComponent);
weakAffinity = legacyAffinity = getWeakAffinity(statefulSessionComponent, ejbLocator.asStateful());
} else if (componentView.getComponent() instanceof StatelessSessionComponent) {
// V3 and less used cluster affinity as a weak affinity for SLSBs
legacyAffinity = clusterAffinity;
// Stateless invocations no not require strong affinity, only weak affinity to nodes within the same cluster, if present.
// However, since V3, the EJB client does not support weak affinity updates referencing a cluster (and even then, only via Affinity.WEAK_AFFINITY_CONTEXT_KEY), only a node.
// Until this is corrected, we need to use the strong affinity instead.
strongAffinity = legacyAffinity = this.getStatelessAffinity();
}

// Always use the cluster as the strong affinity, if there is one
if (clusterAffinity != null) {
invocationRequest.updateStrongAffinity(clusterAffinity);
// cause the affinity values to get sent back to the client
if (strongAffinity != null && !(strongAffinity instanceof NodeAffinity)) {
invocationRequest.updateStrongAffinity(strongAffinity);
}

if (weakAffinity != null && !weakAffinity.equals(Affinity.NONE)) {
Expand All @@ -269,6 +273,10 @@ private void updateAffinities(InvocationRequest invocationRequest, Map<String, O
if (legacyAffinity != null && !legacyAffinity.equals(Affinity.NONE)) {
attachments.put(Affinity.WEAK_AFFINITY_CONTEXT_KEY, legacyAffinity);
}

EjbLogger.EJB3_INVOCATION_LOGGER.debugf("Called receiveInvocationRequest ( bean = %s ): strong affinity = %s, weak affinity = %s \n",
componentView.getComponent().getClass().getName(), strongAffinity, weakAffinity);

}

private void execute(Request request, Runnable task, final boolean isAsync) {
Expand Down Expand Up @@ -332,16 +340,20 @@ public CancelHandle receiveSessionOpenRequest(@NotNull final SessionOpenRequest
return;
}

Affinity clusterAffinity = getClusterAffinity();
if (clusterAffinity != null) {
sessionOpenRequest.updateStrongAffinity(clusterAffinity);
// do not update strongAffinity when it is of type NodeAffinity; this will be achieved on the client in DiscoveryInterceptor via targetAffinity
Affinity strongAffinity = getStrongAffinity(statefulSessionComponent);
if (strongAffinity != null && !(strongAffinity instanceof NodeAffinity)) {
sessionOpenRequest.updateStrongAffinity(strongAffinity);
}

Affinity weakAffinity = getWeakAffinity(statefulSessionComponent, sessionID);
if (weakAffinity != null && !Affinity.NONE.equals(weakAffinity)) {
sessionOpenRequest.updateWeakAffinity(weakAffinity);
}

EjbLogger.EJB3_INVOCATION_LOGGER.debugf("Called receiveSessionOpenRequest ( bean = %s ): strong affinity = %s, weak affinity = %s \n",
statefulSessionComponent.getClass().getName(), strongAffinity, weakAffinity);

sessionOpenRequest.convertToStateful(sessionID);
};
execute(sessionOpenRequest, runnable, false);
Expand Down Expand Up @@ -588,6 +600,10 @@ private static Method findMethod(final ComponentView componentView, final EJBMet
return null;
}

private static Affinity getStrongAffinity(final StatefulSessionComponent statefulSessionComponent) {
return statefulSessionComponent.getCache().getStrictAffinity();
}

private static Affinity getWeakAffinity(final StatefulSessionComponent statefulSessionComponent, final StatefulEJBLocator<?> statefulEJBLocator) {
final SessionID sessionID = statefulEJBLocator.getSessionId();
return getWeakAffinity(statefulSessionComponent, sessionID);
Expand All @@ -597,7 +613,7 @@ private static Affinity getWeakAffinity(final StatefulSessionComponent statefulS
return statefulSessionComponent.getCache().getWeakAffinity(sessionID);
}

private Affinity getClusterAffinity() {
private Affinity getStatelessAffinity() {
Registry<String, List<ClientMapping>> registry = this.clientMappingRegistry;
Group group = registry != null ? registry.getGroup() : null;

Expand Down
@@ -0,0 +1,97 @@
package org.jboss.as.test.clustering.cluster.ejb.remote;

import java.util.PropertyPermission;
import javax.ejb.NoSuchEJBException;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.as.test.clustering.cluster.AbstractClusteringTestCase;
import org.jboss.as.test.clustering.cluster.ejb.remote.bean.Incrementor;
import org.jboss.as.test.clustering.cluster.ejb.remote.bean.IncrementorBean;
import org.jboss.as.test.clustering.cluster.ejb.remote.bean.PassivationDisabledStatefulIncrementorBean;
import org.jboss.as.test.clustering.cluster.ejb.remote.bean.Result;
import org.jboss.as.test.clustering.ejb.EJBDirectory;
import org.jboss.as.test.clustering.ejb.RemoteEJBDirectory;
import org.jboss.as.test.shared.TimeoutUtil;
import org.jboss.as.test.shared.integration.ejb.security.PermissionUtils;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.common.function.ExceptionSupplier;

/**
* Test the following properties of passivation-disabled SFSB when deployed in a cluster:
* - stickiness of passivation-disabled SFSB to the node its is created on, as well as
* - verify that it does not fail over to another node in the cluster when the node it is created on goes down
* .
* @author Paul Ferraro
*/
@RunWith(Arquillian.class)
public class PassivationDisabledRemoteStatefulEjbFailoverTestCase extends AbstractClusteringTestCase {
private static final int COUNT = 20;
private static final long CLIENT_TOPOLOGY_UPDATE_WAIT = TimeoutUtil.adjust(5000);
private static final String MODULE_NAME = PassivationDisabledRemoteStatefulEjbFailoverTestCase.class.getSimpleName();

@Deployment(name = DEPLOYMENT_1, managed = false, testable = false)
@TargetsContainer(NODE_1)
public static Archive<?> createDeploymentForContainer1() {
return createDeployment();
}

@Deployment(name = DEPLOYMENT_2, managed = false, testable = false)
@TargetsContainer(NODE_2)
public static Archive<?> createDeploymentForContainer2() {
return createDeployment();
}

private static Archive<?> createDeployment() {
return ShrinkWrap.create(JavaArchive.class, MODULE_NAME + ".jar")
.addPackage(EJBDirectory.class.getPackage())
.addClasses(Result.class, Incrementor.class, IncrementorBean.class, PassivationDisabledStatefulIncrementorBean.class)
.addAsManifestResource(PermissionUtils.createPermissionsXmlAsset(new PropertyPermission(NODE_NAME_PROPERTY, "read")), "permissions.xml")
;
}

private final ExceptionSupplier<EJBDirectory, Exception> directoryProvider;

public PassivationDisabledRemoteStatefulEjbFailoverTestCase() {
this.directoryProvider = () -> new RemoteEJBDirectory(MODULE_NAME);
}

@Test
public void test() throws Exception {
try (EJBDirectory directory = this.directoryProvider.get()) {
Incrementor bean = directory.lookupStateful(PassivationDisabledStatefulIncrementorBean.class, Incrementor.class);

Result<Integer> result = bean.increment();
String target = result.getNode();
int count = 1;

Assert.assertEquals(count++, result.getValue().intValue());

// Bean should retain strong affinity for this node
for (int i = 0; i < COUNT; ++i) {
result = bean.increment();
Assert.assertEquals(count++, result.getValue().intValue());
Assert.assertEquals(String.valueOf(i), target, result.getNode());
}

undeploy(this.findDeployment(target));

Thread.sleep(CLIENT_TOPOLOGY_UPDATE_WAIT);

try {
result = bean.increment();

// Bean should fail to failover to other node
Assert.fail(result.getNode());
} catch (NoSuchEJBException e) {
// Failover should fail
}
}
}
}
@@ -0,0 +1,14 @@
package org.jboss.as.test.clustering.cluster.ejb.remote.bean;

import javax.ejb.Remote;
import javax.ejb.Stateful;

/**
* SFSB with passivation disabled, behaves as a singleton bean even if deployed on a clustered node
*
* @author Paul Ferarro
*/
@Stateful(passivationCapable = false)
@Remote(Incrementor.class)
public class PassivationDisabledStatefulIncrementorBean extends IncrementorBean {
}

0 comments on commit dbeb346

Please sign in to comment.