JList in UI not reflecting changes from all peer processes - java

I am writing a simple chat application in Java. As soon as a new user joins, the server component of my application should inform all other clients about this event.
The relevant code on the server side is as follows:
private void notifyUser() throws IOException {
String[] user = new String[onlineUsers.size()];
int i = 0;
for(Socket sock : sockets){
out.get(i).writeInt(1);
out.get(i).writeObject(onlineUsers.toArray(user));
out.get(i).flush();
}
}
The client thread is listening on the socket using the following code:
ObjectInputStream in = new ObjectInputStream(sockfd.getInputStream());
while(true) {
command = in.readInt();
switch(command) {
case 1:
String[] user = (String[])in.readObject();
System.out.println(user);
client.addOnlineUsers(user);
}
}
The addOnlineUsers(..) method consists of the following code:
public void addOnlineUsers(String[] user) {
for(String s : user){
listmodel.addElement(s);
}
}
I have instantiated the JList like so:
JList list = new JList(listmodel);
, where listmodel is a public instance.
The problem is, however, that as soon as a new user joins the server, the JList of the 1st (i.e. the oldest) client is updated while the rest of the users don't receive any updates to the JList in their UI.

In the notifyUser method, you are only writing to the output stream of the first user in the array, i.e.
i = 0;
It should be incremented after
out.get(i).flush();

Related

JDA - Event if new user joined to Guild

I have a problem, I am trying to code a bot using Java Discord API (JDA).
When a new user joins a server, the bot shall send a message, but my code is not working.
Code:
public class UserJoinModule extends ListenerAdapter {
public void onGuildMemberJoined(GuildMemberJoinEvent event) throws LoginException {
String user = event.getMember().getAsMention();
JDA client = new JDABuilder("awesome token").build();
final List<TextChannel> channels = client.getTextChannelsByName("awesome channel name", true);
for (final TextChannel ch : channels) {
ch.sendMessage("New member joined: " + user).queue();
}
}
}
Can someone tell me what is wrong?
For me the issue was not from the listener and method I override.
I believe you have to add GatewayIntent.GUILD_MEMBERS to your JDABuilder.
builder.enableIntents(GatewayIntent.GUILD_MEMBERS);
This fixed the same issue for me.
In your Main.java or whatever the file is, there is a variable of type JDABuilder, on it's same line of code, there is your token, a .build() at the end etc...
Insert this code into that line:
.enableIntents(GatewayIntent.GUILD_MEMBERS)
So it looks like this:
jda = JDABuilder.createDefault("TOKEN").enableIntents(GatewayIntent.GUILD_MEMBERS).build();
For it to work, go to your Discord Developer Portal, click your bot, from the menu on the left, click Bot, then scroll down and enable:
Server Members Intent
There are still noticeable errors like registering a new client on every message and other issues, fix them, then start your bot and it shall work.
Your code should look like this:
public class UserJoinModule extends ListenerAdapter {
#Override // USE THIS WHEN YOU WANT TO OVERRIDE A METHOD
public void onGuildMemberJoin(GuildMemberJoinEvent event) {
String user = event.getMember().getAsMention();
JDA client = event.getJDA(); // DO NOT CREATE A NEW JDA INSTANCE EVERY TIME
List<TextChannel> channels = client.getTextChannelsByName("awesome channel name", true);
for (TextChannel ch : channels) {
ch.sendMessage("New member joined: " + user).queue();
}
}
}
And you must register this listeners in your JDABuilder instance, preferably you only have one of these in your entire codebase. See addEventListeners.
You have 2 problems in your code.
You are creating a new JDA client every time a member joins.
You are sending messages to every channel with that name, in every guild. Not just the guild that the user joined.
Here is what you want to do:
public class UserJoinModule extends ListenerAdapter {
#Override
public void onGuildMemberJoin(GuildMemberJoinEvent event) {
Guild guild = event.getGuild(); // Get the guild that the user joined.
User user = event.getUser(); // Get the user that joined.
JDA client = event.getJDA(); // Get the already existing JDA instance.
List<TextChannel> channels = guild.getTextChannelsByName("awesome channel name", true); // Get the list of channels in the guild that matches that name.
for (TextChannel channel : channels) { // Loops through the channels and sends a message to each one.
channel.sendMessage("New member joined: " + user).queue();
}
}
}

