Clean source

This commit is contained in:
2023-01-10 19:46:53 +01:00
commit 7bbcfaed36
7 changed files with 442 additions and 0 deletions

26
build.xml Normal file
View File

@@ -0,0 +1,26 @@
<project name="JadeLab2" default="run" basedir=".">
<property name="src" location="src"/>
<property name="build" location="build"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init" description="compile the source " >
<javac srcdir="${src}" destdir="${build}">
<classpath location="jade/lib/jade.jar" />
</javac>
</target>
<target name="clean" description="clean up" >
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
<target name="run" description="create agents" >
<java fork="true" classpath="jade/lib/jade.jar;build" classname="jade.Boot">
<arg value="-gui" />
<arg value="buyer:jadelab2.BookBuyerAgent(10000);seller1:jadelab2.BookSellerAgent;seller2:jadelab2.BookSellerAgent" />
</java>
</target>
</project>

Binary file not shown.

BIN
jade/lib/jade.jar Normal file

Binary file not shown.

173
src/BookBuyerAgent.java Normal file
View File

@@ -0,0 +1,173 @@
package jadelab2;
import jade.core.Agent;
import jade.core.AID;
import jade.core.behaviours.*;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
public class BookBuyerAgent extends Agent {
private BookBuyerGui myGui;
private String targetBookTitle;
//list of found sellers
private AID[] sellerAgents;
protected void setup() {
targetBookTitle = "";
System.out.println("Hello! " + getAID().getLocalName() + " is ready for the purchase order.");
myGui = new BookBuyerGui(this);
myGui.display();
//time interval for buyer for sending subsequent CFP
//as a CLI argument
int interval = 20000;
Object[] args = getArguments();
if (args != null && args.length > 0) interval = Integer.parseInt(args[0].toString());
addBehaviour(new TickerBehaviour(this, interval)
{
protected void onTick()
{
//search only if the purchase task was ordered
if (!targetBookTitle.equals(""))
{
System.out.println(getAID().getLocalName() + ": I'm looking for " + targetBookTitle);
//update a list of known sellers (DF)
DFAgentDescription template = new DFAgentDescription();
ServiceDescription sd = new ServiceDescription();
sd.setType("book-selling");
template.addServices(sd);
try
{
DFAgentDescription[] result = DFService.search(myAgent, template);
System.out.println(getAID().getLocalName() + ": the following sellers have been found");
sellerAgents = new AID[result.length];
for (int i = 0; i < result.length; ++i)
{
sellerAgents[i] = result[i].getName();
System.out.println(sellerAgents[i].getLocalName());
}
}
catch (FIPAException fe)
{
fe.printStackTrace();
}
myAgent.addBehaviour(new RequestPerformer());
}
}
});
}
//invoked from GUI, when purchase was ordered
public void lookForTitle(final String title)
{
addBehaviour(new OneShotBehaviour()
{
public void action()
{
targetBookTitle = title;
System.out.println(getAID().getLocalName() + ": purchase order for " + targetBookTitle + " accepted");
}
});
}
protected void takeDown() {
myGui.dispose();
System.out.println("Buyer agent " + getAID().getLocalName() + " terminated.");
}
private class RequestPerformer extends Behaviour {
private AID bestSeller;
private int bestPrice;
private int repliesCnt = 0;
private MessageTemplate mt;
private int step = 0;
public void action() {
switch (step) {
case 0:
//call for proposal (CFP) to found sellers
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
for (int i = 0; i < sellerAgents.length; ++i) {
cfp.addReceiver(sellerAgents[i]);
}
cfp.setContent(targetBookTitle);
cfp.setConversationId("book-trade");
cfp.setReplyWith("cfp"+System.currentTimeMillis()); //unique value
myAgent.send(cfp);
mt = MessageTemplate.and(MessageTemplate.MatchConversationId("book-trade"),
MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
step = 1;
break;
case 1:
//collect proposals
ACLMessage reply = myAgent.receive(mt);
if (reply != null) {
if (reply.getPerformative() == ACLMessage.PROPOSE) {
//proposal received
int price = Integer.parseInt(reply.getContent());
if (bestSeller == null || price < bestPrice) {
//the best proposal as for now
bestPrice = price;
bestSeller = reply.getSender();
}
}
repliesCnt++;
if (repliesCnt >= sellerAgents.length) {
//all proposals have been received
step = 2;
}
}
else {
block();
}
break;
case 2:
//best proposal consumption - purchase
ACLMessage order = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
order.addReceiver(bestSeller);
order.setContent(targetBookTitle);
order.setConversationId("book-trade");
order.setReplyWith("order"+System.currentTimeMillis());
myAgent.send(order);
mt = MessageTemplate.and(MessageTemplate.MatchConversationId("book-trade"),
MessageTemplate.MatchInReplyTo(order.getReplyWith()));
step = 3;
break;
case 3:
//seller confirms the transaction
reply = myAgent.receive(mt);
if (reply != null) {
if (reply.getPerformative() == ACLMessage.INFORM) {
//purchase succeeded
System.out.println(getAID().getLocalName() + ": " + targetBookTitle + " purchased for " + bestPrice + " from " + reply.getSender().getLocalName());
System.out.println(getAID().getLocalName() + ": waiting for the next purchase order.");
targetBookTitle = "";
//myAgent.doDelete();
}
else {
System.out.println(getAID().getLocalName() + ": purchase has failed. " + targetBookTitle + " was sold in the meantime.");
}
step = 4; //this state ends the purchase process
}
else {
block();
}
break;
}
}
public boolean done() {
if (step == 2 && bestSeller == null) {
System.out.println(getAID().getLocalName() + ": " + targetBookTitle + " is not on sale.");
}
//process terminates here if purchase has failed (title not on sale) or book was successfully bought
return ((step == 2 && bestSeller == null) || step == 4);
}
}
}

60
src/BookBuyerGui.java Normal file
View File

@@ -0,0 +1,60 @@
package jadelab2;
import jade.core.AID;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class BookBuyerGui extends JFrame {
private BookBuyerAgent myAgent;
private JTextField titleField;
BookBuyerGui(BookBuyerAgent a) {
super(a.getLocalName());
myAgent = a;
JPanel p = new JPanel();
p.setLayout(new GridLayout(2, 2));
p.add(new JLabel("Title:"));
titleField = new JTextField(15);
p.add(titleField);
getContentPane().add(p, BorderLayout.CENTER);
JButton addButton = new JButton("Search");
addButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ev) {
try {
String title = titleField.getText().trim();
myAgent.lookForTitle(title);
titleField.setText("");
}
catch (Exception e) {
JOptionPane.showMessageDialog(BookBuyerGui.this, "Invalid values. " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
} );
p = new JPanel();
p.add(addButton);
getContentPane().add(p, BorderLayout.SOUTH);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
myAgent.doDelete();
}
} );
setResizable(false);
}
public void display() {
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int centerX = (int)screenSize.getWidth() / 2;
int centerY = (int)screenSize.getHeight() / 2;
setLocation(centerX - getWidth() / 2, centerY - getHeight() / 2);
setVisible(true);
}
}

