Unified
Split
Showing
with
308 additions
and 1 deletion.
- +1 −1 ...erver/src/main/java/org/wildfly/clustering/server/dispatcher/ChannelCommandDispatcherFactory.java
- +136 −0 ...on/clustering/src/test/java/org/jboss/as/test/clustering/cluster/group/GroupListenerTestCase.java
- +30 −0 ...ion/clustering/src/test/java/org/jboss/as/test/clustering/cluster/group/bean/ClusterTopology.java
- +6 −0 ...ering/src/test/java/org/jboss/as/test/clustering/cluster/group/bean/ClusterTopologyRetriever.java
- +28 −0 ...g/src/test/java/org/jboss/as/test/clustering/cluster/group/bean/ClusterTopologyRetrieverBean.java
- +32 −0 ...e/integration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/group/bean/Group.java
- +75 −0 ...tegration/clustering/src/test/java/org/jboss/as/test/clustering/cluster/group/bean/GroupBean.java
| @@ -264,7 +264,7 @@ public void viewAccepted(View view) { | ||
| this.members.keySet().removeAll(leftMembers); | ||
| } | ||
|
|
||
| if (this.listeners.isEmpty()) { | ||
| if (!this.listeners.isEmpty()) { | ||
| Address localAddress = this.dispatcher.getChannel().getAddress(); | ||
| ViewMembership oldMembership = new ViewMembership(localAddress, oldView, this); | ||
| ViewMembership membership = new ViewMembership(localAddress, view, this); | ||
| @@ -0,0 +1,136 @@ | ||
| /* | ||
| * JBoss, Home of Professional Open Source. | ||
| * Copyright 2019, Red Hat, Inc., and individual contributors | ||
| * as indicated by the @author tags. See the copyright.txt file in the | ||
| * distribution for a full listing of individual contributors. | ||
| * | ||
| * This is free software; you can redistribute it and/or modify it | ||
| * under the terms of the GNU Lesser General Public License as | ||
| * published by the Free Software Foundation; either version 2.1 of | ||
| * the License, or (at your option) any later version. | ||
| * | ||
| * This software is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public | ||
| * License along with this software; if not, write to the Free | ||
| * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
| * 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
| */ | ||
|
|
||
| package org.jboss.as.test.clustering.cluster.group; | ||
|
|
||
| import static org.junit.Assert.*; | ||
|
|
||
| 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.group.bean.ClusterTopology; | ||
| import org.jboss.as.test.clustering.cluster.group.bean.ClusterTopologyRetriever; | ||
| import org.jboss.as.test.clustering.cluster.group.bean.ClusterTopologyRetrieverBean; | ||
| 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.shrinkwrap.api.Archive; | ||
| import org.jboss.shrinkwrap.api.ShrinkWrap; | ||
| import org.jboss.shrinkwrap.api.spec.JavaArchive; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
|
|
||
| /** | ||
| * Integration test for the listener facility of a {@link Group}. | ||
| * @author Paul Ferraro | ||
| */ | ||
| @RunWith(Arquillian.class) | ||
| public class GroupListenerTestCase extends AbstractClusteringTestCase { | ||
| private static final String MODULE_NAME = GroupListenerTestCase.class.getSimpleName(); | ||
| private static final long VIEW_CHANGE_WAIT = TimeoutUtil.adjust(2000); | ||
|
|
||
| @Deployment(name = DEPLOYMENT_1, managed = false, testable = false) | ||
| @TargetsContainer(NODE_1) | ||
| public static Archive<?> deployment1() { | ||
| return createDeployment(); | ||
| } | ||
|
|
||
| @Deployment(name = DEPLOYMENT_2, managed = false, testable = false) | ||
| @TargetsContainer(NODE_2) | ||
| public static Archive<?> deployment2() { | ||
| return createDeployment(); | ||
| } | ||
|
|
||
| private static Archive<?> createDeployment() { | ||
| final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, MODULE_NAME + ".jar"); | ||
| ejbJar.addPackage(ClusterTopologyRetriever.class.getPackage()); | ||
| return ejbJar; | ||
| } | ||
|
|
||
| @Test | ||
| public void test() throws Exception { | ||
| try (EJBDirectory directory = new RemoteEJBDirectory(MODULE_NAME)) { | ||
| ClusterTopologyRetriever bean = directory.lookupStateless(ClusterTopologyRetrieverBean.class, ClusterTopologyRetriever.class); | ||
|
|
||
| ClusterTopology topology = bean.getClusterTopology(); | ||
| assertEquals(topology.getCurrentMembers().toString(), 2, topology.getCurrentMembers().size()); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_1)); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_2)); | ||
| assertEquals(topology.getPreviousMembers().toString(), 0, topology.getPreviousMembers().size()); | ||
|
|
||
| stop(NODE_2); | ||
|
|
||
| Thread.sleep(VIEW_CHANGE_WAIT); | ||
|
|
||
| topology = bean.getClusterTopology(); | ||
| assertEquals(topology.getCurrentMembers().toString(), 1, topology.getCurrentMembers().size()); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_1)); | ||
| assertEquals(topology.getPreviousMembers().toString(), 2, topology.getPreviousMembers().size()); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_1)); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_2)); | ||
|
|
||
| start(NODE_2); | ||
|
|
||
| Thread.sleep(VIEW_CHANGE_WAIT); | ||
|
|
||
| topology = bean.getClusterTopology(); | ||
| assertEquals(topology.getCurrentMembers().toString(), 2, topology.getCurrentMembers().size()); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_1)); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_2)); | ||
| if (topology.getTargetMember().equals(NODE_1)) { | ||
| assertEquals(topology.getPreviousMembers().toString(), 1, topology.getPreviousMembers().size()); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_1)); | ||
| } else { | ||
| // Since node 2 was just started, its previous membership will be empty | ||
| assertEquals(topology.getPreviousMembers().toString(), 0, topology.getPreviousMembers().size()); | ||
| } | ||
|
|
||
| stop(NODE_1); | ||
|
|
||
| Thread.sleep(VIEW_CHANGE_WAIT); | ||
|
|
||
| topology = bean.getClusterTopology(); | ||
| assertEquals(topology.getCurrentMembers().toString(), 1, topology.getCurrentMembers().size()); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_2)); | ||
| assertEquals(topology.getPreviousMembers().toString(), 2, topology.getPreviousMembers().size()); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_1)); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_2)); | ||
|
|
||
| start(NODE_1); | ||
|
|
||
| Thread.sleep(VIEW_CHANGE_WAIT); | ||
|
|
||
| topology = bean.getClusterTopology(); | ||
| assertEquals(topology.getCurrentMembers().toString(), 2, topology.getCurrentMembers().size()); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_1)); | ||
| assertTrue(topology.getCurrentMembers().toString(), topology.getCurrentMembers().contains(NODE_2)); | ||
| if (topology.getTargetMember().equals(NODE_2)) { | ||
| assertEquals(topology.getPreviousMembers().toString(), 1, topology.getPreviousMembers().size()); | ||
| assertTrue(topology.getPreviousMembers().toString(), topology.getPreviousMembers().contains(NODE_2)); | ||
| } else { | ||
| // Since node 1 was just started, its previous membership will be empty | ||
| assertEquals(topology.getPreviousMembers().toString(), 0, topology.getPreviousMembers().size()); | ||
| } | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,30 @@ | ||
| package org.jboss.as.test.clustering.cluster.group.bean; | ||
|
|
||
| import java.io.Serializable; | ||
| import java.util.List; | ||
|
|
||
| public class ClusterTopology implements Serializable { | ||
| private static final long serialVersionUID = 413628123168918069L; | ||
|
|
||
| private final String targetMember; | ||
| private final List<String> currentMembers; | ||
| private final List<String> previousMembers; | ||
|
|
||
| public ClusterTopology(String targetMember, List<String> currentMembers, List<String> previousMembers) { | ||
| this.targetMember = targetMember; | ||
| this.currentMembers = currentMembers; | ||
| this.previousMembers = previousMembers; | ||
| } | ||
|
|
||
| public String getTargetMember() { | ||
| return this.targetMember; | ||
| } | ||
|
|
||
| public List<String> getCurrentMembers() { | ||
| return this.currentMembers; | ||
| } | ||
|
|
||
| public List<String> getPreviousMembers() { | ||
| return this.previousMembers; | ||
| } | ||
| } |
| @@ -0,0 +1,6 @@ | ||
| package org.jboss.as.test.clustering.cluster.group.bean; | ||
|
|
||
| public interface ClusterTopologyRetriever { | ||
|
|
||
| ClusterTopology getClusterTopology(); | ||
| } |
| @@ -0,0 +1,28 @@ | ||
| package org.jboss.as.test.clustering.cluster.group.bean; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import javax.ejb.EJB; | ||
| import javax.ejb.Remote; | ||
| import javax.ejb.Stateless; | ||
|
|
||
| import org.wildfly.clustering.group.Membership; | ||
| import org.wildfly.clustering.group.Node; | ||
|
|
||
| @Stateless | ||
| @Remote(ClusterTopologyRetriever.class) | ||
| public class ClusterTopologyRetrieverBean implements ClusterTopologyRetriever { | ||
| @EJB | ||
| private Group group; | ||
|
|
||
| @Override | ||
| public ClusterTopology getClusterTopology() { | ||
| return new ClusterTopology(this.group.getLocalMember().getName(), getNames(this.group.getMembership()), getNames(this.group.getPreviousMembership())); | ||
| } | ||
|
|
||
| private static List<String> getNames(Membership membership) { | ||
| return (membership != null) ? membership.getMembers().stream().map(Node::getName).collect(Collectors.toList()) : Collections.emptyList(); | ||
| } | ||
| } |
| @@ -0,0 +1,32 @@ | ||
| /* | ||
| * JBoss, Home of Professional Open Source. | ||
| * Copyright 2019, Red Hat, Inc., and individual contributors | ||
| * as indicated by the @author tags. See the copyright.txt file in the | ||
| * distribution for a full listing of individual contributors. | ||
| * | ||
| * This is free software; you can redistribute it and/or modify it | ||
| * under the terms of the GNU Lesser General Public License as | ||
| * published by the Free Software Foundation; either version 2.1 of | ||
| * the License, or (at your option) any later version. | ||
| * | ||
| * This software is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public | ||
| * License along with this software; if not, write to the Free | ||
| * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
| * 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
| */ | ||
|
|
||
| package org.jboss.as.test.clustering.cluster.group.bean; | ||
|
|
||
| import org.wildfly.clustering.group.Membership; | ||
|
|
||
| /** | ||
| * @author Paul Ferraro | ||
| */ | ||
| public interface Group extends org.wildfly.clustering.group.Group { | ||
| Membership getPreviousMembership(); | ||
| } |
| @@ -0,0 +1,75 @@ | ||
| package org.jboss.as.test.clustering.cluster.group.bean; | ||
|
|
||
| import javax.annotation.PostConstruct; | ||
| import javax.annotation.PreDestroy; | ||
| import javax.annotation.Resource; | ||
| import javax.ejb.Local; | ||
| import javax.ejb.Singleton; | ||
| import javax.ejb.Startup; | ||
|
|
||
| import org.wildfly.clustering.Registration; | ||
| import org.wildfly.clustering.group.GroupListener; | ||
| import org.wildfly.clustering.group.Membership; | ||
| import org.wildfly.clustering.group.Node; | ||
|
|
||
| @Singleton | ||
| @Startup | ||
| @Local(Group.class) | ||
| public class GroupBean implements Group, GroupListener { | ||
|
|
||
| @Resource(lookup = "java:jboss/clustering/group/default") | ||
| private org.wildfly.clustering.group.Group group; | ||
| private Registration registration; | ||
| private volatile Membership previousMembership; | ||
|
|
||
| @PostConstruct | ||
| public void init() { | ||
| this.registration = this.group.register(this); | ||
| } | ||
|
|
||
| @PreDestroy | ||
| public void destroy() { | ||
| this.registration.close(); | ||
| } | ||
|
|
||
| @Override | ||
| public void membershipChanged(Membership previousMembership, Membership membership, boolean merged) { | ||
| this.previousMembership = previousMembership; | ||
| } | ||
|
|
||
| @Deprecated | ||
| @Override | ||
| public void removeListener(org.wildfly.clustering.group.Group.Listener listener) { | ||
| this.group.removeListener(listener); | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return this.group.getName(); | ||
| } | ||
|
|
||
| @Override | ||
| public Node getLocalMember() { | ||
| return this.group.getLocalMember(); | ||
| } | ||
|
|
||
| @Override | ||
| public Membership getMembership() { | ||
| return this.group.getMembership(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isSingleton() { | ||
| return this.group.isSingleton(); | ||
| } | ||
|
|
||
| @Override | ||
| public Registration register(GroupListener object) { | ||
| return this.group.register(object); | ||
| } | ||
|
|
||
| @Override | ||
| public Membership getPreviousMembership() { | ||
| return this.previousMembership; | ||
| } | ||
| } |