Waiting until boolean value is changed without blocking main JavaFX thread?

I have been reading for hours about different multi-threading techniques for JavaFX and cannot seem to find what I'm looking for. The application being worked on is the "Messenger" which part of a bigger application to provide a trading marketplace for a game.
A breakdown of the process I am having trouble with:
A window with a 'Contact Seller' button is displayed
The user clicks 'Contact Seller', and the Messenger window should be displayed
Using the seller's name from the main window, the Messenger should check if a chat already exists with that name
If the chat already exists, get the index of that chat in the Messenger's ListView, and select the chat so the Messenger's text area is populated with the corresponding messages
If the chat doesn't exist, create one
The issue:
Chats are stored server-side
Messages between clients are stored in a message 'cache' on the server when being processed from one user to the other (they are sorted to their respective chats, inserted into the chats, and pushed to the database upon client disconnection)
Messages received on the client are stored locally in the respective chat
When the Messenger is opened, it requests a list of chats for the signed-in user
The server sends an ArrayList of chats, and upon receiving the client builds the Messenger's ListView with these objects
Now when I need to open the Messenger in order to 'Contact Seller', I need to make sure the sync with the server is complete. Without doing so, I won't be able to properly check if a chat already exists with that name since I won't have the most up-to-date list.
The 'RequestWorker' thread that handles incoming server messages is NOT on the JavaFX thread.
The RequestWorker 'gets' the Messenger instance if it is currently open, and populates the ListView with the newly received chat list. (This needs to happen on the JavaFX thread, since I am working in the Messenger GUI)
What I am trying to do is set a static AtomicBoolean syncInProgress to true when the Messenger initiates a sync upon being displayed. When the RequestWorker receives the latest list back from the server and finishes populating the Messengers ListView, it sets syncInProgress to false.
The sync takes longer than opening the Messenger and making it check if a chat exists. Doing it this way it has no items populated yet in the ListView and the method is ineffective.
Calling a while loop to wait until the boolean is changed, blocks the JavaFX thread which means the RequestWorker cannot do what it needs to in the JavaFX thread.
How can I continuously check for this variable to be set false, then continue to 'Contact Seller' once the ListView has been properly populated?
Contact Seller method: The while loop in here causes a block on the JavaFX thread, thus not enabling RequestWorker to properly populate the ListView.
public static void contactSeller(Messenger messenger, String destination, String itemName)
{
while (TarkovTrader.syncInProgress.get())
{
; // Wait until sync is complete to check the latest chat list for an existing chat
}
if (messenger.chatExists(destination))
{
// Chat exists, select the chat for the user
for (Chat openChat : messenger.chatListView.getItems())
{
if (openChat.getName(TarkovTrader.username).equals(destination))
{
messenger.chatListView.getSelectionModel().select(openChat);
messenger.unpackChatMessages(openChat.getMessages());
break;
}
}
}
else
{
messenger.buildNewChat(destination);
}
messenger.chatInput.setText("Hey " + destination + ". Interested in your '" + itemName + "'.");
messenger.chatInput.setOnMouseClicked(e -> messenger.chatInput.clear());
}
RequestWorker process chat block:
switch(receivedFromServer)
case "chatlist":
// Client requested a chat list, results were returned from the server, and now we need to populate the messenger list
ChatListForm chatlistform = (ChatListForm)processedRequest;
if (Messenger.isOpen)
{
FutureTask<Void> updateChatList = new FutureTask(() -> {
Messenger tempMessenger = trader.getMessenger();
int currentIndex = tempMessenger.chatListView.getSelectionModel().getSelectedIndex();
tempMessenger.populate(chatlistform.getChatList());
tempMessenger.chatListView.getSelectionModel().select(currentIndex);
}, null);
Platform.runLater(updateChatList); // RequestWorker needs access to the JavaFX application thread
try {
updateChatList.get(); // Wait until the ListView has been populated before setting 'syncInProgress' to false again
}
catch (InterruptedException e) {
Alert.display(null, "Sync interrupted.");
}
catch (ExecutionException e) {
Alert.display(null, "Sync failed.");
}
TarkovTrader.syncInProgress.compareAndSet(true, false); // The value of syncInProgress should be true, change to false. Sync complete
}
else
{
Platform.runLater(() -> Alert.display(null, "New chat received."));
TarkovTrader.syncInProgress.compareAndSet(true, false);
}
break;
Contact Seller button logic: If a messenger is not open, create it and pass to static contactSeller method to use.
contactButton.setOnAction(e -> {
Messenger messenger;
if (Messenger.isOpen)
{
// Get messenger
messenger = trader.getMessenger();
}
else
{
messenger = new Messenger(worker);
messenger.display();
trader.setMessenger(messenger);
}
Messenger.contactSeller(messenger, item.getUsername(), item.getName());
itemdisplay.close();
});
EDIT:
Partially using Slaw's idea (AtomicBoolean is still being used since I'm not sure how to do this without it), this is what I came up with...
public static void contactSeller(Messenger messenger, String destination, String itemName)
{
Task<Void> waitForSync = new Task<Void>() {
#Override
public Void call()
{
while (TarkovTrader.syncInProgress.get())
{
; // Wait until sync is complete
}
return null;
}
};
waitForSync.setOnSucceeded(e -> {
while (TarkovTrader.syncInProgress.get())
{
; // Wait until sync is complete to check the latest chat list for an existing chat
}
if (messenger.chatExists(destination))
{
// Chat exists, select the chat for the user
for (Chat openChat : messenger.chatListView.getItems())
{
if (openChat.getName(TarkovTrader.username).equals(destination))
{
messenger.chatListView.getSelectionModel().select(openChat);
messenger.unpackChatMessages(openChat.getMessages());
break;
}
}
}
else
{
messenger.buildNewChat(destination);
}
messenger.chatInput.setText("Hey " + destination + ". Interested in your '" + itemName + "'.");
messenger.chatInput.setOnMouseClicked(me -> messenger.chatInput.clear());
});
Thread t = new Thread(waitForSync);
t.setDaemon(true);
t.start();
}
Which does work, but this doesn't seem like a great solution. Is it fine to do something like this or is there a preferred method over this? I feel like the while loop and using a triggered boolean is sloppy..but is this common practice?
Don't try to handle the opened chat in the same method that schedules the chat retrieval. Instead create a queue of handlers that is executed as soon as the chat is ready.
Simplified Example
public class ChatManager {
private final Map<String, Chat> openChats = new HashMap<>();
// only call from application thread
public void openChat(String user, Consumer<Chat> chatReadyHandler) {
Chat chat = openChats.computeIfAbsent(user, this::createNewChat);
chat.addReadyHandler(chatReadyHandler);
}
private Chat createNewChat(String user) {
return new Chat(user);
}
public class Chat {
// list keeping track of handlers any used for synchronisation
private final ArrayList<Consumer<Chat>> readyHandlers = new ArrayList<>(1);
private boolean ready = false;
private final String user;
public String getUser() {
return user;
}
private void addReadyHandler(Consumer<Chat> chatReadyHandler) {
synchronized (readyHandlers) {
// if already ready, immediately execute, otherwise enqueue
if (ready) {
chatReadyHandler.accept(this);
} else {
readyHandlers.add(chatReadyHandler);
}
}
}
private void chatReady() {
synchronized (readyHandlers) {
ready = true;
}
// execute all handlers on the application thread
Platform.runLater(() -> {
synchronized (readyHandlers) {
for (Consumer<Chat> readyHandler : readyHandlers) {
readyHandler.accept(this);
}
readyHandlers.clear();
readyHandlers.trimToSize();
}
});
}
private Chat(String user) {
this.user = user;
new Thread(() -> {
try {
Thread.sleep(10000); // simulate time required to acquire chat
} catch (InterruptedException ex) {
}
chatReady();
}).start();
}
}
}
The following code creates a Chat with a user when Enter is pressed after typing the user name in the TextField and prints a message to the TextArea when the chat is ready.
#Override
public void start(Stage primaryStage) throws Exception {
ChatManager worker = new ChatManager();
TextField userName = new TextField();
TextArea textArea = new TextArea();
textArea.setEditable(false);
userName.setOnAction(evt -> {
String user = userName.getText();
userName.clear();
textArea.appendText("opening chat for " + user + "\n");
worker.openChat(user, chat -> textArea.appendText("chat for " + chat.getUser() + " ready\n"));
});
Scene scene = new Scene(new VBox(10, userName, textArea));
primaryStage.setScene(scene);
primaryStage.show();
}

