org.projectodd.wunderboss.wildfly.SingletonClusterParticipant Maven / Gradle / Ivy
/*
* Copyright 2014-2015 Red Hat, Inc, and individual contributors.
*
* 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.projectodd.wunderboss.wildfly;
import org.jboss.logging.Logger;
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.MembershipListener;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.blocks.RequestHandler;
import org.jgroups.util.Rsp;
import org.jgroups.util.RspList;
import org.projectodd.wunderboss.WunderBoss;
import org.projectodd.wunderboss.singleton.ClusterChangeCallback;
import org.projectodd.wunderboss.singleton.ClusterParticipant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
public class SingletonClusterParticipant implements ClusterParticipant, RequestHandler, MembershipListener {
public SingletonClusterParticipant(String name) {
this.name = name;
this.channelWrapper = WunderBoss.findOrCreateComponent(ChannelWrapper.class);
this.channel = (JChannel)this.channelWrapper.channel();
this.channelWrapper.registerHandler(name, this).addMembershipListener(this);
}
@Override
public void viewAccepted(View view) {
if (!view.containsMember(this.currentMaster)) {
//TODO: synchronize access to currentMaster?
// clear the master, since it no longer exists in the cluster
this.currentMaster = null;
}
if (this.clusterChangeCallback != null) {
this.clusterChangeCallback.clusterChanged(isMasterWithoutInterrogatingCluster(), isMaster());
}
}
@Override
public void suspect(Address suspected_mbr) {
}
@Override
public void block() {
}
@Override
public void unblock() {
}
@Override
public Object handle(Message msg) {
Map content = (Map)msg.getObject();
Address requestedMaster = (Address)content.get("payload");
Address oldMaster = this.currentMaster;
if (this.currentMaster == null) {
this.currentMaster = requestedMaster;
}
return oldMaster;
}
public Address id() {
return this.channel.getAddress();
}
public boolean isMasterWithoutInterrogatingCluster() {
final Address id = id();
return id != null &&
id.equals(this.currentMaster);
}
@Override
public void setClusterChangeCallback(ClusterChangeCallback callback) {
this.clusterChangeCallback = callback;
}
/*
* - if master is known
* - run if we are it
* - else
* - lock
* - check master again, run if master
* - tell other nodes we are master
* - wait for responses
* - responses are the id each node thought was master, which will be null if we are to be master
* - if all are null, we are master
* - else if all agree someone else is master, store that id
* - else throw
* - unlock
*/
@Override
public boolean isMaster() {
boolean isMaster = false;
try {
if (this.currentMaster != null) {
isMaster = isMasterWithoutInterrogatingCluster();
} else {
Lock lock = this.channelWrapper.getLock(this.name);
lock.lock();
try {
if (this.currentMaster != null) {
//I don't think we can become master while someone else holds the lock, but...
isMaster = isMasterWithoutInterrogatingCluster();
} else if (id() != null) { // id() will be null if jgroups is shut/shutting down
Map msg = new HashMap();
msg.put("dispatch", this.name);
msg.put("payload", id());
//TODO: what happens on timeout?
RspList responses =
this.channelWrapper.castMessage(null, msg, 5000);
Set possibleMasters = new HashSet<>();
Address reportedMaster = null;
for(Map.Entry> each : responses.entrySet()) {
Address addr = each.getValue().getValue();
if (addr != null) {
possibleMasters.add(addr);
reportedMaster = addr;
}
}
if (possibleMasters.size() > 1) {
throw new IllegalStateException("Cluster in broken state, multiple masters for " + this.name +
": " + possibleMasters);
}
if (reportedMaster == null) {
isMaster = true;
} else {
this.currentMaster = reportedMaster;
isMaster = false;
}
}
} finally {
lock.unlock();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isMaster;
}
private final String name;
private final JChannel channel;
private final ChannelWrapper channelWrapper;
private Address currentMaster = null;
private ClusterChangeCallback clusterChangeCallback;
private static final Logger log = Logger.getLogger(SingletonClusterParticipant.class);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy