贴一个比较有意思的多Agent应用系统,实现二手图书交易功能。
首先来看一下系统功能和需求说明:
1、系统功能要求
系统中需要构建两类 Agent:"卖书"Agent和"买书"Agent,通过启动多个买方
agent 和卖方 agent可实现自动“图书交易”。
(1)买方Agent功能要求买方Agent(对应bookTrading.buyer包中的BookBuyerAgent类),带有一个简单的图形界面GUI, 买方Agent 界面允许买方用户指定他想购买的书名和其他在交易协商谈判时需要的信息(如允许的最高价格和最后时间期限等)。买方Agent 启动时首先显示图形界面,然后添加一个周期性行为,该行为每隔一段时间(本例中指定为6秒)周期性地利用 DF 的黄页服务搜索 JADE平台内可提供 Book-selling类型服务的卖方 agent,从而更行自己的卖方agent列表。买方 Agent终止时要关闭该图形界面。
当买方用户填写完要购买图书的名称、允许的最高价格和最后时间期限,并按下“Buy”按钮时,买方agent会添加一个针对该书的周期性购买管理行为(对应PurchaseManager行为类,在课程PPT 第四讲中有介绍)。为了尽可能以最低价格买到该图书,PurchaseManager行为使用一个非常简单的策略来管理要购买图书的当前可接受价格。每个周期(本例中指定为6 秒)中,买方 agent 当前能接受的价格线性上升,直到当交易截止时间临近时达到可以接受的价格上限。此
外在每个周期中当更新当前可接受价格之后,会针对该图书以当前可接受价格发起一轮与卖方agent的图书协商行为(对应BookNegotiator行为类)。当买方agent顺利购买图书后或者超过了用户指定的购书最后期限时,PurchaseManager行为被终止。
图书协商行为(对应 BookNegotiator行为类,在课程PPT 第五讲中有介绍)为普通行为,它针对指定图书以当前可接受价格完成一轮与卖方agent的图书协商。BookNegotiator行为在第 0 阶段首先向目前可提供Book-selling类型服务的所有卖方Agent发送 CFP 消息;然后在第1 阶段接收和收集卖方 agent 的 REFUSE或PROPOSE通信原语的回复消息,并记录给出最低报价的卖方Agent信息;然后在第2 阶段如果最低报价不超过当前可接受价格,那么发送一个通信原语为ACCEPT_PROPOSAL的请求购买消息给最佳卖方agent;在第3 阶段如果接受到卖方 agent 的通信原语为 INFORM 的确定消息表示购买成功,那么终止该图书对应的PurchaseManager行为。
(2)卖方Agent功能要求
卖方 Agent(对应bookTrading.seller包中的 BookSellerAgent类),也带有一个简单的图形界面,通过该界面用户可指定欲出售图书的名称、出售初始价格、出售价格下限和出售时间限制,从而向本地的书目列表(catalogue)中添加新的书目。买方Agent启动时首先显示图形界面,然后添加两个循环行为:处理报价请求的行为(对应于CallForOfferServer类)和处理购买请求的行为(对应于PurchaseOrderServer类),最后在DF中注册其具有的Book-selling类型服务。卖方Agent终止时首先关闭其图形界面,还要在DF中注销其提供的服务。
当卖方用户填写完欲出售图书的名称、出售初始价格、出售价格下限和出售时间限制,并按下“Sell”按钮时,卖方agent会添加一个针对该书的周期性价格管理行为(对应PriceManager行为类,在课程PPT 第四讲中有介绍)。在该行为首次被调度时,应向卖方agent的本地出售图书哈希表(catalogue)中添加新的书目。为了尽可能以最高价格出售该图书,PriceManager行为也使用一个非常简单的线性递减定价策略来管理欲出售图书的当前出售价格。每个周期(本例中指定为6 秒)中,卖方 agent 逐渐线性下调当前价格,直到当交易截止时间临近时达到出售价格下限。每个周期中,该行为也应判断该图书是否超过交易期限,该行为应在图书哈希表中删除超过交易期限的图书。
处理报价请求的循环行为(对应于CallForOfferServer类)只监听、接收和处理卖方Agent消息队列中来至买方的携带报价请求信息(通信原语为INFORM)的消息。该行为在收到该消息后,在本地图书哈希表中查询是否有报价请求消息中指定的图书可卖,若有回复买方agent一条通信原语为PROPOSE的消息,若没有则回复买方agent一条通信原语为REFUSE的消息。
处理购买请求的循环行为(对应于PurchaseOrderServer类)只监听、接收
和处理卖方 Agent消息队列中来至买方的携带购买请求信息(通信原语为ACCEPT_PROPOSAL)的消息。该行为在收到该消息后,从本地图书哈希表中查询是否有购买请求消息中指定的图书,若有则删除该图书,回复买方agent一条通信原语为INFORM的购买成功确认消息,并终止该书对应的价格管理行为PriceManager;若没有则回复买方agent一条通信原语为FAILURE的购买失败消息。
附源代码:
BookBuyerAgent.java
package com.leejay.booktrading.buyer;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import java.util.Date;
import java.util.Vector;
public class BookBuyerAgent extends Agent {
// The list of known seller agents
private Vector sellerAgents = new Vector();
// The GUI to interact with the user
private BookBuyerGui myGui;
/**
* Agent initializations
**/
protected void setup() {
// Printout a welcome message
System.out.println("Buyer-agent " + getAID().getName() + " is ready.");
// Get names of seller agents as arguments
/*
* Object[] args = getArguments(); if (args != null && args.length > 0)
* { for (int i = 0; i < args.length; ++i) { AID seller = new
* AID((String) args[i], AID.ISLOCALNAME);
* sellerAgents.addElement(seller); } }
*/
// Show the GUI to interact with the user
myGui = new BookBuyerGuiImpl();
myGui.setAgent(this);
myGui.show();
/**
* This piece of code, to search services with the DF, is explained in
* the book in section 4.4.3
**/
// Update the list of seller agents every 6s
addBehaviour(new SearchSeller(this, 6000));
}
/**
* Agent clean-up
**/
protected void takeDown() {
// Dispose the GUI if it is there
if (myGui != null) {
myGui.dispose();
}
// Printout a dismissal message
System.out.println("Buyer-agent " + getAID().getName() + "terminated.");
}
/**
* This method is called by the GUI when the user inserts a new book to buy
*
* @param title
* The title of the book to buy
* @param maxPrice
* The maximum acceptable price to buy the book
* @param deadline
* The deadline by which to buy the book
**/
public void purchase(String title, int maxPrice, Date deadline) {
// the following line is in the book at page 62
addBehaviour(new PurchaseManager(this, title, maxPrice, deadline));
}
/**
* This method is called by the GUI. At the moment it is not implemented.
**/
/*
* public void setCreditCard(String creditCarNumber) { }
*/
/**
* Inner class SearchSeller. This is the behaviour used by Book-buyer agents
* to Update the list of seller agents every 6s
*
*/
private class SearchSeller extends TickerBehaviour {
public SearchSeller(Agent a, long time) {
super(a, time);
}
@Override
protected void onTick() {
DFAgentDescription template = new DFAgentDescription();
ServiceDescription templateSd = new ServiceDescription();
templateSd.setType("book-trade");
template.addServices(templateSd);
try {
DFAgentDescription[] result = DFService.search(getAgent(),
template);
//update sellerAgents
sellerAgents.clear();
for (int i = 0; i < result.length; i++) {
sellerAgents.add(result[i].getName());
}
} catch (FIPAException fe) {
fe.printStackTrace();
}
}
}
/**
* Section 4.2.4, Page 62
**/
private class PurchaseManager extends TickerBehaviour {
private String title;
private int maxPrice, startPrice =0;
private long deadline, initTime, deltaT;
private PurchaseManager(Agent a, String t, int mp, Date d) {
super(a, 6000); // tick every minute
title = t;
maxPrice = mp;
deadline = d.getTime();
initTime = System.currentTimeMillis();
deltaT = deadline - initTime;
}
public void onTick() {
long currentTime = System.currentTimeMillis();
if (currentTime > deadline) {
// Deadline expired
myGui.notifyUser("Cannot buy book " + title);
stop();
} else {
// Compute the currently acceptable price and start a
// negotiation
long elapsedTime = currentTime - initTime;
int acceptablePrice = (int) Math.round(1.0 * maxPrice
* (1.0 * elapsedTime / deltaT));
// System.out.println("elapsedTime"+elapsedTime+"deltaT"+deltaT+"acceptablePrice"+acceptablePrice+"maxPrice="+maxPrice);
myAgent.addBehaviour(new BookNegotiator(title, acceptablePrice,
this));
}
}
}
/**
* Section 4.3.5 of the book, page 69 Inner class BookNegotiator. This is
* the behaviour used by Book-buyer agents to actually negotiate with seller
* agents the purchase of a book.
**/
private class BookNegotiator extends Behaviour {
private String title;
private int maxPrice; //maxmimum accepted price
private PurchaseManager manager;
private AID bestSeller; // The seller agent who provides the best offer
private int bestPrice; // The best offered price
private int repliesCnt = 0; // The counter of replies from seller agents
private MessageTemplate mt; // The template to receive replies
private int step = 0;
public BookNegotiator(String t, int p, PurchaseManager m) {
super(null);
title = t;
maxPrice = p;
manager = m;
}
public void action() {
switch (step) {
case 0:
// Send the cfp to all sellers
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
for (int i = 0; i < sellerAgents.size(); ++i) {
cfp.addReceiver((AID) sellerAgents.elementAt(i));
}
cfp.setContent(title);
cfp.setConversationId("book-trade");
cfp.setReplyWith("cfp" + System.currentTimeMillis()); // Unique
// value
myAgent.send(cfp);
myGui.notifyUser("Sent Call for Proposal");
// Prepare the template to get proposals
mt = MessageTemplate.and(
MessageTemplate.MatchConversationId("book-trade"),
MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
step = 1;
break;
case 1:
// Receive all proposals/refusals from seller agents
ACLMessage reply = myAgent.receive(mt);
if (reply != null) {
// Reply received
if (reply.getPerformative() == ACLMessage.PROPOSE) {
// This is an offer
int price = Integer.parseInt(reply.getContent());
myGui.notifyUser("Received Proposal at " + price
+ " when maximum acceptable price was "
+ maxPrice);
if (bestSeller == null || price < bestPrice) {
// This is the best offer at present
bestPrice = price;
bestSeller = reply.getSender();
}
}
repliesCnt++;
if (repliesCnt >= sellerAgents.size()) {
// We received all replies
step = 2;
}
} else {
block();
}
break;
case 2:
if (bestSeller != null && bestPrice <= maxPrice) {
// Send the purchase order to the seller that provided the
// best offer
ACLMessage order = new ACLMessage(
ACLMessage.ACCEPT_PROPOSAL);
order.addReceiver(bestSeller);
order.setContent(title);
order.setConversationId("Book-selling");
order.setReplyWith("order" + System.currentTimeMillis());
myAgent.send(order);
myGui.notifyUser("sent Accept Proposal");
// Prepare the template to get the purchase order reply
mt = MessageTemplate.and(MessageTemplate
.MatchConversationId("book-trade"), MessageTemplate
.MatchInReplyTo(order.getReplyWith()));
step = 3;
} else {
// If we received no acceptable proposals, terminate
step = 4;
}
break;
case 3:
// Receive the purchase order reply
reply = myAgent.receive(mt);
if (reply != null) {
// Purchase order reply received
if (reply.getPerformative() == ACLMessage.INFORM) {
// Purchase successful. We can terminate
myGui.notifyUser("Book " + title
+ " successfully purchased. Price = "
+ bestPrice);
manager.stop();
}
step = 4;
} else {
block();
}
break;
} // end of switch
}
public boolean done() {
return step == 4;
}
} // End of inner class BookNegotiator
}
package com.leejay.booktrading.seller;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class BookSellerAgent extends Agent {
// The catalogue of books available for sale
private Map catalogue = new HashMap();
// The GUI to interact with the user
private BookSellerGui myGui;
public static final String SERVICE_NAME = "sell_service";
/**
* Agent initializations
**/
protected void setup() {
// Printout a welcome message
System.out.println("Seller-agent " + getAID().getName() + " is ready.");
// Create and show the GUI
myGui = new BookSellerGuiImpl();
myGui.setAgent(this);
myGui.show();
// Add the behaviour serving calls for price from buyer agents
addBehaviour(new CallForOfferServer());
// Add the behaviour serving purchase requests from buyer agents
addBehaviour(new PurchaseOrderServer());
/**
* This piece of code, to register services with the DF, is explained in
* the book in section 4.4.2.1
**/
// Register the book-selling service in the yellow pages
registService();
}
/**
* Register the book-selling service in the yellow pages
*/
private void registService() {
DFAgentDescription dfd = new DFAgentDescription();
dfd.setName(getAID());
ServiceDescription sd = new ServiceDescription();
sd.setName(SERVICE_NAME);
sd.setType("Book-selling");
dfd.addServices(sd);
try {
DFService.register(this, dfd);
} catch (FIPAException fe) {
fe.printStackTrace();
}
}
/**
* Agent clean-up
**/
protected void takeDown() {
// Dispose the GUI if it is there
if (myGui != null) {
myGui.dispose();
}
// Printout a dismissal message
System.out.println("Seller-agent " + getAID().getName()
+ "terminating.");
/**
* This piece of code, to deregister with the DF, is explained in the
* book in section 4.4.2.1, page 73
**/
// Deregister from the yellow pages
try {
DFService.deregister(this);
} catch (FIPAException e) {
e.printStackTrace();
}
}
/**
* This method is called by the GUI when the user inserts a new book for
* sale
*
* @param title
* The title of the book for sale
* @param initialPrice
* The initial price
* @param minPrice
* The minimum price
* @param deadline
* The deadline by which to sell the book
**/
public void putForSale(String title, int initPrice, int minPrice,
Date deadline) {
addBehaviour(new PriceManager(this, title, initPrice, minPrice,
deadline));
}
private class PriceManager extends TickerBehaviour {
private String title;
private int minPrice, currentPrice, initPrice, deltaP;
private long initTime, deadline, deltaT;
private PriceManager(Agent a, String t, int ip, int mp, Date d) {
super(a, 6000); // tick every minute
title = t;
initPrice = ip;
currentPrice = initPrice;
deltaP = initPrice - mp;
deadline = d.getTime();
initTime = System.currentTimeMillis();
deltaT = ((deadline - initTime) > 0 ? (deadline - initTime) : 6000);
}
public void onStart() {
// Insert the book in the catalogue of books available for sale
catalogue.put(title, this);
super.onStart();
}
public void onTick() {
long currentTime = System.currentTimeMillis();
if (currentTime > deadline) {
// Deadline expired
myGui.notifyUser("Cannot sell book " + title);
catalogue.remove(title);
stop();
} else {
// Compute the current price
long elapsedTime = currentTime - initTime;
currentPrice = (int) Math.round(initPrice - 1.0 * deltaP
* (1.0 * elapsedTime / deltaT));
}
}
public int getCurrentPrice() {
return currentPrice;
}
}
/**
* Section 4.3.3 , page 67. Inner class CallForOfferServer. This is the
* behaviour used by Book-seller agents to serve incoming call for offer
* from buyer agents. If the indicated book is in the local catalogue, the
* seller agent replies with a PROPOSE message specifying the price.
* Otherwise a REFUSE message is sent back.
**/
private class CallForOfferServer extends CyclicBehaviour {
private MessageTemplate mt = MessageTemplate
.MatchPerformative(ACLMessage.CFP);
public void action() {
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
// CFP Message received. Process it
String title = msg.getContent();
myGui.notifyUser("Received Proposal to buy " + title);
ACLMessage reply = msg.createReply();
PriceManager pm = (PriceManager) catalogue.get(title);
if (pm != null) {
// The requested book is available for sale. Reply with the
// price
reply.setPerformative(ACLMessage.PROPOSE);
reply.setContent(String.valueOf(pm.getCurrentPrice()));
} else {
// The requested book is NOT available for sale.
reply.setPerformative(ACLMessage.REFUSE);
}
myAgent.send(reply);
myGui.notifyUser(pm != null ? "Sent Proposal to sell at "
+ reply.getContent()
: "Refused Proposal as the book is not for sale");
} else {
block();
}
}
} // End of inner class CallForOfferServer
/**
* Inner class PurchaseOrdersServer. This is the behaviour used by
* Book-seller agents to serve incoming offer acceptances (i.e. purchase
* orders) from buyer agents. The seller agent removes the purchased book
* from its catalogue and replies with an INFORM message to notify the buyer
* that the purchase has been sucesfully completed.
*/
private class PurchaseOrderServer extends CyclicBehaviour {
private MessageTemplate mt = MessageTemplate
.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL);
public void action() {
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
// ACCEPT_PROPOSAL Message received. Process it
String title = msg.getContent();
myGui.notifyUser("Received ACCEPT_PROPOSAL to purchase "
+ title);
ACLMessage reply = msg.createReply();
PriceManager pm = (PriceManager) catalogue.get(title);
if (pm != null) {
// removes the purchased book from its catalogue
catalogue.remove(title);
reply.setPerformative(ACLMessage.INFORM);
pm.stop();
} else {
// The requested book is NOT available for sale.
reply.setPerformative(ACLMessage.FAILURE);
}
myAgent.send(reply);
myGui.notifyUser(pm != null ? "The book " + title
+ " has been sold at price = " + pm.getCurrentPrice()
: "The book is not exist.");
} else {
block();
}
}
} // End of inner class PurchaseOrdersServer
}
package com.leejay.booktrading.buyer;
public interface BookBuyerGui {
void setAgent(BookBuyerAgent a);
void show();
void hide();
void notifyUser(String message);
void dispose();
}
package com.leejay.booktrading.buyer;
import jade.gui.TimeChooser;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.Date;
/**
J2SE (Swing-based) implementation of the GUI of the agent that
tries to buy books on behalf of its user
*/
public class BookBuyerGuiImpl extends JFrame implements BookBuyerGui {
private BookBuyerAgent myAgent;
private JTextField titleTF, desiredCostTF, maxCostTF, deadlineTF;
private JButton setDeadlineB;
private JButton setCCB, buyB, resetB, exitB;
private JTextArea logTA;
private Date deadline;
public BookBuyerGuiImpl() {
super();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
myAgent.doDelete();
}
} );
JPanel rootPanel = new JPanel();
rootPanel.setLayout(new GridBagLayout());
rootPanel.setMinimumSize(new Dimension(330, 125));
rootPanel.setPreferredSize(new Dimension(330, 125));
///////////
// Line 0
///////////
JLabel l = new JLabel("Book to buy:");
l.setHorizontalAlignment(SwingConstants.LEFT);
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
titleTF = new JTextField(64);
titleTF.setMinimumSize(new Dimension(222, 20));
titleTF.setPreferredSize(new Dimension(222, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(titleTF, gridBagConstraints);
///////////
// Line 1
///////////
/*l = new JLabel("Best cost:");
l.setHorizontalAlignment(SwingConstants.LEFT);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
desiredCostTF = new JTextField(64);
desiredCostTF.setMinimumSize(new Dimension(70, 20));
desiredCostTF.setPreferredSize(new Dimension(70, 20));
desiredCostTF.setEditable(false); //FIXME just for this example
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(desiredCostTF, gridBagConstraints);*/
l = new JLabel("Max cost:");
l.setHorizontalAlignment(SwingConstants.LEFT);
l.setMinimumSize(new Dimension(70, 20));
l.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
maxCostTF = new JTextField(64);
maxCostTF.setMinimumSize(new Dimension(70, 20));
maxCostTF.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(maxCostTF, gridBagConstraints);
///////////
// Line 2
///////////
l = new JLabel("Deadline:");
l.setHorizontalAlignment(SwingConstants.LEFT);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
deadlineTF = new JTextField(64);
deadlineTF.setMinimumSize(new Dimension(146, 20));
deadlineTF.setPreferredSize(new Dimension(146, 20));
deadlineTF.setEnabled(false);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(deadlineTF, gridBagConstraints);
setDeadlineB = new JButton("Set");
setDeadlineB.setMinimumSize(new Dimension(70, 20));
setDeadlineB.setPreferredSize(new Dimension(70, 20));
setDeadlineB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
Date d = deadline;
if (d == null) {
d = new Date();
}
TimeChooser tc = new TimeChooser(d);
if (tc.showEditTimeDlg(BookBuyerGuiImpl.this) == TimeChooser.OK) {
deadline = tc.getDate();
deadlineTF.setText(deadline.toString());
}
}
} );
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(setDeadlineB, gridBagConstraints);
/*setCCB = new JButton("Set CreditCard");
setCCB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String cc = JOptionPane.showInputDialog(BookBuyerGuiImpl.this, "Insert the Credit Card number");
if (cc != null && cc.length() > 0) {
myAgent.setCreditCard(cc);
}
else {
JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "Invalid Credit Card number", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
} );
//setCCB.setMinimumSize(new Dimension(70, 20));
//setCCB.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(setCCB, gridBagConstraints);*/
rootPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
getContentPane().add(rootPanel, BorderLayout.NORTH);
logTA = new JTextArea();
logTA.setEnabled(false);
JScrollPane jsp = new JScrollPane(logTA);
jsp.setMinimumSize(new Dimension(400, 200));
jsp.setPreferredSize(new Dimension(400, 200));
JPanel p = new JPanel();
p.setBorder(new BevelBorder(BevelBorder.LOWERED));
p.add(jsp);
getContentPane().add(p, BorderLayout.CENTER);
p = new JPanel();
buyB = new JButton("Buy");
buyB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String title = titleTF.getText();
int desiredCost = -1;
int maxCost = -1;
if (title != null && title.length() > 0) {
if (deadline != null && deadline.getTime() > System.currentTimeMillis()) {
try {
//desiredCost = Integer.parseInt(desiredCostTF.getText());
try {
maxCost = Integer.parseInt(maxCostTF.getText());
// if (maxCost >= desiredCost) {
// myAgent.purchase(title, desiredCost, maxCost, deadline.getTime());
myAgent.purchase(title, maxCost, deadline);
notifyUser("PUT FOR BUY: "+title+" at max "+maxCost+" by "+deadline);
//}
//else {
// Max cost < desiredCost
//JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "Max cost must be greater than best cost", "WARNING", JOptionPane.WARNING_MESSAGE);
//}
}
catch (Exception ex1) {
// Invalid max cost
JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "Invalid max cost", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
catch (Exception ex2) {
// Invalid desired cost
JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "Invalid best cost", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
else {
// No deadline specified
JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "Invalid deadline", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
else {
// No book title specified
JOptionPane.showMessageDialog(BookBuyerGuiImpl.this, "No book title specified", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
} );
resetB = new JButton("Reset");
resetB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
titleTF.setText("");
desiredCostTF.setText("");
maxCostTF.setText("");
deadlineTF.setText("");
deadline = null;
}
} );
exitB = new JButton("Exit");
exitB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
myAgent.doDelete();
}
} );
buyB.setPreferredSize(resetB.getPreferredSize());
exitB.setPreferredSize(resetB.getPreferredSize());
p.add(buyB);
p.add(resetB);
p.add(exitB);
p.setBorder(new BevelBorder(BevelBorder.LOWERED));
getContentPane().add(p, BorderLayout.SOUTH);
pack();
setResizable(false);
}
public void setAgent(BookBuyerAgent a) {
myAgent = a;
setTitle(myAgent.getName());
}
public void notifyUser(String message) {
logTA.append(message+"\n");
}
}
package com.leejay.booktrading.seller;
public interface BookSellerGui {
void setAgent(BookSellerAgent a);
void show();
void hide();
void notifyUser(String message);
void dispose();
}
package com.leejay.booktrading.seller;
import jade.gui.TimeChooser;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.Date;
/**
This is the GUI of the agent that tries to sell books on behalf of its user
*/
public class BookSellerGuiImpl extends JFrame implements BookSellerGui {
private BookSellerAgent myAgent;
private JTextField titleTF, desiredPriceTF, minPriceTF, deadlineTF;
private JButton setDeadlineB;
private JButton setCCB, sellB, resetB, exitB;
private JTextArea logTA;
private Date deadline;
public void setAgent(BookSellerAgent a) {
myAgent = a;
setTitle(myAgent.getName());
}
public BookSellerGuiImpl() {
super();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
myAgent.doDelete();
}
} );
JPanel rootPanel = new JPanel();
rootPanel.setLayout(new GridBagLayout());
rootPanel.setMinimumSize(new Dimension(330, 125));
rootPanel.setPreferredSize(new Dimension(330, 125));
///////////
// Line 0
///////////
JLabel l = new JLabel("Book to sell:");
l.setHorizontalAlignment(SwingConstants.LEFT);
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
titleTF = new JTextField(64);
titleTF.setMinimumSize(new Dimension(222, 20));
titleTF.setPreferredSize(new Dimension(222, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(titleTF, gridBagConstraints);
///////////
// Line 1
///////////
l = new JLabel("Best price:");
l.setHorizontalAlignment(SwingConstants.LEFT);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
desiredPriceTF = new JTextField(64);
desiredPriceTF.setMinimumSize(new Dimension(70, 20));
desiredPriceTF.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(desiredPriceTF, gridBagConstraints);
l = new JLabel("Min price:");
l.setHorizontalAlignment(SwingConstants.LEFT);
l.setMinimumSize(new Dimension(70, 20));
l.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
minPriceTF = new JTextField(64);
minPriceTF.setMinimumSize(new Dimension(70, 20));
minPriceTF.setPreferredSize(new Dimension(70, 20));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(minPriceTF, gridBagConstraints);
///////////
// Line 2
///////////
l = new JLabel("Deadline:");
l.setHorizontalAlignment(SwingConstants.LEFT);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(5, 3, 0, 3);
rootPanel.add(l, gridBagConstraints);
deadlineTF = new JTextField(64);
deadlineTF.setMinimumSize(new Dimension(146, 20));
deadlineTF.setPreferredSize(new Dimension(146, 20));
deadlineTF.setEnabled(false);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(deadlineTF, gridBagConstraints);
setDeadlineB = new JButton("Set");
setDeadlineB.setMinimumSize(new Dimension(70, 20));
setDeadlineB.setPreferredSize(new Dimension(70, 20));
setDeadlineB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
Date d = deadline;
if (d == null) {
d = new Date();
}
TimeChooser tc = new TimeChooser(d);
if (tc.showEditTimeDlg(BookSellerGuiImpl.this) == TimeChooser.OK) {
deadline = tc.getDate();
deadlineTF.setText(deadline.toString());
}
}
} );
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new Insets(5, 3, 0, 3);
rootPanel.add(setDeadlineB, gridBagConstraints);
rootPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
getContentPane().add(rootPanel, BorderLayout.NORTH);
logTA = new JTextArea();
logTA.setEnabled(false);
JScrollPane jsp = new JScrollPane(logTA);
jsp.setMinimumSize(new Dimension(400, 200));
jsp.setPreferredSize(new Dimension(400, 200));
JPanel p = new JPanel();
p.setBorder(new BevelBorder(BevelBorder.LOWERED));
p.add(jsp);
getContentPane().add(p, BorderLayout.CENTER);
p = new JPanel();
sellB = new JButton("Sell");
sellB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String title = titleTF.getText();
int desiredPrice = -1;
int minPrice = -1;
if (title != null && title.length() > 0) {
if (deadline != null && deadline.getTime() > System.currentTimeMillis()) {
try {
desiredPrice = Integer.parseInt(desiredPriceTF.getText());
try {
minPrice = Integer.parseInt(minPriceTF.getText());
if (minPrice <= desiredPrice) {
// myAgent.addToCatalogue(title, desiredPrice, minPrice, deadline.getTime());
myAgent.putForSale(title, desiredPrice, minPrice, deadline);
notifyUser("PUT FOR SALE: "+title+" between "+desiredPrice+" and "+minPrice+" by "+deadline);
}
else {
// minPrice > desiredPrice
JOptionPane.showMessageDialog(BookSellerGuiImpl.this, "Min price must be cheaper than best price", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
catch (Exception ex1) {
// Invalid max cost
JOptionPane.showMessageDialog(BookSellerGuiImpl.this, "Invalid min price", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
catch (Exception ex2) {
// Invalid desired cost
JOptionPane.showMessageDialog(BookSellerGuiImpl.this, "Invalid best price", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
else {
// No deadline specified
JOptionPane.showMessageDialog(BookSellerGuiImpl.this, "Invalid deadline", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
else {
// No book title specified
JOptionPane.showMessageDialog(BookSellerGuiImpl.this, "No book title specified", "WARNING", JOptionPane.WARNING_MESSAGE);
}
}
} );
resetB = new JButton("Reset");
resetB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
titleTF.setText("");
desiredPriceTF.setText("");
minPriceTF.setText("");
deadlineTF.setText("");
deadline = null;
}
} );
exitB = new JButton("Exit");
exitB.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
myAgent.doDelete();
}
} );
sellB.setPreferredSize(resetB.getPreferredSize());
exitB.setPreferredSize(resetB.getPreferredSize());
p.add(sellB);
p.add(resetB);
p.add(exitB);
p.setBorder(new BevelBorder(BevelBorder.LOWERED));
getContentPane().add(p, BorderLayout.SOUTH);
pack();
setResizable(false);
}
public void notifyUser(String message) {
logTA.append(message+"\n");
}
}