Dynamical update a textfield on live events

i am at the moment developing a Softphone with javafx. and i kind of a have problem capturing incoming call to a textfield. an example of my code is here.
an incoming call is with Joptionpane successful bt i had like to have the value appear in call textfield just like telephone.
Thank you.
public void telephoneNumbs(String numbers) {
String replace = numbers.replace("sip:", "").trim().replace(".", ""); // Incoming Call Numbers from Sip UA
if (!replace.isEmpty()) {
List<TelephoneObj> telephons;
telTextField.setText(null); //init it with null
costumDao = new CostumersDao(); // costumers DB
telephons = costumDao.getOrCompareTelfone(numbers);
for (TelephoneObj tmp : telephons) {
System.out.println("Test: " + tmp.getTelephoneNums); // am getting exactle what i need here from my Database
//or
JOptionPane.showMessageDialog(null,"incoming:"+ tmp.getTelephoneNums); // it show it during incoming calls
//here is the problem. it wouldnt show the Value on the Textfield
telTextField.setText(tmp.getTelephoneNums); //try to push that Value(Telephone number) to show in JFXTextfield/it cold be any other Textfields
}
}
Sooo much happy today it went well with after 2days of thinking how to solve this miserable life of not taking time to think.
I finally got the answer by using Task to solve the problem.
Task<Void> task = new Task<Void>() {
{
updateMessage("");
}
#Override
public Void call() throws Exception {
while (true) {
updateMessage(callee);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
break;
}
}
return null;
}
};
//neuLabel.textProperty().bind(task.messageProperty());
kdAddrTel.textProperty().bind(task.messageProperty());
Thread th = new Thread(task);
th.setDaemon(true);
th.start();

