This program uses four classes: Student, Teacher, Agent, and AgentFactory. These classes and their methods are described in the next sections. Details of the Student ClassThe Student class represents the student in the teacher-student relationship in this program. This class creates the JXTA environment for the student. First it connects with the teacher, and accepts the questions sent by the teacher. Then it sends answers to the teacher. The Student class implements the Runnable interface and employs the following methods:
Listing 22.1 presents the code for Student.java. Listing 22.1 Student.javapackage client; /** * This is the client in the Distance Learning example. It represents the * student in the teacher-student relationship. */ import server.*; import net.jxta.id.IDFactory; import net.jxta.pipe.InputPipe; import net.jxta.pipe.PipeMsgEvent; import net.jxta.pipe.PipeMsgListener; import net.jxta.credential.AuthenticationCredential; import net.jxta.document.StructuredDocument; import net.jxta.document.Document; import net.jxta.document.StructuredTextDocument; import net.jxta.document.MimeMediaType; import net.jxta.membership.Authenticator; import net.jxta.membership.MembershipService; import net.jxta.peergroup.PeerGroup; import net.jxta.peergroup.PeerGroupFactory; import net.jxta.exception.PeerGroupException; import net.jxta.exception.ProtocolNotSupportedException; import net.jxta.document.AdvertisementFactory; import net.jxta.document.MimeMediaType; import net.jxta.discovery.DiscoveryService; import net.jxta.discovery.DiscoveryListener; import net.jxta.discovery.DiscoveryEvent; import net.jxta.pipe.PipeService; import net.jxta.pipe.OutputPipe; import net.jxta.pipe.OutputPipeListener; import net.jxta.pipe.OutputPipeEvent; import net.jxta.protocol.DiscoveryResponseMsg; import net.jxta.protocol.PipeAdvertisement; import net.jxta.protocol.PeerGroupAdvertisement; import net.jxta.protocol.ModuleImplAdvertisement; import net.jxta.protocol.PeerAdvertisement; import net.jxta.protocol.ResolverQueryMsg; import net.jxta.protocol.ResolverResponseMsg; import net.jxta.resolver.ResolverService; import net.jxta.resolver.QueryHandler; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.document.Advertisement; import net.jxta.id.ID; import net.jxta.impl.protocol.ResolverResponse; import net.jxta.impl.protocol.ResolverQuery; import java.util.*; import java.io.*; public class Student implements Runnable { private static final int MAX_TRIES = 5; private PeerGroup netPeerGroup; private DiscoveryService discoverySvc; private PipeAdvertisement pipeTeacherAdv; private OutputPipe outputTeacherPipe; private PipeAdvertisement pipeStudentAdv; private InputPipe inputStudentPipe; /** * Find an advertisement of a Teacher using * JXTA discovery services. */ private void discoverTeacherPipeAdv() throws IOException { String name = Teacher.DISTANCE_LEARNING + Teacher.TEACHER_PIPE; this.discoverySvc.getRemoteAdvertisements(null, DiscoveryService.ADV, "name", name, 10, null); try { Thread.sleep(5); System.out.print("."); } catch (Exception ignore) { } Enumeration enum = this.discoverySvc.getLocalAdvertisements(DiscoveryService.ADV, "Name", name); while (enum.hasMoreElements()) { pipeTeacherAdv = (PipeAdvertisement) enum.nextElement(); break; } } /** * Set up the output pipe to the teacher. */ private boolean createTeacherPipes() throws IOException { PipeService pipeSvc = netPeerGroup.getPipeService(); this.outputTeacherPipe = pipeSvc.createOutputPipe(this.pipeTeacherAdv, 1000); return true; } /** * Publish the student advertisement pipes. */ private void publishStudentPipeAdv() throws IOException { PipeAdvertisement pipeAdv = null; String advType = PipeAdvertisement.getAdvertisementType(); pipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(advType); pipeAdv.setPipeID(IDFactory.newPipeID(netPeerGroup.getPeerGroupID())); String name = "DL:" + netPeerGroup.getPeerName(); pipeAdv.setName(name); pipeAdv.setType(PipeService.UnicastType); discoverySvc.publish(pipeAdv, DiscoveryService.ADV); discoverySvc.remotePublish(pipeAdv, DiscoveryService.ADV); this.pipeStudentAdv = pipeAdv; } /** * Create an input pipe for inbound messages. */ private void createStudentInputPipe() throws IOException { PipeService pipeSvc = netPeerGroup.getPipeService(); this.inputStudentPipe = pipeSvc.createInputPipe(this.pipeStudentAdv); } /** * Wait indefinitely for the teacher to respond * to the message sent. */ public Message accept() throws InterruptedException { System.out.println("Waiting for the teacher to respond."); return inputStudentPipe.waitForMessage(); } /** * Send to the Teacher a request for a new series of * questions and answers. */ public void requestNewSession() throws IOException { Map map = new HashMap(); map.put("tag", "NEW"); map.put("course", "whatevers"); this.sendToTeacher(map); } /** * Get the Student's response from IO. */ private String getUserResponse() throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); return reader.readLine(); } /** * Utility method to send a response back to the Teacher. * * @param map Key/value pair to be sent. */ private void sendToTeacher(Map map) throws IOException { PipeService pipeSvc = netPeerGroup.getPipeService(); Message message = pipeSvc.createMessage(); Set keySet = map.keySet(); Iterator iterator = keySet.iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); String value = (String) map.get(key); MessageElement element = message.newMessageElement (key, new MimeMediaType( "text/plain"), new ByteArrayInputStream (value.getBytes())); message.addElement(element); } MessageElement clientElement = message.newMessageElement ("client", new MimeMediaType("text/plain"), new ByteArrayInputStream(netPeerGroup.getPeerName().getBytes( ))); message.addElement(clientElement); this.outputTeacherPipe.send(message); } /** * Processes incoming messages from the teacher object. * Handles questions and the * END signal which signifies the end of the question answer session. */ public void processMessage(Message message) throws IOException { String tag = getElementValue(message, "tag"); System.out.println("Received a message from the teacher."); System.out.println("tag=" + tag); if (tag.equals("QUESTION")) { String question = getElementValue(message, "question"); String number = getElementValue(message, "number"); String total = getElementValue(message, "total"); System.out.println("Question [" + number + "/" + total + "]"); System.out.println(question); System.out.print("Your answer: "); String reply = getUserResponse(); // Send back the response to the teacher Map map = new HashMap(); map.put("tag", "ANSWER"); map.put("ANSWER", reply); sendToTeacher(map); } else if (tag.equals("END")) { // The teacher signals the end of the questions // Print out the suggestions String suggestions = getElementValue(message, "SUGGESTIONS"); String response = getElementValue(message, "question"); System.out.println(response); System.out.println(suggestions); this.disconnect(); } } /** * Parses a Message for a specified tag. * * @param message Contains a message from the Teacher. * @param key The key to parse. * * @returns The value for the key if found. Null otherwise. */ private String getElementValue(Message message, String key) { if (!message.hasElement(key)) { return null; } MessageElement element = message.getElement(key); InputStream stream = element.getStream(); try { byte [] bytes = new byte[stream.available()]; stream.read(bytes); return new String(bytes); } catch (IOException io) { io.printStackTrace(); return null; } } /** * Start up the Net Peer group & get the Discovery Service for that group. */ public void startJxta() throws PeerGroupException, IOException { this.netPeerGroup = PeerGroupFactory.newNetPeerGroup(); this.discoverySvc = netPeerGroup.getDiscoveryService(); // Flush all local cache information. We want to start from scratch. discoverySvc.flushAdvertisements(null, DiscoveryService.ADV); discoverySvc.flushAdvertisements(null, DiscoveryService.PEER); discoverySvc.flushAdvertisements(null, DiscoveryService.GROUP); } /** * Establish communications with a Teacher instance through JXTA. */ public boolean connect() throws Exception { for (int i = 0 ; this.pipeTeacherAdv == null && i < MAX_TRIES ; i++) { this.discoverTeacherPipeAdv(); try { Thread.sleep(5*1000); } catch (Exception ignore) { } } if (this.pipeTeacherAdv == null) { return false; } try { this.createTeacherPipes(); this.publishStudentPipeAdv(); this.createStudentInputPipe(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * Exit out of the application. */ public void disconnect() { System.out.println("bye bye"); System.exit(0); } /** * Thread to capture the student's responses and * process the teacher's messages. */ public void run() { try { requestNewSession(); for (;;) { try { Message message = this.accept(); processMessage(message); } catch (InterruptedException ie) { } } } catch (IOException io) { io.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * Instantiates a Student and launches a thread to allow * the student to input answers to * questions sent by the Teacher. */ public static void main(String argc[]) throws Exception { Student student = new Student(); student.startJxta(); if (!student.connect()) { System.out.println ("Unable to communicate with the teacher."); System.out.println ("Make sure the Teacher is ready to teach."); System.exit(1); } Thread thread = new Thread(student); thread.start(); } } Details of the Teacher ClassThe Teacher class represents the teacher in the teacher-student relationship in this program. The Teacher class creates the JXTA environment and establishes a communication with a student. It employs the following methods: netPeerGroup(), discoverySvc(), pipeAdv(), inputPipe(), agentFactory(), sendMessage(), publishPipeAdv(), createInputPipe(), startJXTA(), and main(). The InputPipeMsgListener class in the main() method implements the PipeMsgListener interface to get the pipe message. The InputPipeMsgeListener class employs the following methods:
Listing 22.2 presents the code for Teacher.java. Listing 22.2 Teacher.javapackage server; import java.util.*; import java.io.*; import net.jxta.id.IDFactory; import net.jxta.pipe.InputPipe; import net.jxta.pipe.PipeMsgEvent; import net.jxta.pipe.PipeMsgListener; import net.jxta.credential.AuthenticationCredential; import net.jxta.document.StructuredDocument; import net.jxta.document.Document; import net.jxta.document.StructuredTextDocument; import net.jxta.document.MimeMediaType; import net.jxta.membership.Authenticator; import net.jxta.membership.MembershipService; import net.jxta.peergroup.PeerGroup; import net.jxta.peergroup.PeerGroupFactory; import net.jxta.exception.PeerGroupException; import net.jxta.exception.ProtocolNotSupportedException; import net.jxta.document.AdvertisementFactory; import net.jxta.document.MimeMediaType; import net.jxta.discovery.DiscoveryService; import net.jxta.discovery.DiscoveryListener; import net.jxta.discovery.DiscoveryEvent; import net.jxta.pipe.PipeService; import net.jxta.pipe.OutputPipe; import net.jxta.pipe.OutputPipeListener; import net.jxta.pipe.OutputPipeEvent; import net.jxta.protocol.DiscoveryResponseMsg; import net.jxta.protocol.PipeAdvertisement; import net.jxta.protocol.PeerGroupAdvertisement; import net.jxta.protocol.ModuleImplAdvertisement; import net.jxta.protocol.PeerAdvertisement; import net.jxta.protocol.ResolverQueryMsg; import net.jxta.protocol.ResolverResponseMsg; import net.jxta.resolver.ResolverService; import net.jxta.resolver.QueryHandler; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.document.Advertisement; import net.jxta.id.ID; import net.jxta.impl.protocol.ResolverResponse; import net.jxta.impl.protocol.ResolverQuery; /** * Encapsulates an Agent framework that handles communication with many * Student peers. Sends a series of questions to Student peers, and * processes their answers. Once completed, this Teacher peer will * recommend additional resources to the student for improvement * depending on the exam result. */ public class Teacher { public static final String DISTANCE_LEARNING = "DL:"; public static final String TEACHER_PIPE = "teacher_pipe"; public static final String ACTION_TAG = "tag"; public static final String CLIENT_NAME = "client"; public static final String COURSE_NAME = "course"; private PeerGroup netPeerGroup; private DiscoveryService discoverySvc; private PipeAdvertisement pipeAdv; private InputPipe inputPipe; private AgentFactory agentFactory = AgentFactory.getInstance(); // This Hashtable is used to store the agents used // for individual students. private Hashtable agentTable = new Hashtable(); private void sendMessage(String clientName, Map map) throws IOException { for (;;) { Enumeration enum = discoverySvc.getLocalAdvertisements(DiscoveryService.ADV, "Name", Teacher.DISTANCE_LEARNING + clientName); while (enum.hasMoreElements()) { PipeAdvertisement pipeAdv = (PipeAdvertisement) enum.nextElement(); PipeService pipeSvc = netPeerGroup.getPipeService(); OutputPipe outputPipe = null; try { outputPipe = pipeSvc.createOutputPipe(pipeAdv, 1000); Message message = pipeSvc.createMessage(); Set keySet = map.keySet(); Iterator iterator = keySet.iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); String value = (String) map.get(key); MessageElement element = message.newMessageElement(key, new MimeMediaType("text/plain"), new ByteArrayInputStream(value.getBytes())); message.addElement(element); } outputPipe.send(message); System.out.println("Sent message"); return; } catch (IOException io) { io.printStackTrace(); } finally { if (outputPipe != null) { outputPipe.close(); } } } // Need to look remotely. discoverySvc.getRemoteAdvertisements (null, DiscoveryService.ADV, "Name", Teacher.DISTANCE_LEARNING + clientName, 10, null); try { Thread.sleep(5 * 1000); // 5 sec too short if this is truly distant System.out.print("."); } catch (Exception ignore) { } } } /** * Publish a pipe advertisement. */ public void publishPipeAdv() throws IOException { PipeAdvertisement pipeAdv = null; String advType = PipeAdvertisement.getAdvertisementType(); pipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(advType); pipeAdv.setPipeID(IDFactory.newPipeID (netPeerGroup.getPeerGroupID())); pipeAdv.setName(DISTANCE_LEARNING + TEACHER_PIPE); pipeAdv.setType(PipeService.UnicastType); discoverySvc.publish(pipeAdv, DiscoveryService.ADV); discoverySvc.remotePublish(pipeAdv, DiscoveryService.ADV); this.pipeAdv = pipeAdv; } /** * Create an input pipe for inbound messages. */ public void createInputPipe() throws IOException { PipeService pipeSvc = netPeerGroup.getPipeService(); PipeMsgListener listener = new InputPipeMsgListener(); this.inputPipe = pipeSvc.createInputPipe(this.pipeAdv, listener); } /** * Start up the Net Peer group & get the * Discovery Service for that group. */ public void startJxta() throws PeerGroupException, IOException { this.netPeerGroup = PeerGroupFactory.newNetPeerGroup(); this.discoverySvc = netPeerGroup.getDiscoveryService(); // Flush all local cache information. // We want to start from scratch. discoverySvc.flushAdvertisements(null, DiscoveryService.ADV); discoverySvc.flushAdvertisements(null, DiscoveryService.PEER); discoverySvc.flushAdvertisements(null, DiscoveryService.GROUP); } /** * Create the Teacher and initiate the JXTA services. */ public static void main(String argc[]) throws Exception { Teacher teacher = new Teacher(); teacher.startJxta(); teacher.publishPipeAdv(); teacher.createInputPipe(); System.out.println("Ready to teach"); } /** * Handles the Student Teacher interactions. */ class InputPipeMsgListener implements PipeMsgListener { /** * Utility method to extract the value of a provided key * * @param message The Message to parse. * @param key The key to search for. */ private String getElementValue(Message message, String key) { if (!message.hasElement(key)) { return null; } MessageElement element = message.getElement(key); InputStream stream = element.getStream(); try { byte [] bytes = new byte[stream.available()]; stream.read(bytes); return new String(bytes); } catch (IOException io) { io.printStackTrace(); return null; } } /** * Get the next question from the Agent and send it * to the student. */ private void sendNextQuestion(String clientName) { Agent agent = (Agent) Teacher.this.agentTable.get(clientName); Agent.Problem problem = agent.getNextProblem(); Map map = new HashMap(); map.put(Teacher.ACTION_TAG, "QUESTION"); map.put("question", problem.getQuestion()); map.put("number", "" + problem.getNumber()); map.put("total", "" + agent.getTotalNumberOfQuestions()); try { Teacher.this.sendMessage(clientName, map); } catch (IOException io) { System.out.println("Unable to send a message. Exiting."); io.printStackTrace(); System.exit(1); } } /** * Processes incoming messages from Student objects. */ public void pipeMsgEvent(PipeMsgEvent pipeMsgEvent) { Message message = pipeMsgEvent.getMessage(); String messageAction = getElementValue (message, Teacher.ACTION_TAG); String clientName = getElementValue (message, Teacher.CLIENT_NAME); String courseName = getElementValue (message, Teacher.COURSE_NAME); System.out.println("Received a message from " + clientName); System.out.println("tag=" + messageAction); if (messageAction.equals("NEW")) { // Student requests a new course. Agent agent = agentFactory.getAgent(courseName); // keep track of the student and agent pair with HashTable agentTable.put(clientName, agent); sendNextQuestion(clientName); } else if (messageAction.equals("ANSWER")) { // Process the Student's answer Agent agent = (Agent) Teacher.this.agentTable.get(clientName); String answer = getElementValue(message, "ANSWER"); String nextQuestion = (!agent.processAnswer(answer)) ? "Incorrect. The correct answer is: " + agent.getLastAnswer() + ". You answered: " + answer + "\n" : "You answered the question correctly.\n"; Map map = new HashMap(); if (agent.hasMoreProblems()) { // Send the next question Agent.Problem problem = agent.getNextProblem(); nextQuestion += "The next question is: " + problem.getQuestion(); map.put(Teacher.ACTION_TAG, "QUESTION"); map.put("question", nextQuestion); map.put("number", "" + problem.getNumber()); map.put("total", "" + agent.getTotalNumberOfQuestions()); } else { // no more problems to solve :) map.put("question", nextQuestion); map.put(Teacher.ACTION_TAG, "END"); String suggestion = "You answered " + agent.getTotalAnswersCorrect() + " out of " + agent.getTotalNumberOfQuestions() + " questions correctly.\n" + agent. getSuggestions(); map.put("SUGGESTIONS", suggestion); // Remove the agent from the HashMap Teacher.this.agentTable.remove(agent); } try { // Send the response back to the Student. Teacher.this.sendMessage(clientName, map); } catch (IOException io) { System.out.println ("Unable to send a message. Exiting."); io.printStackTrace(); System.exit(1); } } } } ///:~ InputPipeMsgListener } ///:~ Teacher Details of the Agent ClassThe Agent class collects questions and answers, responds to student answers, and gives recommendations at the end of the question-answer session. This class employs the following methods and a class:
The Problem class uses three methods, getNumber(), getQuestion(), and getAnswer(), to get the number of questions, the contents of the questions, and the answers, respectively. Listing 22.3 presents the code for Agent.java. Listing 22.3 Agent.javapackage server; import java.util.*; import java.io.*; /** * The agent object that encapsulates a series of questions and answers. */ public class Agent { private List problems = new Vector(); private int totalAnswersCorrect; private int totalQuestions; private String lastAnswer; public Agent() { initProblems(); this.totalQuestions = problems.size(); } /** * Loads the questions and answers. */ private void initProblems() { Problem problem = null; problem = new Problem (1, "Who is the first president?", "George Washington"); problems.add(problem); problem = new Problem (2, "Who is the second president?", "John Adams"); problems.add(problem); problem = new Problem (3, "Who is the third president?", "Thomas Jefferson"); problems.add(problem); problem = new Problem (4, "Who is the fourth president?", "James Madison"); problems.add(problem); problem = new Problem (5, "Who is the fifth president?", "James Monroe"); problems.add(problem); problem = new Problem (6, "Who is the sixth president?", "John Quincy Adams"); problems.add(problem); problem = new Problem (7, "Who is the seventh president?", "Andrew Jackson"); problems.add(problem); problem = new Problem (8, "Who is the eight president?", "Martin Van Buren"); problems.add(problem); problem = new Problem (9, "Who is the ninth president?", "William Henry Harrison"); problems.add(problem); problem = new Problem (10, "Who is the tenth president?", "John Tyler"); problems.add(problem); } /** * Gets the total number of questions. */ public int getTotalNumberOfQuestions() { return this.totalQuestions; } /** * Returns the next question. */ public Problem getNextProblem() { return (Problem) problems.get(0); } /** * Processes the answer to the current problem. * * @returns true if answered correctly. * @returns false if answered incorrectly. */ public boolean processAnswer(String answer) { Problem problem = (Problem) problems.remove(0); this.lastAnswer = problem.getAnswer(); if (answer.equals(problem.getAnswer())) { totalAnswersCorrect++; return true; } else { return false; } } /** * Returns the last answer. */ public String getLastAnswer() { return this.lastAnswer; } /** * Returns true if there are more problems left to solve. */ public boolean hasMoreProblems() { return !problems.isEmpty(); } /** * Returns the total number of questions answered correctly so far. */ public int getTotalAnswersCorrect() { return totalAnswersCorrect; } /** * Returns tips and suggestions to help the student improve his score * based on the total number of questions answered correctly. */ public String getSuggestions() { return (totalAnswersCorrect >= 8) ? "Excellent! Please view this website for further knowledge: http://www.ipl.org/ref/POTUS" : "You need to study harder! Here is a website to help http://www.ipl.org/ref/POTUS"; } /** * Encapsulates a problem with a solution. */ class Problem { private int number; private String question; private String answer; Problem(int number, String question, String answer) { this.number = number; this.question = question; this.answer = answer; } /** * Returns the problem number. */ public int getNumber() { return this.number; } /** * Returns the question. */ public String getQuestion() { return this.question; } /** * Returns the answer to the problem. */ String getAnswer() { return answer; } }///:~ Problem }///:~ Agent Details of the AgentFactory ClassThe AgentFactory class constructs the Agent object. It employs one method, getAgent(String course), to create a new Agent. Listing 22.4 presents the code for AgentFactory.java. Listing 22.4 AgentFactory.javapackage server; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import java.util.*; import java.io.*; /** * A factory to instantiate Agent objects */ public final class AgentFactory { private static AgentFactory factory = new AgentFactory(); /** * Get the instance of the AgentFactory */ public static AgentFactory getInstance() { return factory; } /** * Instantiates an Agent based on the course. * Currently returns an Agent object. * * @param course The course requested. */ public Agent getAgent(String course) { return new Agent(); } } |