118
src/BookSellerAgent.java Normal file
View File

@@ -0,0 +1,118 @@
package jadelab2;
import jade.core.Agent;
import jade.core.behaviours.*;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import java.util.*;
public class BookSellerAgent extends Agent {
private Hashtable catalogue;
private BookSellerGui myGui;
protected void setup() {
catalogue = new Hashtable();
myGui = new BookSellerGui(this);
myGui.display();
//book selling service registration at DF
DFAgentDescription dfd = new DFAgentDescription();
dfd.setName(getAID());
ServiceDescription sd = new ServiceDescription();
sd.setType("book-selling");
sd.setName("JADE-book-trading");
dfd.addServices(sd);
try {
DFService.register(this, dfd);
}
catch (FIPAException fe) {
fe.printStackTrace();
}
addBehaviour(new OfferRequestsServer());
addBehaviour(new PurchaseOrdersServer());
}
protected void takeDown() {
//book selling service deregistration at DF
try {
DFService.deregister(this);
}
catch (FIPAException fe) {
fe.printStackTrace();
}
myGui.dispose();
System.out.println("Seller agent " + getAID().getName() + " terminated.");
}
//invoked from GUI, when a new book is added to the catalogue
public void updateCatalogue(final String title, final int price) {
addBehaviour(new OneShotBehaviour() {
public void action() {
catalogue.put(title, new Integer(price));
System.out.println(getAID().getLocalName() + ": " + title + " put into the catalogue. Price = " + price);
}
} );
}
private class OfferRequestsServer extends CyclicBehaviour {
public void action() {
//proposals only template
MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.CFP);
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
String title = msg.getContent();
ACLMessage reply = msg.createReply();
Integer price = (Integer) catalogue.get(title);
if (price != null) {
//title found in the catalogue, respond with its price as a proposal
reply.setPerformative(ACLMessage.PROPOSE);
reply.setContent(String.valueOf(price.intValue()));
}
else {
//title not found in the catalogue
reply.setPerformative(ACLMessage.REFUSE);
reply.setContent("not-available");
}
myAgent.send(reply);
}
else {
block();
}
}
}
private class PurchaseOrdersServer extends CyclicBehaviour {
public void action() {
//purchase order as proposal acceptance only template
MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL);
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
String title = msg.getContent();
ACLMessage reply = msg.createReply();
Integer price = (Integer) catalogue.remove(title);
if (price != null) {
reply.setPerformative(ACLMessage.INFORM);
System.out.println(getAID().getLocalName() + ": " + title + " sold to " + msg.getSender().getLocalName());
}
else {
//title not found in the catalogue, sold to another agent in the meantime (after proposal submission)
reply.setPerformative(ACLMessage.FAILURE);
reply.setContent("not-available");
}
myAgent.send(reply);
}
else {
block();
}
}
}
}

65
src/BookSellerGui.java Normal file
View File

@@ -0,0 +1,65 @@
package jadelab2;
import jade.core.AID;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class BookSellerGui extends JFrame {
private BookSellerAgent myAgent;
private JTextField titleField, priceField;
BookSellerGui(BookSellerAgent a) {
super(a.getLocalName());
myAgent = a;
JPanel p = new JPanel();
p.setLayout(new GridLayout(2, 2));
p.add(new JLabel("Title:"));
titleField = new JTextField(15);
p.add(titleField);
p.add(new JLabel("Price:"));
priceField = new JTextField(15);
p.add(priceField);
getContentPane().add(p, BorderLayout.CENTER);
JButton addButton = new JButton("Add");
addButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ev) {
try {
String title = titleField.getText().trim();
String price = priceField.getText().trim();
myAgent.updateCatalogue(title, Integer.parseInt(price));
titleField.setText("");
priceField.setText("");
}
catch (Exception e) {
JOptionPane.showMessageDialog(BookSellerGui.this, "Invalid values. " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
} );
p = new JPanel();
p.add(addButton);
getContentPane().add(p, BorderLayout.SOUTH);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
myAgent.doDelete();
}
} );
setResizable(false);
}
public void display() {
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int centerX = (int)screenSize.getWidth() / 2;
int centerY = (int)screenSize.getHeight() / 2;
setLocation(centerX - getWidth() / 2, centerY - getHeight() / 2);
setVisible(true);
}
}