JADE ContractNet and GUI problems

I have some problems with use of ContractNet (Interaction Protocol) and GUI with the use of JADE multiagent framework.
In particular, in the override of handlePropose method.
I know that my problem comes from the use of a GUI. Let me explain:
My agent (Initiator) uses a first GUI and, after a click, the
conversation begins with a second agent (Responder). According to the
Protocol, the Initiator has thus sent a CFP to Responder. The agent
Responder responds with a PROPOSE that contains different data.
Since here, everything ok. Now...
I wish that the agent Initiator, BEFORE returning a reply, may examine
the data ... ie publish them on a JTable, for the user! The user will
examine the proposal via GUI and will choose if to accept or not, by
click on a button.
If accept, the Initiator send ACCEPT_PROPOSAL.
If not accept, the Initiator send REJECT_PROPOSAL.
This should be done in the method handleProposal. This is my code:
#Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": receive PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
// the problem:
gcb.getExecuteJButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agent "+getLocalName()+": send ACCEPT PROPOSAL ");
acceptances.addElement(reply);
}
});
// similar case, but for REJECT:
// gcb.getAbortJButton().addActionListener(... bla bla
gcb.setVisible(true);
} catch (UnreadableException e){
e.printStackTrace();
}
}
..... But, obviously, does not work.
In the Initiator agent, the ContractNet behaviour is aborted... so also handleInform, handleRefuse and handleFailure (for handle the answers) do not work.
The Initiator's principal GUI is blocked. And other problems...
Instead, if I do this (WITHOUT JButton, another GUI and ActionListener):
#Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": received PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agente "+getLocalName()+": ACCEPT PROPOSAL di "+propose.getSender().getLocalName());
acceptances.addElement(reply);
} catch (UnreadableException e){
e.printStackTrace();
}
}
.... works.
I know that the problem is the ActionListener and its multithread nature.
But I need the GUI there.
How can I fix?
I try to answer myself. I'm not sure it's the best solution, but certainly works.
Note that before coming to this solution I am well documented with the guides and tutorials found (on http://jade.tilab.com/), and confronting myself with other
JADE developers (in mailing lists http://jade.tilab.com/pipermail/jade-develop/)
The answer is complicated, so I'll try to be exhaustive.
In my project I have to deal with two different types of agents.
The ShipperAgent, which represents one shipper: it keeps track of the vehicles owned by the shipper, those available, and the goods "reserved" from it.
The BuyerAgent, which represents customers (or buyers): each customer has a list of goods that want to move from point A to point B.
The two agents are registered to the yellow pages service.
In ShipperAgent, clicking on the "SEARCH" button you start a search: start a Contract Net Interaction Protocol.
Explain the Contract Net Interaction Protocol and my case
In the standard FIPA: http://www.fipa.org/specs/fipa00029/SC00029H.html
In JADE guide can be found here: http://jade.tilab.com/doc/programmersguide.pdf (p. 35)
Further on you will notice the changes that I had to take.
The ShipperAgent sends CFP each BuyerAgent.
Each BuyerAgent:
2.1 if he has goods, send a PROPOSE to ShipperAgent.
2.2 if does not have the goods, send a REFUSE to ShipperAgent. And for buyer, the protocol ends.
Since here is easy. With the sniffer, we can observe:
Now:
The ShipperAgent:
3.1 receives one or more PROPOSE by buyers, and displays (see image below).
3.2 if it receives the REFUSE (or does not receive anything after a certain time), ending communication with those buyer.
Here's how the Shipper graphically displays the proposals:
Now it's up to the user to choose which goods wants and what not.
To achieve this, I had to create some kind of "internal communication" to the agent himself: the GUI (in 3.1), once clicked Execute, sends a message to the agent. It may seem inelegant, but it seems to be the only way to not crash the protocol side ShipperAgent.
The ShipperAgent:
 4.1 if the user has selected one or more goods proposals (and click Execute), sends to the corresponding BuyerAgent an ACCEPT_PROPOSAL, where specific goods that want to (a subset of the previous proposal).
 4.2 if the user does not select any good (or click on Cancel), sends to the corresponding BuyerAgent an REJECT_PROPOSAL. Ends communication to that buyer.
The BuyerAgent:
 5.1 if receives an ACCEPT_PROPOSAL, check that the goods are still available (any other shippers could have them "reserved" in the meanwhile) and, if so, sends an INFORM.
5.2 if receives an ACCEPT_PROPOSAL ma one or more goods are no longer available, sends FAILURE.
 5.3 if it receives an REJECT_PROPOSAL, ends communication with the ShipperAgent.
In brief this (for example):
The code
BuyerAgent.java
I create a dispatcher who is always ready to receive the CFP. As soon as it receives and start protocol, buyer-side: start SearchJobResponder.
/*
* ...
*/
final MessageTemplate template = MessageTemplate.and(
MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET),
MessageTemplate.MatchPerformative(ACLMessage.CFP) );
// SSResponderDispatcher:
SSResponderDispatcher dispatcher = new SSResponderDispatcher(this, template) {
BuyerAgent b = (BuyerAgent) this.myAgent;
protected Behaviour createResponder(ACLMessage initiationMsg) {
// SearchJobResponder for single cfp:
return new SearchJobResponder(b, initiationMsg);
}
};
addBehaviour(dispatcher);
/*
* ...
*/
ShipperAgent.java
Search all buyer, creates a CFP and start the protocol, shipper-side: start SearchJobInitiator.
/*
* ...
*/
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
AID[] buyerAgents = searchBuyers(); // search all buyerAgents
for (AID buyer : buyerAgents)
cfp.addReceiver(buyer);
addBehaviour(new SearchJobInitiator(this, cfp));
/*
* ...
*/
SearchJobInitiator.java
This was the hard part...
/*
* ...
*/
public class SearchJobInitiator extends ContractNetInitiator {
ShipperAgent shipperAgent;
public SearchJobInitiator(ShipperAgent a, ACLMessage cfp) {
super(a, cfp);
shipperAgent=a;
// Very important:
registerHandleAllResponses(new HandleProposes());
}
#Override
protected Vector<?> prepareCfps(ACLMessage cfp) {
long now = System.currentTimeMillis();
cfp.setConversationId("contractNet-by-"
+shipperAgent.getAID().getLocalName()+now);
cfp.setContent("Fammi delle proposte di lavoro");
/*
* filtering...
*/
cfp.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
cfp.setReplyByDate(new Date(now+10000));
//cfp.setReplyWith("cfp"+System.currentTimeMillis()) //useless, is overwrited at the end
return super.prepareCfps(cfp);
}
//inner class for handling a single proposal
public class HandleProposes extends Behaviour {
private static final long serialVersionUID = 1L;
private Vector<ACLMessage> proposes;
private Vector<ACLMessage> acceptances;
private int numberOfProposes;
public void onStart() {
proposes = (Vector<ACLMessage>) getDataStore().get(ALL_RESPONSES_KEY);
acceptances = (Vector<ACLMessage>) getDataStore().get(ALL_ACCEPTANCES_KEY);
numberOfProposes=proposes.size();
for (Iterator I=proposes.iterator(); I.hasNext();) {
ACLMessage propose = (ACLMessage) I.next();
// Very important:
if (propose.getPerformative()==ACLMessage.PROPOSE)
myAgent.addBehaviour(new HandleSinglePropose(propose, acceptances));
else
numberOfProposes--;
}
}
public void action() {
if (!done())
block();
}
public boolean done() {
return (acceptances.size()==numberOfProposes);
}
/*
* Inner class for handle a single proposal and display it:
*/
public class HandleSinglePropose extends Behaviour {
private ACLMessage propose;
private Vector<ACLMessage> acceptances;
private boolean finish=false;
public HandleSinglePropose (ACLMessage propose, Vector<ACLMessage> acceptances) {
this.propose=propose;
this.acceptances=acceptances;
// This is GUI in 3.1 point
GoodsChoiceBox gcb = new GoodsChoiceBox(shipperAgent, this, propose); // fill the JTable
gcb.setVisible(true);
}
#Override
public void action() {
MessageTemplate mt = MessageTemplate.and(
MessageTemplate.MatchSender(shipperAgent.getAID()),
MessageTemplate.and(
MessageTemplate.MatchReplyWith("response"+propose.getReplyWith()),
MessageTemplate.or(
MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL),
MessageTemplate.MatchPerformative(ACLMessage.REJECT_PROPOSAL)
) ) ) ;
// Read data from GUI. The user accept or reject:
ACLMessage decisionFromGUI = shipperAgent.receive(mt);
if (decisionFromGUI != null) {
ACLMessage reply = propose.createReply();
// bla bla...
finish=true;
HandleProposes.this.restart();
} else {
block();
}
}
public boolean done() {
return finish;
}
public void handleChoice(ACLMessage propose, boolean bool, Vector<Goods> selectedGoods) {
ACLMessage reply;
if (bool){
reply = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
//...
} else {
reply = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
//...
}
reply.addReceiver(shipperAgent.getAID());
reply.setReplyWith("response"+propose.getReplyWith());
shipperAgent.send(reply);
}
} // closes HandleSinglePropose
} // closes HandleProposes
}
SearchJobResponder.java
The responder is simple. The only thing of note: I extends SSContractNetResponder, don't extends ContractNetResponder.
public class SearchJobResponder extends SSContractNetResponder {
BuyerAgent buyerAgent;
public SearchJobResponder(BuyerAgent a, ACLMessage cfp) {
super(a, cfp);
buyerAgent = a;
}
/*
* override methods...
*/
}
GoodsChoiceBox.java
The GUI for show the proposals...
public GoodsChoiceBox(final Agent agent, final HandleSinglePropose behaviour, final ACLMessage propose){
/*
* graphics stuff
*/
// if goods selected and press Execute
behaviour.handleChoice(propose,true,selectedGoods);
//else
behaviour.handleChoice(propose,false,null);
/*
* bla bla
*/
}
I know, I have dwelt much, but I did not know how else to explain.
However, now my project work. But I'm open to any suggestions.
I often run into this problems of this sort. These are Finite State machine behaviours so you should be able to pause and resume a behavior but I'm not sure how. What I do is create two separate Interaction behaviors on the initiator side and one on the responder side.
Initiator Responder
| |
| |
| First behaviour |The responder only has 1 behaviour
|| CFP-> ||
|| <-Proposal ||
| ||
| Second behaviour ||
|| Accept prop-> ||
|| <-Response ||
| |
Two points to remember
(1)
Make sure that you save the conversationID
msgRecieved.getConversationID
from the first behaviour and use it in the second Behaviour.
msg.setConversationID().
(2)
The second behavior is another Contract net initiator but in prepareCFPs method set MESSAGE performative to accept proposal
class ContractServiceList extends ContractNetInitiator
{
protected Vector prepareCfps(ACLMessage cfp) {
ACLmessage AP= new ACLmessage(ACLmessage.ACCEPT_PROPOSAL)
.....
These things are hard to explain so I tried to attached a picture but have 2 little rep points.
I now have enough rep points to attach the picture which I am doing.
I just realized that there is another solution to this problem. The second solution involves using ChildBehaviours and data stores. A child behaviour can be initiated pausing the parent behaviour. The parent behaviour must then be resumed one the child is complete.
I'm attaching a pic to better explain this interaction.
So at point A in your CNI (ContractNetInitiator) parent behaviour you want to initiate the Child behaviour. You would do this by using the CNI.registerHandlePropose(new Childbehaviour).
This is what the setup() method should look like:
protected void setup()
{
ContractNetInitiator parentBehave= new ContractNetInitiator (null, null, GlobDataStore);
ContractNetInitiator.registerHandlePropose(new ChildBehavoiur (GlobDataStore));
addBehaviour(CNI);
}
In you Child behaviour you will have to check the data from the parent (GlobDataStore) and return a message to be passed back. Code to follow:
class ChildBehaviour extends OneShotBehaviour{
#Override
public void action() {
//evaluate globalestore here;
ACLMessage CNIresponse=new ACLMessage();
if(true)
{
storeNotification(ACLMessage.ACCEPT_PROPOSAL, CNIresponse);
}
else
{
storeNotification(ACLMessage.REJECT_PROPOSAL, CNIresponse);
}
}
public void storeNotification(int performative, ACLMessage original)
{
// Retrieve the incoming request from the DataStore
String incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
ACLMessage incomingCFP = (ACLMessage) getDataStore().get(incomingCFPkey);
// Prepare the notification to the request originator and store it in the DataStore
ACLMessage notification = incomingCFP.createReply();
notification.setPerformative(performative);
notification.setContent(original.getContent());
String notificationkey = (String) ((ContractNetResponder) parent).PROPOSE_KEY;
getDataStore().put(notificationkey, notification);
}
}

Synchronizing HttpURLConnection and KeyListener:

So, I'm trying to write code for a search JTextField that retrieves results from the web (kinda like Google's fancy search tips). The only problem is... probably my lack of programming experience. More specifically, I have it set up to whenever a user hits a key, it sends out a request with the JTextField input through an HttpURLConnection and retrieves the results then updates. But whenever the HttpURLConnection is running, it freezes the program for a couple seconds while it does its thing, which mainly prohibits entering in more characters into the search field if I didn't mind anything else. I've considered using separate threads, but I'm not sure how I would properly interrupt a thread and have it start over using new data while having everything else wait on it but still accepting new data. I've considered using
thread.stop();
to help manage the issue, but from what I've read, that seems taboo (am I right?). Anyways, here's pretty much what I'm using:
public class SearchField extends JTextField implements KeyListener {
public SearchField() {
addkeyListener(this);
}
public void updateData(ArrayList<String results) {
/*Pass the data off the the GUI*/
{
#Override
public void keyTyped(KeyEvent e) {
SearchQuery query = new SearchQuery(this.getText());
updateData(query.getResults());
}
}
public class SearchQuery {
ArrayList<String> results = new ArrayList<String>();
public SearchQuery(String search) {
String search_path = "http://www.whatever.com/" + search;
URL url = new URL(search_path);
conn = (HttpURLConnection) url.openConnection();
conn.addRequestProperty("User-Agent", "Mozilla/4.76");
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuffer buf = new StringBuffer();
int i;
while((i = in.read()) != -1) {
buf.append((char) i);
}
String data = buf.toString();
parse(data);
}
public void parse(String data) {
/*Parse a bunch of JSON, return the results in an array*/
results.add(data);
}
public ArrayList<String> getResults() {
return results;
}
}
I'm just at a loss for how to synchronise recieving continuous input while getting new results for each key stroke and dismissing unfinished connections and their respective data processing when it becomes invalid at the next key stroke.
You are doing the download in the same thread that responds to user events. You need to do the download in another thread, perhaps by making SearchQuery extend Thread and doing the query in the run method. When it comes time to update the text field, you can use SwingUtilities.invokeLater to update the text field. Here’s an example:
public class SearchQuery extends Thread {
private SearchField f;
public SearchQuery(JTextField f) {
this.f = f;
}
public void run() {
// do the query here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.updateData(results);
}
});
}
}
Then change the line:
SearchQuery query = new SearchQuery(this.getText());
to this:
SearchQuery query = new SearchQuery(this);

Categories

Resources