diff --git a/Makefile b/Makefile index 5d5313d..d2a2ca0 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -QUORUM ?= 2 +QUORUM ?= 3 PATH_PROJECT_JAR = target/simple_voting_structure-0.0.1-SNAPSHOT.jar PROJECT_GROUP = simple_voting_structure JADE_AGENTS = simple_voting_structure:$(PROJECT_GROUP).App($(QUORUM)); diff --git a/src/main/java/simple_voting_structure/App.java b/src/main/java/simple_voting_structure/App.java index fe16054..eba90d4 100644 --- a/src/main/java/simple_voting_structure/App.java +++ b/src/main/java/simple_voting_structure/App.java @@ -10,7 +10,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.logging.Level; -import java.util.Random; /** @@ -39,8 +38,6 @@ protected void setup() { votersQuorum = Integer.parseInt(args[0].toString()); } - Random rand = new Random(); - int votingStarter = rand.nextInt(votersQuorum); logger.log(Level.INFO, "Agent number " + votingStarter + " will request to the mediator!"); diff --git a/src/main/java/simple_voting_structure/BaseAgent.java b/src/main/java/simple_voting_structure/BaseAgent.java index 2d1f506..0ea0a1f 100644 --- a/src/main/java/simple_voting_structure/BaseAgent.java +++ b/src/main/java/simple_voting_structure/BaseAgent.java @@ -2,12 +2,16 @@ import jade.core.AID; import jade.core.Agent; +import jade.core.behaviours.CyclicBehaviour; +import jade.core.behaviours.OneShotBehaviour; import jade.domain.DFService; import jade.domain.FIPAException; import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.domain.FIPAAgentManagement.ServiceDescription; import jade.lang.acl.ACLMessage; + import java.util.logging.Logger; +import java.util.Random; import java.util.logging.ConsoleHandler; import java.util.logging.Level; @@ -21,6 +25,11 @@ public abstract class BaseAgent extends Agent { public static final String ANSWER = "ANSWER"; public static final String THANKS = "THANKS"; public static final String START = "START"; + public static final String VOTEID = "VOTEID"; + public static final String INVITE = "INVITE"; + public static final String REGISTERED = "REGISTERED"; + public static final String INFORM = "INFORM"; + public static final String VOTE = "VOTE"; public static final String ANSI_RESET = "\u001B[0m"; public static final String ANSI_BLUE = "\u001B[34m"; @@ -32,23 +41,84 @@ public abstract class BaseAgent extends Agent { public static final String ANSI_CYAN = "\u001B[36m"; public static final String ANSI_WHITE = "\u001B[37m"; + protected static final Random rand = new Random(); + + protected int votingCode; + protected static final Logger logger = Logger.getLogger(BaseAgent.class.getName()); @Override protected void setup() {} + protected CyclicBehaviour handleMessages () { + CyclicBehaviour handleMessages = new CyclicBehaviour(this) { + private static final long serialVersionUID = 1L; + + public void action() { + ACLMessage msg = receive(); + + if ( msg == null ) block(); + else { + switch ( msg.getPerformative() ) { + case ACLMessage.INFORM: + addBehaviour(handleInform(msg)); + break; + case ACLMessage.REQUEST: + addBehaviour(handleRequest(msg)); + break; + default: + logger.log(Level.INFO, + String.format("%s RECEIVED UNEXPECTED MESSAGE PERFORMATIVE FROM %s", getLocalName(), msg.getSender().getLocalName())); + } + } + } + }; + + return handleMessages; + } + + protected OneShotBehaviour handleInform ( ACLMessage msg ) { + OneShotBehaviour handleInform = new OneShotBehaviour(this) { + private static final long serialVersionUID = 1L; + + public void action () {} + }; + + return handleInform; + } + + protected OneShotBehaviour handleRequest ( ACLMessage msg ) { + OneShotBehaviour handleRequest = new OneShotBehaviour(this) { + private static final long serialVersionUID = 1L; + + public void action () {} + }; + + return handleRequest; + } + protected void registerDF(Agent regAgent, String sdName, String sdType) { try { DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(getAID()); + ServiceDescription sd = new ServiceDescription(); sd.setType(sdType); sd.setName(sdName); + + DFAgentDescription [] found = DFService.search(this, dfd); + dfd.addServices(sd); - DFService.register(regAgent, dfd); + if ( found.length == 0 ) { + DFService.register(regAgent, dfd); + } else { + found[0].addServices(sd); + DFService.modify(regAgent, found[0]); + } + logger.log(Level.INFO, getLocalName()+" REGISTERED WITH THE DF" ); } catch (FIPAException e) { e.printStackTrace(); @@ -79,6 +149,26 @@ protected DFAgentDescription[] searchAgentByType (String type) { return foundAgents; } + protected DFAgentDescription[] searchAgentByType (String [] type) { + DFAgentDescription search = new DFAgentDescription(); + + DFAgentDescription [] foundAgents = null; + + for ( int i = 0; i < type.length; ++i ) { + ServiceDescription sd = new ServiceDescription(); + sd.setType(type[i]); + search.addServices(sd); + } + + try { + foundAgents = DFService.search(this, search); + } catch ( Exception any ) { + any.printStackTrace(); + } + + return foundAgents; + } + protected void takeDown() { // Deregister with the DF try { @@ -92,10 +182,8 @@ protected void takeDown() { protected void loggerSetup() { ConsoleHandler handler = new ConsoleHandler(); - handler.setFormatter(new Formatter()); + handler.setFormatter(new LogFormatter()); logger.setUseParentHandlers(false); logger.addHandler(handler); } - - } diff --git a/src/main/java/simple_voting_structure/Formatter.java b/src/main/java/simple_voting_structure/LogFormatter.java similarity index 64% rename from src/main/java/simple_voting_structure/Formatter.java rename to src/main/java/simple_voting_structure/LogFormatter.java index 8b8ecad..08bb5e8 100644 --- a/src/main/java/simple_voting_structure/Formatter.java +++ b/src/main/java/simple_voting_structure/LogFormatter.java @@ -2,11 +2,7 @@ import java.util.logging.LogRecord; -public class Formatter extends java.util.logging.Formatter { - - public Formatter() { - // TODO Auto-generated constructor stub - } +public class LogFormatter extends java.util.logging.Formatter { @Override public String format(LogRecord record) { diff --git a/src/main/java/simple_voting_structure/Mediator.java b/src/main/java/simple_voting_structure/Mediator.java index 7e38b8c..ae3be71 100644 --- a/src/main/java/simple_voting_structure/Mediator.java +++ b/src/main/java/simple_voting_structure/Mediator.java @@ -1,88 +1,128 @@ package simple_voting_structure; import java.util.ArrayList; +import java.util.Arrays; import java.util.logging.Level; +import FIPA.stringsHelper; import jade.core.AID; import jade.core.behaviours.CyclicBehaviour; +import jade.core.behaviours.OneShotBehaviour; +import jade.domain.FIPAException; +import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.lang.acl.ACLMessage; import jade.lang.acl.MessageTemplate; public class Mediator extends BaseAgent { private static final long serialVersionUID = 1L; - - private int answersCnt = 0; - - private int inpA, inpB; + private static final int MAX_VOTING_CODE = 9999; + private static final int MIN_VOTING_VALUE = 1; + private static final int MAX_VOTING_VALUE = 100; + + private int votingAnswer = 0; + private int registeredQuorum = 0; + private int totalQuorum = 0; @Override protected void setup() { logger.log(Level.INFO, "I'm the mediator!"); - - Object[] args = getArguments(); - ArrayList votersName = new ArrayList(); + this.registerDF(this, "Mediator", "mediator"); + + addBehaviour(handleMessages()); + } + + @Override + protected OneShotBehaviour handleInform ( ACLMessage msg ) { + OneShotBehaviour handleInform = new OneShotBehaviour(this) { + private static final long serialVersionUID = 1L; + + public void action () { + if (msg.getContent().startsWith(START)) { + // send them a message requesting for a number; + + votingCode = votingCodeGenerator(); + + setAns(); + + registerDF(myAgent, Integer.toString(votingCode), Integer.toString(votingCode)); + + registeredQuorum = 0; + + logger.log(Level.INFO, String.format("%s AGENT GENERATED VOTING WITH CODE %d!", getLocalName(), votingCode)); + + ACLMessage msg2 = msg.createReply(); + + msg2.setContent(String.format("VOTEID %d MINVALUE %d MAXVALUE %d", votingCode, MIN_VOTING_VALUE, MAX_VOTING_VALUE)); + + send(msg2); + logger.log(Level.INFO, String.format("%s SENT VOTING CODE TO %s", getLocalName(), msg.getSender().getLocalName())); + } else if ( msg.getContent().startsWith(INFORM) ) { + String [] splittedMsg = msg.getContent().split(" "); + + totalQuorum = Integer.parseInt(splittedMsg[2]); + + logger.log(Level.INFO, String.format("EXPECTED QUORUM BY %s: %d VOTERS!", getLocalName(), totalQuorum)); + } else if ( msg.getContent().startsWith(REGISTERED) ) { + ++registeredQuorum; + + if ( registeredQuorum == totalQuorum ) { + logger.log(Level.INFO, "TOTAL QUORUM REACHED! REQUESTING VOTES!"); + + requestVotes(); + } + } else { + logger.log(Level.INFO, + String.format("%s RECEIVED AN UNEXPECTED MESSAGE FROM %s", getLocalName(), msg.getSender().getLocalName())); + } + } + }; - if (args != null && args.length > 0) { - for (Object voter : args) { - votersName.add(voter.toString()); + return handleInform; + } + + private void requestVotes() { + try { + ACLMessage requestVoteMsg = new ACLMessage(ACLMessage.REQUEST); + requestVoteMsg.setContent(String.format("%s VOTE FOR %d", REQUEST, votingCode)); + + ArrayList foundVotingParticipants = new ArrayList<>(); + String [] types = { Integer.toString(votingCode), "voter" }; + foundVotingParticipants = new ArrayList( + Arrays.asList(searchAgentByType(types))); + + if ( foundVotingParticipants.size() != registeredQuorum ) { + throw new Exception(String.format("FOUND VOTERS DIFFERS FROM REGISTERED QUORUM! (%d x %d)", + foundVotingParticipants.size(), registeredQuorum)); } + + foundVotingParticipants.forEach(ag -> { + requestVoteMsg.addReceiver(ag.getName()); + }); + + send(requestVoteMsg); + logger.log(Level.INFO, + String.format("%s REQUESTED A VOTE FOR ALL %d VOTERS!", getLocalName(), foundVotingParticipants.size())); + } catch (Exception e) { + e.printStackTrace(); } + } + + private int votingCodeGenerator () { + int proposedCode; + DFAgentDescription [] foundAgents; - this.registerDF(this, "Mediator", "mediator"); - - addBehaviour(new CyclicBehaviour(this) { - public void action() { - // listen if a greetings message arrives - ACLMessage msg = receive(MessageTemplate.MatchPerformative(ACLMessage.INFORM)); - if (msg != null) { - if (ANSWER.equalsIgnoreCase(msg.getContent().split(" ")[0])) { - // if an ANSWER to a greetings message is arrived - // then send a THANKS message - logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED ANSWER MESSAGE FROM " + msg.getSender().getLocalName()); - ACLMessage replyT = msg.createReply(); - replyT.setContent(THANKS); - myAgent.send(replyT); - logger.log(Level.INFO, myAgent.getLocalName() + " SENT THANKS MESSAGE"); - - if (msg.getSender().getLocalName().equals(votersName.get(0))) { - inpA = Integer.parseInt(msg.getContent().split(" ")[1]); - } else { - inpB = Integer.parseInt(msg.getContent().split(" ")[1]); - } - - answersCnt++; - if (answersCnt == 2) { - ACLMessage replyW = new ACLMessage(ACLMessage.INFORM); - - replyW.setContent((((inpA + inpB) % 2 != 0) ? ODD + " " + votersName.get(0) - : EVEN + " " + votersName.get(1)) + " WINNER!"); - replyW.addReceiver(new AID(votersName.get(0), AID.ISLOCALNAME)); - replyW.addReceiver(new AID(votersName.get(1), AID.ISLOCALNAME)); - myAgent.send(replyW); + do { + proposedCode = rand.nextInt(MAX_VOTING_CODE); + + foundAgents = searchAgentByType(Integer.toString(proposedCode)); + } while ( foundAgents.length > 0 ); - logger.log(Level.INFO, myAgent.getLocalName() + " SENT WINNER MESSAGE"); - } - } else if (START.equalsIgnoreCase(msg.getContent())) { - // send them a message requesting for a number; - ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM); - msg2.setContent(REQUEST); - - msg2.addReceiver(new AID(votersName.get(0), AID.ISLOCALNAME)); - msg2.addReceiver(new AID(votersName.get(1), AID.ISLOCALNAME)); + return proposedCode; + } - send(msg2); - logger.log(Level.INFO, getLocalName() + " SENT REQUEST MESSAGE TO " + votersName.get(0) + " AND " + votersName.get(1)); - } else { - logger.log(Level.INFO, - myAgent.getLocalName() + " Unexpected message received from " + msg.getSender().getLocalName()); - } - } else { - // if no message is arrived, block the behaviour - block(); - } - } - }); + private void setAns(){ + votingAnswer = rand.nextInt(MIN_VOTING_VALUE, MAX_VOTING_VALUE + 1); } } diff --git a/src/main/java/simple_voting_structure/Voter.java b/src/main/java/simple_voting_structure/Voter.java index cab792f..ecd92bf 100644 --- a/src/main/java/simple_voting_structure/Voter.java +++ b/src/main/java/simple_voting_structure/Voter.java @@ -1,9 +1,13 @@ package simple_voting_structure; +import java.util.ArrayList; +import java.util.Arrays; import java.util.logging.Level; import jade.core.AID; +import jade.core.Agent; import jade.core.behaviours.CyclicBehaviour; +import jade.core.behaviours.OneShotBehaviour; import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.lang.acl.ACLMessage; import jade.lang.acl.MessageTemplate; @@ -11,6 +15,10 @@ public class Voter extends BaseAgent { private static final long serialVersionUID = 1L; + + private int minVotingValue = 0; + private int maxVotingValue = 0; + private int myVotingValue = 0; @Override protected void setup() { @@ -18,56 +26,141 @@ protected void setup() { this.registerDF(this, "Voter", "voter"); - addBehaviour(new CyclicBehaviour(this) { - public void action() { - // listen if a greetings message arrives - ACLMessage msg = receive(MessageTemplate.MatchPerformative(ACLMessage.INFORM)); - if (msg != null) { - if (REQUEST.equalsIgnoreCase(msg.getContent())) { - final int Max = 10; - final int Min = 1; + addBehaviour(handleMessages()); + } + + @Override + protected OneShotBehaviour handleInform ( ACLMessage msg ) { + + OneShotBehaviour handleInform = new OneShotBehaviour(this) { + private static final long serialVersionUID = 1L; - // if a greetings message is arrived then send an ANSWER - logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED REQUEST MESSAGE FROM " + msg.getSender().getLocalName()); - ACLMessage reply = msg.createReply(); - reply.setContent(ANSWER + " " + (Min + (int) (Math.random() * ((Max - Min) + 1)))); - myAgent.send(reply); - logger.log(Level.INFO, myAgent.getLocalName() + " SENT ANSWER MESSAGE"); - } else if (START.equalsIgnoreCase(msg.getContent())) { - ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM); - msg2.setContent(START); - - DFAgentDescription [] foundAgents = searchAgentByType("mediator"); - - try { - AID foundMediator = null; - if ( foundAgents.length > 0 ) { - foundMediator = foundAgents[0].getName(); - - msg2.addReceiver(foundMediator); - - send(msg2); - logger.log(Level.INFO, getLocalName() + " SENT START MESSAGE TO " + foundMediator.getLocalName()); - } - } catch ( Exception any ) { - logger.log(Level.SEVERE, ANSI_RED + "ERROR WHILE SENDING MESSAGE" + ANSI_RESET); - any.printStackTrace(); + public void action () { + if (msg.getContent().startsWith(START)) { + ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM); + msg2.setContent(START); + + DFAgentDescription [] foundAgents = searchAgentByType("mediator"); + + try { + AID foundMediator = null; + if ( foundAgents.length > 0 ) { + foundMediator = foundAgents[0].getName(); + + msg2.addReceiver(foundMediator); + + send(msg2); + logger.log(Level.INFO, String.format("%s SENT START MESSAGE TO %s", getLocalName(), foundMediator.getLocalName())); } - } else if (THANKS.equalsIgnoreCase(msg.getContent())) { - logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED THANKS MESSAGE FROM " + msg.getSender().getLocalName()); - } else if (ODD.equalsIgnoreCase(msg.getContent().split(" ")[0]) - || EVEN.equalsIgnoreCase(msg.getContent().split(" ")[0])) { - logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED RESULTS MESSAGE FROM " + msg.getSender().getLocalName()); - logger.log(Level.INFO, myAgent.getLocalName() + " " + msg.getContent()); - } else { - logger.log(Level.INFO, - myAgent.getLocalName() + " Unexpected message received from " + msg.getSender().getLocalName()); + } catch ( Exception any ) { + logger.log(Level.SEVERE, ANSI_RED + "ERROR WHILE SENDING MESSAGE" + ANSI_RESET); + any.printStackTrace(); } + } else if (THANKS.equalsIgnoreCase(msg.getContent())) { + logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED THANKS MESSAGE FROM " + msg.getSender().getLocalName()); + } else if (ODD.equalsIgnoreCase(msg.getContent().split(" ")[0]) + || EVEN.equalsIgnoreCase(msg.getContent().split(" ")[0])) { + logger.log(Level.INFO, myAgent.getLocalName() + " RECEIVED RESULTS MESSAGE FROM " + msg.getSender().getLocalName()); + logger.log(Level.INFO, myAgent.getLocalName() + " " + msg.getContent()); + } else if (msg.getContent().startsWith(VOTEID)) { + logger.log(Level.INFO, + String.format("RECEIVED VOTING STRUCTURE FROM %s: %s", msg.getSender().getLocalName(), msg.getContent())); + + String [] splittedMsg = msg.getContent().split(" "); + + votingCode = Integer.parseInt(splittedMsg[1]); + minVotingValue = Integer.parseInt(splittedMsg[3]); + maxVotingValue = Integer.parseInt(splittedMsg[5]); + + registerDF(myAgent, Integer.toString(votingCode), Integer.toString(votingCode)); + + informVotingRegistration(); + + ArrayList foundAgents = new ArrayList( + Arrays.asList(searchAgentByType("voter"))); + + ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM); + msg2.setContent(String.format("%s %s", INVITE, msg.getContent())); + + foundAgents.forEach(ag -> { + if ( !ag.getName().equals(myAgent.getAID()) ) { + msg2.addReceiver(ag.getName()); + } + }); + + logger.log(Level.INFO, + String.format("%s RECEIVED UNEXPECTED MESSAGE FROM %s", getLocalName(), msg.getSender().getLocalName())); ACLMessage reply = msg.createReply(); + reply.setContent(String.format("%s QUORUM %d", INFORM, foundAgents.size())); + myAgent.send(reply); + + send(msg2); + logger.log(Level.INFO, String.format("%s SENT INVITE TO VOTERS!", getLocalName())); + + + } else if (msg.getContent().startsWith(INVITE)) { + logger.log(Level.INFO, + String.format("RECEIVED VOTING STRUCTURE FROM %s: %s", msg.getSender().getLocalName(), msg.getContent())); + + String [] splittedMsg = msg.getContent().split(" "); + + votingCode = Integer.parseInt(splittedMsg[2]); + minVotingValue = Integer.parseInt(splittedMsg[4]); + maxVotingValue = Integer.parseInt(splittedMsg[6]); + + registerDF(myAgent, Integer.toString(votingCode), Integer.toString(votingCode)); + + informVotingRegistration(); } else { - // if no message is arrived, block the behaviour - block(); + logger.log(Level.INFO, + String.format("%s RECEIVED UNEXPECTED MESSAGE FROM %s", getLocalName(), msg.getSender().getLocalName())); } } + }; + + return handleInform; + } + + @Override + protected OneShotBehaviour handleRequest ( ACLMessage msg ) { + OneShotBehaviour handleRequest = new OneShotBehaviour(this) { + private static final long serialVersionUID = 1L; + + public void action () { + if (msg.getContent().startsWith(REQUEST)) { + + myVotingValue = rand.nextInt(minVotingValue, maxVotingValue + 1); + + ACLMessage voteMsg = msg.createReply(); + voteMsg.setPerformative(ACLMessage.INFORM); + voteMsg.setContent(String.format("%s ON %d: %d", VOTE, votingCode, myVotingValue)); + + send(voteMsg); + + logger.log(Level.INFO, String.format("%s SENT VOTE TO %s", getLocalName(), msg.getSender().getLocalName())); + } else { + logger.log(Level.INFO, + String.format("%s RECEIVED UNEXPECTED MESSAGE FROM %s", getLocalName(), msg.getSender().getLocalName())); + } + } + }; + + return handleRequest; + } + + private void informVotingRegistration() { + ACLMessage informMsg = new ACLMessage(ACLMessage.INFORM); + informMsg.setContent(String.format("%s IN %d", REGISTERED, votingCode)); + + ArrayList foundVotingParticipants = new ArrayList<>(); + String [] types = { Integer.toString(votingCode), "mediator" }; + foundVotingParticipants = new ArrayList( + Arrays.asList(searchAgentByType(types))); + + foundVotingParticipants.forEach(ag -> { + informMsg.addReceiver(ag.getName()); }); + + send(informMsg); + logger.log(Level.INFO, String.format("%s INFORMED VOTING REGISTRATION TO MEDIATOR!", getLocalName())); } }