Leader Election Pattern (#923)
* Fix issue #761: ThreadSafeDoubleCheckLocking.java: Instantiating by Reflection call will be successful if you do that firstly * Create leader election module * Create Interface of Instance and MessageManager * Create implementations with token ring algorithm * Change package structure. Create basic message system. * Implement heartbeat and heartbeat invoking message system * Implement election message handler * Add leader message handler * Add main entry point * Add comments * Update README.md * Fix checkstyle issue * Add Unit Tests * Add Unit Tests * Add bully leader selection * Change System.out to log print. Add MIT license in each file. * Add More java doc comments * Add unit test * Add unit tests
This commit is contained in:
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* Abstract class of all the instance implementation classes.
|
||||
*/
|
||||
public abstract class AbstractInstance implements Instance, Runnable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractInstance.class);
|
||||
|
||||
protected static final int HEARTBEAT_INTERVAL = 5000;
|
||||
|
||||
protected MessageManager messageManager;
|
||||
protected Queue<Message> messageQueue;
|
||||
protected final int localId;
|
||||
protected int leaderId;
|
||||
protected boolean alive;
|
||||
|
||||
/**
|
||||
* Constructor of BullyInstance.
|
||||
*/
|
||||
public AbstractInstance(MessageManager messageManager, int localId, int leaderId) {
|
||||
this.messageManager = messageManager;
|
||||
this.messageQueue = new ConcurrentLinkedQueue<>();
|
||||
this.localId = localId;
|
||||
this.leaderId = leaderId;
|
||||
this.alive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The instance will execute the message in its message queue periodically once it is alive.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
if (!this.messageQueue.isEmpty()) {
|
||||
this.processMessage(this.messageQueue.remove());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Once messages are sent to the certain instance, it will firstly be added to the queue and wait to be executed.
|
||||
* @param message Message sent by other instances
|
||||
*/
|
||||
@Override
|
||||
public void onMessage(Message message) {
|
||||
messageQueue.offer(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the instance is alive or not.
|
||||
* @return {@code true} if the instance is alive.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the health status of the certain instance.
|
||||
* @param alive {@code true} for alive.
|
||||
*/
|
||||
@Override
|
||||
public void setAlive(boolean alive) {
|
||||
this.alive = alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the message according to its type.
|
||||
* @param message Message polled from queue.
|
||||
*/
|
||||
private void processMessage(Message message) {
|
||||
switch (message.getType()) {
|
||||
case ELECTION:
|
||||
LOGGER.info("Instance " + localId + " - Election Message handling...");
|
||||
handleElectionMessage(message);
|
||||
break;
|
||||
case LEADER:
|
||||
LOGGER.info("Instance " + localId + " - Leader Message handling...");
|
||||
handleLeaderMessage(message);
|
||||
break;
|
||||
case HEARTBEAT:
|
||||
LOGGER.info("Instance " + localId + " - Heartbeat Message handling...");
|
||||
handleHeartbeatMessage(message);
|
||||
break;
|
||||
case ELECTION_INVOKE:
|
||||
LOGGER.info("Instance " + localId + " - Election Invoke Message handling...");
|
||||
handleElectionInvokeMessage();
|
||||
break;
|
||||
case LEADER_INVOKE:
|
||||
LOGGER.info("Instance " + localId + " - Leader Invoke Message handling...");
|
||||
handleLeaderInvokeMessage();
|
||||
break;
|
||||
case HEARTBEAT_INVOKE:
|
||||
LOGGER.info("Instance " + localId + " - Heartbeat Invoke Message handling...");
|
||||
handleHeartbeatInvokeMessage();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract methods to handle different types of message. These methods need to be implemented in concrete instance
|
||||
* class to implement corresponding leader-selection pattern.
|
||||
*/
|
||||
protected abstract void handleElectionMessage(Message message);
|
||||
|
||||
protected abstract void handleElectionInvokeMessage();
|
||||
|
||||
protected abstract void handleLeaderMessage(Message message);
|
||||
|
||||
protected abstract void handleLeaderInvokeMessage();
|
||||
|
||||
protected abstract void handleHeartbeatMessage(Message message);
|
||||
|
||||
protected abstract void handleHeartbeatInvokeMessage();
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Abstract class of all the message manager classes.
|
||||
*/
|
||||
public abstract class AbstractMessageManager implements MessageManager {
|
||||
|
||||
/**
|
||||
* Contain all the instances in the system. Key is its ID, and value is the instance itself.
|
||||
*/
|
||||
protected Map<Integer, Instance> instanceMap;
|
||||
|
||||
/**
|
||||
* Construtor of AbstractMessageManager
|
||||
*/
|
||||
public AbstractMessageManager(Map<Integer, Instance> instanceMap) {
|
||||
this.instanceMap = instanceMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the next instance with smallest ID.
|
||||
* @return The next instance.
|
||||
*/
|
||||
protected Instance findNextInstance(int currentId) {
|
||||
Instance result = null;
|
||||
List<Integer> candidateList = instanceMap.keySet()
|
||||
.stream()
|
||||
.filter((i) -> i > currentId && instanceMap.get(i).isAlive())
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
if (candidateList.isEmpty()) {
|
||||
int index = instanceMap.keySet()
|
||||
.stream()
|
||||
.filter((i) -> instanceMap.get(i).isAlive())
|
||||
.sorted()
|
||||
.collect(Collectors.toList())
|
||||
.get(0);
|
||||
result = instanceMap.get(index);
|
||||
} else {
|
||||
int index = candidateList.get(0);
|
||||
result = instanceMap.get(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
/**
|
||||
* Instance interface
|
||||
*/
|
||||
public interface Instance {
|
||||
|
||||
/**
|
||||
* Check if the instance is alive or not.
|
||||
* @return {@code true} if the instance is alive.
|
||||
*/
|
||||
boolean isAlive();
|
||||
|
||||
/**
|
||||
* Set the health status of the certain instance.
|
||||
* @param alive {@code true} for alive.
|
||||
*/
|
||||
void setAlive(boolean alive);
|
||||
|
||||
/**
|
||||
* Consume messages from other instances.
|
||||
* @param message Message sent by other instances
|
||||
*/
|
||||
void onMessage(Message message);
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Message used to transport data between instances.
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
private MessageType type;
|
||||
|
||||
private String content;
|
||||
|
||||
public Message() {}
|
||||
|
||||
public Message(MessageType type, String content) {
|
||||
this.type = type;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(MessageType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Message message = (Message) o;
|
||||
return type == message.type && Objects.equals(content, message.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, content);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
/**
|
||||
* MessageManager interface
|
||||
*/
|
||||
public interface MessageManager {
|
||||
|
||||
/**
|
||||
* Send heartbeat message to leader instance to check whether the leader instance is alive.
|
||||
* @param leaderId Instance ID of leader instance.
|
||||
* @return {@code true} if leader instance is alive, or {@code false} if not.
|
||||
*/
|
||||
boolean sendHeartbeatMessage(int leaderId);
|
||||
|
||||
/**
|
||||
* Send election message to other instances.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
* @param content Election message content.
|
||||
* @return {@code true} if the message is accepted by the target instances.
|
||||
*/
|
||||
boolean sendElectionMessage(int currentId, String content);
|
||||
|
||||
/**
|
||||
* Send new leader notification message to other instances.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
* @param leaderId Leader message content.
|
||||
* @return {@code true} if the message is accepted by the target instances.
|
||||
*/
|
||||
boolean sendLeaderMessage(int currentId, int leaderId);
|
||||
|
||||
/**
|
||||
* Send heartbeat invoke message. This will invoke heartbeat task in the target instance.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
*/
|
||||
void sendHeartbeatInvokeMessage(int currentId);
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
/**
|
||||
* Message Type enum
|
||||
*/
|
||||
public enum MessageType {
|
||||
|
||||
/**
|
||||
* Start the election. The content of the message stores ID(s) of the candidate instance(s).
|
||||
*/
|
||||
ELECTION,
|
||||
|
||||
/**
|
||||
* Nodify the new leader. The content of the message should be the leader ID.
|
||||
*/
|
||||
LEADER,
|
||||
|
||||
/**
|
||||
* Check health of current leader instance.
|
||||
*/
|
||||
HEARTBEAT,
|
||||
|
||||
/**
|
||||
* Inform target instance to start election.
|
||||
*/
|
||||
ELECTION_INVOKE,
|
||||
|
||||
/**
|
||||
* Inform target instance to notify all the other instance that it is the new leader.
|
||||
*/
|
||||
LEADER_INVOKE,
|
||||
|
||||
/**
|
||||
* Inform target instance to start heartbeat.
|
||||
*/
|
||||
HEARTBEAT_INVOKE
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import com.iluwatar.leaderelection.Instance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageManager;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Example of how to use bully leader election. Initially 5 instances is created in the clould
|
||||
* system, and the instance with ID 1 is set as leader. After the system is started stop the
|
||||
* leader instance, and the new leader will be elected.
|
||||
*/
|
||||
public class BullyApp {
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
|
||||
BullyInstance instance1 = new BullyInstance(messageManager, 1, 1);
|
||||
BullyInstance instance2 = new BullyInstance(messageManager, 2, 1);
|
||||
BullyInstance instance3 = new BullyInstance(messageManager, 3, 1);
|
||||
BullyInstance instance4 = new BullyInstance(messageManager, 4, 1);
|
||||
BullyInstance instance5 = new BullyInstance(messageManager, 5, 1);
|
||||
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
instanceMap.put(4, instance4);
|
||||
instanceMap.put(5, instance5);
|
||||
|
||||
instance4.onMessage(new Message(MessageType.HEARTBEAT_INVOKE, ""));
|
||||
|
||||
Thread thread1 = new Thread(instance1);
|
||||
Thread thread2 = new Thread(instance2);
|
||||
Thread thread3 = new Thread(instance3);
|
||||
Thread thread4 = new Thread(instance4);
|
||||
Thread thread5 = new Thread(instance5);
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
thread3.start();
|
||||
thread4.start();
|
||||
thread5.start();
|
||||
|
||||
instance1.setAlive(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractInstance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Impelemetation with bully algorithm. Each instance should have a sequential id and is able to
|
||||
* communicate with other instances in the system. Initially the instance with smallest (or largest)
|
||||
* ID is selected to be the leader. All the other instances send heartbeat message to leader periodically
|
||||
* to check its health. If one certain instance finds the server done, it will send an election message
|
||||
* to all the instances of which the ID is larger. If the target instance is alive, it will return an
|
||||
* alive message (in this sample return true) and then send election message with its ID. If not,
|
||||
* the original instance will send leader message to all the other instances.
|
||||
*/
|
||||
public class BullyInstance extends AbstractInstance {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BullyInstance.class);
|
||||
|
||||
/**
|
||||
* Constructor of BullyInstance.
|
||||
*/
|
||||
public BullyInstance(MessageManager messageManager, int localId, int leaderId) {
|
||||
super(messageManager, localId, leaderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the heartbeat invoke message. After receiving the message, the instance will send a heartbeat
|
||||
* to leader to check its health. If alive, it will inform the next instance to do the heartbeat. If not,
|
||||
* it will start the election process.
|
||||
*/
|
||||
@Override
|
||||
protected void handleHeartbeatInvokeMessage() {
|
||||
try {
|
||||
boolean isLeaderAlive = messageManager.sendHeartbeatMessage(leaderId);
|
||||
if (isLeaderAlive) {
|
||||
LOGGER.info("Instance " + localId + "- Leader is alive.");
|
||||
Thread.sleep(HEARTBEAT_INTERVAL);
|
||||
messageManager.sendHeartbeatInvokeMessage(localId);
|
||||
} else {
|
||||
LOGGER.info("Instance " + localId + "- Leader is not alive. Start election.");
|
||||
boolean electionResult = messageManager.sendElectionMessage(localId, String.valueOf(localId));
|
||||
if (electionResult) {
|
||||
LOGGER.info("Instance " + localId + "- Succeed in election. Start leader notification.");
|
||||
messageManager.sendLeaderMessage(localId, localId);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.info("Instance " + localId + "- Interrupted.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process election invoke message. Send election message to all the instances with smaller ID. If any
|
||||
* one of them is alive, do nothing. If no instance alive, send leader message to all the alive instance
|
||||
* and restart heartbeat.
|
||||
*/
|
||||
@Override
|
||||
protected void handleElectionInvokeMessage() {
|
||||
if (!isLeader()) {
|
||||
LOGGER.info("Instance " + localId + "- Start election.");
|
||||
boolean electionResult = messageManager.sendElectionMessage(localId, String.valueOf(localId));
|
||||
if (electionResult) {
|
||||
LOGGER.info("Instance " + localId + "- Succeed in election. Start leader notification.");
|
||||
leaderId = localId;
|
||||
messageManager.sendLeaderMessage(localId, localId);
|
||||
messageManager.sendHeartbeatInvokeMessage(localId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process leader message. Update local leader information.
|
||||
*/
|
||||
@Override
|
||||
protected void handleLeaderMessage(Message message) {
|
||||
leaderId = Integer.valueOf(message.getContent());
|
||||
LOGGER.info("Instance " + localId + " - Leader update done.");
|
||||
}
|
||||
|
||||
private boolean isLeader() {
|
||||
return localId == leaderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used in Bully instance.
|
||||
*/
|
||||
@Override
|
||||
protected void handleLeaderInvokeMessage() {}
|
||||
|
||||
@Override
|
||||
protected void handleHeartbeatMessage(Message message) {}
|
||||
|
||||
@Override
|
||||
protected void handleElectionMessage(Message message) {}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractMessageManager;
|
||||
import com.iluwatar.leaderelection.Instance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation of BullyMessageManager
|
||||
*/
|
||||
public class BullyMessageManager extends AbstractMessageManager {
|
||||
|
||||
/**
|
||||
* Constructor of BullyMessageManager.
|
||||
*/
|
||||
public BullyMessageManager(Map<Integer, Instance> instanceMap) {
|
||||
super(instanceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat message to current leader instance to check the health.
|
||||
* @param leaderId leaderID
|
||||
* @return {@code true} if the leader is alive.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendHeartbeatMessage(int leaderId) {
|
||||
Instance leaderInstance = instanceMap.get(leaderId);
|
||||
boolean alive = leaderInstance.isAlive();
|
||||
return alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send election message to all the instances with smaller ID.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
* @param content Election message content.
|
||||
* @return {@code true} if no alive instance has smaller ID, so that the election is accepted.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendElectionMessage(int currentId, String content) {
|
||||
List<Integer> candidateList = findElectionCandidateInstanceList(currentId);
|
||||
if (candidateList.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
Message electionMessage = new Message(MessageType.ELECTION_INVOKE, "");
|
||||
candidateList.stream()
|
||||
.forEach((i) -> instanceMap.get(i).onMessage(electionMessage));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send leader message to all the instances to notify the new leader.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
* @param leaderId Leader message content.
|
||||
* @return {@code true} if the message is accepted.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendLeaderMessage(int currentId, int leaderId) {
|
||||
Message leaderMessage = new Message(MessageType.LEADER, String.valueOf(leaderId));
|
||||
instanceMap.keySet()
|
||||
.stream()
|
||||
.filter((i) -> i != currentId)
|
||||
.forEach((i) -> instanceMap.get(i).onMessage(leaderMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat invoke message to the next instance.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
*/
|
||||
@Override
|
||||
public void sendHeartbeatInvokeMessage(int currentId) {
|
||||
Instance nextInstance = this.findNextInstance(currentId);
|
||||
Message heartbeatInvokeMessage = new Message(MessageType.HEARTBEAT_INVOKE, "");
|
||||
nextInstance.onMessage(heartbeatInvokeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the alive instances with smaller ID than current instance.
|
||||
* @param currentId ID of current instance.
|
||||
* @return ID list of all the candidate instance.
|
||||
*/
|
||||
private List<Integer> findElectionCandidateInstanceList(int currentId) {
|
||||
return instanceMap.keySet()
|
||||
.stream()
|
||||
.filter((i) -> i < currentId && instanceMap.get(i).isAlive())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import com.iluwatar.leaderelection.Instance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageManager;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Example of how to use ring leader election. Initially 5 instances is created in the clould
|
||||
* system, and the instance with ID 1 is set as leader. After the system is started stop the
|
||||
* leader instance, and the new leader will be elected.
|
||||
*/
|
||||
public class RingApp {
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
MessageManager messageManager = new RingMessageManager(instanceMap);
|
||||
|
||||
RingInstance instance1 = new RingInstance(messageManager, 1, 1);
|
||||
RingInstance instance2 = new RingInstance(messageManager, 2, 1);
|
||||
RingInstance instance3 = new RingInstance(messageManager, 3, 1);
|
||||
RingInstance instance4 = new RingInstance(messageManager, 4, 1);
|
||||
RingInstance instance5 = new RingInstance(messageManager, 5, 1);
|
||||
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
instanceMap.put(4, instance4);
|
||||
instanceMap.put(5, instance5);
|
||||
|
||||
instance2.onMessage(new Message(MessageType.HEARTBEAT_INVOKE, ""));
|
||||
|
||||
Thread thread1 = new Thread(instance1);
|
||||
Thread thread2 = new Thread(instance2);
|
||||
Thread thread3 = new Thread(instance3);
|
||||
Thread thread4 = new Thread(instance4);
|
||||
Thread thread5 = new Thread(instance5);
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
thread3.start();
|
||||
thread4.start();
|
||||
thread5.start();
|
||||
|
||||
instance1.setAlive(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractInstance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation with token ring algorithm. The instances in the system are organized as a ring.
|
||||
* Each instance should have a sequential id and the instance with smallest (or largest) id should
|
||||
* be the initial leader. All the other instances send heartbeat message to leader periodically
|
||||
* to check its health. If one certain instance finds the server done, it will send an election
|
||||
* message to the next alive instance in the ring, which contains its own ID. Then the next instance
|
||||
* add its ID into the message and pass it to the next. After all the alive instances' ID are add
|
||||
* to the message, the message is send back to the first instance and it will choose the instance
|
||||
* with smallest ID to be the new leader, and then send a leader message to other instances to
|
||||
* inform the result.
|
||||
*/
|
||||
public class RingInstance extends AbstractInstance {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RingInstance.class);
|
||||
|
||||
/**
|
||||
* Constructor of RingInstance.
|
||||
*/
|
||||
public RingInstance(MessageManager messageManager, int localId, int leaderId) {
|
||||
super(messageManager, localId, leaderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the heartbeat invoke message. After receiving the message, the instance will send a heartbeat
|
||||
* to leader to check its health. If alive, it will inform the next instance to do the heartbeat. If not,
|
||||
* it will start the election process.
|
||||
*/
|
||||
@Override
|
||||
protected void handleHeartbeatInvokeMessage() {
|
||||
try {
|
||||
boolean isLeaderAlive = messageManager.sendHeartbeatMessage(this.leaderId);
|
||||
if (isLeaderAlive) {
|
||||
LOGGER.info("Instance " + localId + "- Leader is alive. Start next heartbeat in 5 second.");
|
||||
Thread.sleep(HEARTBEAT_INTERVAL);
|
||||
messageManager.sendHeartbeatInvokeMessage(this.localId);
|
||||
} else {
|
||||
LOGGER.info("Instance " + localId + "- Leader is not alive. Start election.");
|
||||
messageManager.sendElectionMessage(this.localId, String.valueOf(this.localId));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.info("Instance " + localId + "- Interrupted.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process election message. If the local ID is contained in the ID list, the instance will select the
|
||||
* alive instance with smallest ID to be the new leader, and send the leader inform message. If not,
|
||||
* it will add its local ID to the list and send the message to the next instance in the ring.
|
||||
*/
|
||||
@Override
|
||||
protected void handleElectionMessage(Message message) {
|
||||
String content = message.getContent();
|
||||
LOGGER.info("Instance " + localId + " - Election Message: " + content);
|
||||
List<Integer> candidateList =
|
||||
Arrays.stream(content.trim().split(","))
|
||||
.map(Integer::valueOf)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
if (candidateList.contains(localId)) {
|
||||
int newLeaderId = candidateList.get(0);
|
||||
LOGGER.info("Instance " + localId + " - New leader should be " + newLeaderId + ".");
|
||||
messageManager.sendLeaderMessage(localId, newLeaderId);
|
||||
} else {
|
||||
content += "," + localId;
|
||||
messageManager.sendElectionMessage(localId, content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process leader Message. The instance will set the leader ID to be the new one and send the message to
|
||||
* the next instance until all the alive instance in the ring is informed.
|
||||
*/
|
||||
@Override
|
||||
protected void handleLeaderMessage(Message message) {
|
||||
int newLeaderId = Integer.valueOf(message.getContent());
|
||||
if (this.leaderId != newLeaderId) {
|
||||
LOGGER.info("Instance " + localId + " - Update leaderID");
|
||||
this.leaderId = newLeaderId;
|
||||
messageManager.sendLeaderMessage(localId, newLeaderId);
|
||||
} else {
|
||||
LOGGER.info("Instance " + localId + " - Leader update done. Start heartbeat.");
|
||||
messageManager.sendHeartbeatInvokeMessage(localId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used in Ring instance.
|
||||
*/
|
||||
@Override
|
||||
protected void handleLeaderInvokeMessage() {}
|
||||
|
||||
@Override
|
||||
protected void handleHeartbeatMessage(Message message) {}
|
||||
|
||||
@Override
|
||||
protected void handleElectionInvokeMessage() {}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractMessageManager;
|
||||
import com.iluwatar.leaderelection.Instance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementation of RingMessageManager
|
||||
*/
|
||||
public class RingMessageManager extends AbstractMessageManager {
|
||||
|
||||
/**
|
||||
* Constructor of RingMessageManager.
|
||||
*/
|
||||
public RingMessageManager(Map<Integer, Instance> instanceMap) {
|
||||
super(instanceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat message to current leader instance to check the health.
|
||||
* @param leaderId leaderID
|
||||
* @return {@code true} if the leader is alive.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendHeartbeatMessage(int leaderId) {
|
||||
Instance leaderInstance = instanceMap.get(leaderId);
|
||||
boolean alive = leaderInstance.isAlive();
|
||||
return alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send election message to the next instance.
|
||||
* @param currentId currentID
|
||||
* @param content list contains all the IDs of instances which have received this election message.
|
||||
* @return {@code true} if the election message is accepted by the target instance.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendElectionMessage(int currentId, String content) {
|
||||
Instance nextInstance = this.findNextInstance(currentId);
|
||||
Message electionMessage = new Message(MessageType.ELECTION, content);
|
||||
nextInstance.onMessage(electionMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send leader message to the next instance.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
* @param leaderId Leader message content.
|
||||
* @return {@code true} if the leader message is accepted by the target instance.
|
||||
*/
|
||||
@Override
|
||||
public boolean sendLeaderMessage(int currentId, int leaderId) {
|
||||
Instance nextInstance = this.findNextInstance(currentId);
|
||||
Message leaderMessage = new Message(MessageType.LEADER, String.valueOf(leaderId));
|
||||
nextInstance.onMessage(leaderMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send heartbeat invoke message to the next instance.
|
||||
* @param currentId Instance ID of which sends this message.
|
||||
*/
|
||||
@Override
|
||||
public void sendHeartbeatInvokeMessage(int currentId) {
|
||||
Instance nextInstance = this.findNextInstance(currentId);
|
||||
Message heartbeatInvokeMessage = new Message(MessageType.HEARTBEAT_INVOKE, "");
|
||||
nextInstance.onMessage(heartbeatInvokeMessage);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Message test case.
|
||||
*/
|
||||
public class MessageTest {
|
||||
|
||||
@Test
|
||||
public void testGetType() {
|
||||
Message message = new Message(MessageType.HEARTBEAT, "");
|
||||
assertEquals(MessageType.HEARTBEAT, message.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetContent() {
|
||||
String content = "test";
|
||||
Message message = new Message(MessageType.HEARTBEAT, content);
|
||||
assertEquals(content, message.getContent());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* BullyApp unit test.
|
||||
*/
|
||||
public class BullyAppTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String[] args = {};
|
||||
BullyApp.main(args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import com.iluwatar.leaderelection.*;
|
||||
import com.iluwatar.leaderelection.ring.RingInstance;
|
||||
import com.iluwatar.leaderelection.ring.RingMessageManager;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* BullyMessageManager unit test.
|
||||
*/
|
||||
public class BullyMessageManagerTest {
|
||||
|
||||
@Test
|
||||
public void testSendHeartbeatMessage() {
|
||||
Instance instance1 = new BullyInstance(null, 1, 1);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
assertTrue(messageManager.sendHeartbeatMessage(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendElectionMessageNotAccepted() {
|
||||
try {
|
||||
Instance instance1 = new BullyInstance(null, 1, 1);
|
||||
Instance instance2 = new BullyInstance(null, 1, 2);
|
||||
Instance instance3 = new BullyInstance(null, 1, 3);
|
||||
Instance instance4 = new BullyInstance(null, 1, 4);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
instanceMap.put(4, instance4);
|
||||
instance1.setAlive(false);
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
boolean result = messageManager.sendElectionMessage(3, "3");
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message message2 = ((Queue<Message>) messageQueueField.get(instance2)).poll();
|
||||
int instance4QueueSize = ((Queue<Message>) messageQueueField.get(instance4)).size();
|
||||
Message expectedMessage = new Message(MessageType.ELECTION_INVOKE, "");
|
||||
assertEquals(message2, expectedMessage);
|
||||
assertEquals(instance4QueueSize, 0);
|
||||
assertEquals(result, false);
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElectionMessageAccepted() {
|
||||
Instance instance1 = new BullyInstance(null, 1, 1);
|
||||
Instance instance2 = new BullyInstance(null, 1, 2);
|
||||
Instance instance3 = new BullyInstance(null, 1, 3);
|
||||
Instance instance4 = new BullyInstance(null, 1, 4);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
instanceMap.put(4, instance4);
|
||||
instance1.setAlive(false);
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
boolean result = messageManager.sendElectionMessage(2, "2");
|
||||
assertEquals(result, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendLeaderMessage() {
|
||||
try {
|
||||
Instance instance1 = new BullyInstance(null, 1, 1);
|
||||
Instance instance2 = new BullyInstance(null, 1, 2);
|
||||
Instance instance3 = new BullyInstance(null, 1, 3);
|
||||
Instance instance4 = new BullyInstance(null, 1, 4);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
instanceMap.put(4, instance4);
|
||||
instance1.setAlive(false);
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
messageManager.sendLeaderMessage(2, 2);
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message message3 = ((Queue<Message>) messageQueueField.get(instance3)).poll();
|
||||
Message message4 = ((Queue<Message>) messageQueueField.get(instance4)).poll();
|
||||
Message expectedMessage = new Message(MessageType.LEADER, "2");
|
||||
assertEquals(message3, expectedMessage);
|
||||
assertEquals(message4, expectedMessage);
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendHeartbeatInvokeMessage() {
|
||||
try {
|
||||
Instance instance1 = new BullyInstance(null, 1, 1);
|
||||
Instance instance2 = new BullyInstance(null, 1, 2);
|
||||
Instance instance3 = new BullyInstance(null, 1, 3);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
MessageManager messageManager = new BullyMessageManager(instanceMap);
|
||||
messageManager.sendHeartbeatInvokeMessage(2);
|
||||
Message message = new Message(MessageType.HEARTBEAT_INVOKE, "");
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message messageSent = ((Queue<Message>) messageQueueField.get(instance3)).poll();
|
||||
assertEquals(messageSent.getType(), message.getType());
|
||||
assertEquals(messageSent.getContent(), message.getContent());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.bully;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractInstance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Queue;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* BullyInstance unit test.
|
||||
*/
|
||||
public class BullyinstanceTest {
|
||||
|
||||
@Test
|
||||
public void testOnMessage() {
|
||||
try {
|
||||
final BullyInstance bullyInstance = new BullyInstance(null, 1, 1);
|
||||
Message bullyMessage = new Message(MessageType.HEARTBEAT, "");
|
||||
bullyInstance.onMessage(bullyMessage);
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
assertEquals(bullyMessage, ((Queue<Message>) messageQueueField.get(bullyInstance)).poll());
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
fail("fail to access messasge queue.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAlive() {
|
||||
try {
|
||||
final BullyInstance bullyInstance = new BullyInstance(null, 1, 1);
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field aliveField = instanceClass.getDeclaredField("alive");
|
||||
aliveField.setAccessible(true);
|
||||
aliveField.set(bullyInstance, false);
|
||||
assertFalse(bullyInstance.isAlive());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Fail to access field alive.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAlive() {
|
||||
final BullyInstance bullyInstance = new BullyInstance(null, 1, 1);
|
||||
bullyInstance.setAlive(false);
|
||||
assertFalse(bullyInstance.isAlive());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* RingApp unit test.
|
||||
*/
|
||||
public class RingAppTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String[] args = {};
|
||||
RingApp.main(args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import com.iluwatar.leaderelection.AbstractInstance;
|
||||
import com.iluwatar.leaderelection.Message;
|
||||
import com.iluwatar.leaderelection.MessageType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Queue;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* RingInstance unit test.
|
||||
*/
|
||||
public class RingInstanceTest {
|
||||
|
||||
@Test
|
||||
public void testOnMessage() {
|
||||
try {
|
||||
final RingInstance ringInstance = new RingInstance(null, 1, 1);
|
||||
Message ringMessage = new Message(MessageType.HEARTBEAT, "");
|
||||
ringInstance.onMessage(ringMessage);
|
||||
Class ringInstanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = ringInstanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
assertEquals(ringMessage, ((Queue<Message>) messageQueueField.get(ringInstance)).poll());
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
fail("fail to access messasge queue.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAlive() {
|
||||
try {
|
||||
final RingInstance ringInstance = new RingInstance(null, 1, 1);
|
||||
Class ringInstanceClass = AbstractInstance.class;
|
||||
Field aliveField = ringInstanceClass.getDeclaredField("alive");
|
||||
aliveField.setAccessible(true);
|
||||
aliveField.set(ringInstance, false);
|
||||
assertFalse(ringInstance.isAlive());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Fail to access field alive.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAlive() {
|
||||
final RingInstance ringInstance = new RingInstance(null, 1, 1);
|
||||
ringInstance.setAlive(false);
|
||||
assertFalse(ringInstance.isAlive());
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* The MIT License
|
||||
* Copyright (c) 2014-2016 Ilkka Seppälä
|
||||
* <p>
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* <p>
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* <p>
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.leaderelection.ring;
|
||||
|
||||
import com.iluwatar.leaderelection.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* RingMessageManager unit test.
|
||||
*/
|
||||
public class RingMessageManagerTest {
|
||||
|
||||
@Test
|
||||
public void testSendHeartbeatMessage() {
|
||||
Instance instance1 = new RingInstance(null, 1, 1);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
MessageManager messageManager = new RingMessageManager(instanceMap);
|
||||
assertTrue(messageManager.sendHeartbeatMessage(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendElectionMessage() {
|
||||
try {
|
||||
Instance instance1 = new RingInstance(null, 1, 1);
|
||||
Instance instance2 = new RingInstance(null, 1, 2);
|
||||
Instance instance3 = new RingInstance(null, 1, 3);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
MessageManager messageManager = new RingMessageManager(instanceMap);
|
||||
String messageContent = "2";
|
||||
messageManager.sendElectionMessage(2, messageContent);
|
||||
Message ringMessage = new Message(MessageType.ELECTION, messageContent);
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message ringMessageSent = ((Queue<Message>) messageQueueField.get(instance3)).poll();
|
||||
assertEquals(ringMessageSent.getType(), ringMessage.getType());
|
||||
assertEquals(ringMessageSent.getContent(), ringMessage.getContent());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendLeaderMessage() {
|
||||
try {
|
||||
Instance instance1 = new RingInstance(null, 1, 1);
|
||||
Instance instance2 = new RingInstance(null, 1, 2);
|
||||
Instance instance3 = new RingInstance(null, 1, 3);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
MessageManager messageManager = new RingMessageManager(instanceMap);
|
||||
String messageContent = "3";
|
||||
messageManager.sendLeaderMessage(2, 3);
|
||||
Message ringMessage = new Message(MessageType.LEADER, messageContent);
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message ringMessageSent = ((Queue<Message>) messageQueueField.get(instance3)).poll();
|
||||
assertEquals(ringMessageSent, ringMessage);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendHeartbeatInvokeMessage() {
|
||||
try {
|
||||
Instance instance1 = new RingInstance(null, 1, 1);
|
||||
Instance instance2 = new RingInstance(null, 1, 2);
|
||||
Instance instance3 = new RingInstance(null, 1, 3);
|
||||
Map<Integer, Instance> instanceMap = new HashMap<>();
|
||||
instanceMap.put(1, instance1);
|
||||
instanceMap.put(2, instance2);
|
||||
instanceMap.put(3, instance3);
|
||||
MessageManager messageManager = new RingMessageManager(instanceMap);
|
||||
messageManager.sendHeartbeatInvokeMessage(2);
|
||||
Message ringMessage = new Message(MessageType.HEARTBEAT_INVOKE, "");
|
||||
Class instanceClass = AbstractInstance.class;
|
||||
Field messageQueueField = instanceClass.getDeclaredField("messageQueue");
|
||||
messageQueueField.setAccessible(true);
|
||||
Message ringMessageSent = ((Queue<Message>) messageQueueField.get(instance3)).poll();
|
||||
assertEquals(ringMessageSent.getType(), ringMessage.getType());
|
||||
assertEquals(ringMessageSent.getContent(), ringMessage.getContent());
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
fail("Error to access private field.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user