Diferență între revizuiri ale paginii „OOP Lab Task 6”
De la WikiLabs
Jump to navigationJump to search(Nu s-au afișat 7 versiuni intermediare efectuate de același utilizator) | |||
Linia 16: | Linia 16: | ||
== Requirements == | == Requirements == | ||
− | + | ||
− | + | # For this task you need the previously designed classes '''Message''', '''ClientPeer''' and '''Server'''. They are inside the package '''labutil'''. | |
− | # For this task you need the previously designed classes '''Message''', '''ClientPeer''' and '''Server''' | ||
# The class '''ClientPeer''', which was used in the previous laboratory only to send messages, will manage a bidirectional connection, to send AND receive messages through it. | # The class '''ClientPeer''', which was used in the previous laboratory only to send messages, will manage a bidirectional connection, to send AND receive messages through it. | ||
#* Beside the '''ObjectOutputStream''' you need an input stream to read '''Message''' objects from. The following things need to be added to this class: | #* Beside the '''ObjectOutputStream''' you need an input stream to read '''Message''' objects from. The following things need to be added to this class: | ||
Linia 26: | Linia 25: | ||
#** The '''close''' method needs now to close both streams, the input stream and the output stream. | #** The '''close''' method needs now to close both streams, the input stream and the output stream. | ||
#* Remember! All fields are private, all methods are public. | #* Remember! All fields are private, all methods are public. | ||
− | # The client will be enclosed in a graphical user interface (GUI) frame, with two areas, one for typing the messages to be sent, and another area that displays in real time all messages. A message is sent the moment you press the ''Enter'' key. Also, when ''Enter'' is pressed the write area is emptied and your message is shown as the last message in the display area. All messages, sent and received, are displayed as formatted strings ''name'':''message''. Add to your package a class named <code style="color:green;"> | + | # The client will be enclosed in a graphical user interface (GUI) frame, with two areas, one for typing the messages to be sent, and another area that displays in real time all messages. A message is sent the moment you press the ''Enter'' key. Also, when ''Enter'' is pressed the write area is emptied and your message is shown as the last message in the display area. All messages, sent and received, are displayed as formatted strings ''name'':''message''. Add to your '''labutil''' package a class named <code style="color:green;">ClientFrame</code>, that will manage the GUI frame and the ClientPeer. This class will be designed in two steps. First, you design the GUI frame. Then you will add the functionality, coupling the GUI frame to the ClientPeer object. The GUI frame to design is the following: |
#[[Fişier:lab6_client.png]] | #[[Fişier:lab6_client.png]] | ||
− | # The ''' | + | # The '''ClientFrame''' class extends the '''JFrame''' class from the '''javax.swing''' package. An object of JFrame type is a GUI frame, to be open as a separate window on your screen. The frame itself, with its three upper right buttons, is drawn automatically by the JFrame object on the screen upon JFrame instantiation. You need to design only the content of the framed window (the so called ''content pane''), and all this may be done inside the constructor of the '''ClientFrame''' class, so that the frame is fully drawn upon its instantiation. |
− | #* The content pane for the | + | #* The content pane for the ClientFrame has two parts, the display area and the typing area. These areas will be handled by two objects, that you instantiate as follows: |
#** a <code style="color:green;">JTextArea</code> object, instantiated with two arguments, the number of lines and the length of a line in number of characters. Instantiate the display area to have 10 lines with 40 characters per line. | #** a <code style="color:green;">JTextArea</code> object, instantiated with two arguments, the number of lines and the length of a line in number of characters. Instantiate the display area to have 10 lines with 40 characters per line. | ||
#** a <code style="color:green;">JTextField</code> object, instantiated with one argument, the length of the line in number of characters; | #** a <code style="color:green;">JTextField</code> object, instantiated with one argument, the length of the line in number of characters; | ||
Linia 38: | Linia 37: | ||
#* If you do not want the frame size to be explicitly defined, but you want it to adapt to the specified dimensions of the text area and text field, call the method <code style="color:green;">pack</code>, inherited from the parent '''JFrame''' class. | #* If you do not want the frame size to be explicitly defined, but you want it to adapt to the specified dimensions of the text area and text field, call the method <code style="color:green;">pack</code>, inherited from the parent '''JFrame''' class. | ||
#* In order to display the frame, call the parent class method <code style="color:green;">setVisible</code> with the proper argument value. | #* In order to display the frame, call the parent class method <code style="color:green;">setVisible</code> with the proper argument value. | ||
− | #* Your frame is ready to be displayed. You need only to explicitly instantiate a ''' | + | #* Your frame is ready to be displayed. You need only to explicitly instantiate a '''ClientFrame''' object. |
− | # Add to ''' | + | # Add to '''ClientFrame''' class the '''main''' method, and put a single statement inside, the instantiation of a '''ClientFrame''' object. Run the class and you will see a window like the one shown above. |
# Cosmeticize the GUI class as follows: | # Cosmeticize the GUI class as follows: | ||
#* declare the '''JtextArea''' and '''JTextField''' references as instance fields, so that they are accesible outside the constructor in this class. You will need them to couple the ClientPeer to user interface. | #* declare the '''JtextArea''' and '''JTextField''' references as instance fields, so that they are accesible outside the constructor in this class. You will need them to couple the ClientPeer to user interface. | ||
Linia 46: | Linia 45: | ||
#* call somewhere in the constructor the parent method <code style="color:green;">setDefaultCloseOperation</code> whose argument tells the object what to do if the frame window is closed. We want the client application to end (so that it not remains active after the GUI interface dissapears, wasting computer resourses and needing the OS intervention to be killed). The argument value that defines this behavior is the constant value <code style="color:green;">EXIT_ON_CLOSE</code> inherited from the parent class. | #* call somewhere in the constructor the parent method <code style="color:green;">setDefaultCloseOperation</code> whose argument tells the object what to do if the frame window is closed. We want the client application to end (so that it not remains active after the GUI interface dissapears, wasting computer resourses and needing the OS intervention to be killed). The argument value that defines this behavior is the constant value <code style="color:green;">EXIT_ON_CLOSE</code> inherited from the parent class. | ||
# It's time to add functionality to the GUI. It will be defined also in the constructor, in its second part: | # It's time to add functionality to the GUI. It will be defined also in the constructor, in its second part: | ||
− | #* The ''' | + | #* The '''ClientFrame''' object encapsulates a '''ClientPeer''' object which manages the communication between clients. Instantiate a '''ClientPeer''' in the constructor. Its instantiation needs two arguments, the name and a reference to a '''Socket''' object. Change the '''ClientFrame''' constructor declaration so that it accepts two arguments, the name and a reference to a '''Socket'''. These two arguments are passed to the '''ClientPeer''' instantiation. The reference to '''ClientPeer''' needs to be an instance field too, in order to allow the ClientPeer to interact with the GUI. |
− | #* Change the '''main''' method so that it instantiates a '''Socket''' connected to some peer host (try first with the local host, "127.0.0.1"), and then it instantiates a ''' | + | #* Change the '''main''' method so that it instantiates a '''Socket''' connected to some peer host (try first with the local host, "127.0.0.1"), and then it instantiates a '''ClientFrame''' with that socket. |
− | #* Modify the class '''Server''' so that its ''' | + | #* Modify the class '''Server''' so that its '''run''' method, instead of instantiating and running a '''ServerPeer''' (which no longer exists in this project), instantiates a '''ClientFrame''' object. The '''ClientFrame''''s constructor arguments are a string (the name of the client on the server side) and a socket returned by the '''accept''' method called for the '''ServerSocket''' object. |
− | #* You are now ready to open a connection. Run first the Server file, then run the | + | #* You are now ready to open a connection. Run first the Server file, then run the ClientFrame file. After the connection is established you will see two client GUIs on the screen. For the moment they do not communicate. |
# To be able to send messages we need to do the text field responsive to the ''Enter'' key, so that when it is pressed, the text typed in this field is packaged as a message and sent over the connection. The packaging and send operation are already designed in the '''ClientPeer''' class, so you need only to couple the user operation on the GUI (pressing the ''Enter'' key) with the ''sendMessage'' method of the '''ClientPeer''' object. | # To be able to send messages we need to do the text field responsive to the ''Enter'' key, so that when it is pressed, the text typed in this field is packaged as a message and sent over the connection. The packaging and send operation are already designed in the '''ClientPeer''' class, so you need only to couple the user operation on the GUI (pressing the ''Enter'' key) with the ''sendMessage'' method of the '''ClientPeer''' object. | ||
− | #* In the constructor, call the <code style="color:green;">addActionListener</code> method for the '''JTextField''' object. This method has a single argument, a reference to an object of type '''ActionListener''', the object that actually does the necessary coupling. But you need a type derived from that class, specifically designed for this task, to call a method of the '''ClientPeer''' a.s.o. Instead of designing a new file for this class, you will define that class right at its instantiation! You need it only here, so it doesn't need a name. This is an ''anonymous class''. An anonymous class declared inside a method of some other class has the big advantage that it has access to all fields of the later class. Your anonymous class will have acces to ''' | + | #* In the constructor, call the <code style="color:green;">addActionListener</code> method for the '''JTextField''' object. This method has a single argument, a reference to an object of type '''ActionListener''', the object that actually does the necessary coupling. But you need a type derived from that class, specifically designed for this task, to call a method of the '''ClientPeer''' a.s.o. Instead of designing a new file for this class, you will define that class right at its instantiation! You need it only here, so it doesn't need a name. This is an ''anonymous class''. An anonymous class declared inside a method of some other class has the big advantage that it has access to all fields of the later class. Your anonymous class will have acces to '''ClientFrame''''s name, and to references to the '''ClientPeer''' and the graphical objects. The body of this anonymous class has only one method, the overridden <code style="color:green;">public void actionPerformed(ActionEvent e)</code> method of the '''ActionListener'''. This method is called automatically when the user presses the ''Enter'' key inside the graphical component to which this listener is attached (and you attach it to the text field). In this method you need to do the following: |
#** call the <code style="color:green;">getText</code> for the '''JTextField''' object to read the typed string; | #** call the <code style="color:green;">getText</code> for the '''JTextField''' object to read the typed string; | ||
#** call the <code style="color:green;">sendMessage</code> for the '''ClientPeer''', passing to it your name and the typed string; | #** call the <code style="color:green;">sendMessage</code> for the '''ClientPeer''', passing to it your name and the typed string; | ||
Linia 59: | Linia 58: | ||
#* Some classes used here are part of '''java.awt.event''' package. | #* Some classes used here are part of '''java.awt.event''' package. | ||
# To display messages in real time a separate thread is needed, so that the main thread that sends messages is not blocked by message reading when this last operation waits for a message to be received. | # To display messages in real time a separate thread is needed, so that the main thread that sends messages is not blocked by message reading when this last operation waits for a message to be received. | ||
− | #* Make the ''' | + | #* Make the '''ClientFrame''' class '''Runnable'''. |
#* Override the '''run''' method of the '''Runnable''' interface. This method will loop forever (or until the connection is closed by the peer). In each iteration it: | #* Override the '''run''' method of the '''Runnable''' interface. This method will loop forever (or until the connection is closed by the peer). In each iteration it: | ||
#** calls the <code style="color:green;">readMessage</code> for the '''ClientPeer''' object to get the formatted version of the received message. This method waits until a message is received from the connection. | #** calls the <code style="color:green;">readMessage</code> for the '''ClientPeer''' object to get the formatted version of the received message. This method waits until a message is received from the connection. | ||
#** calls the <code style="color:green;">append</code> method for the '''JTextArea''' object to append the received message, together with a newline at its end. | #** calls the <code style="color:green;">append</code> method for the '''JTextArea''' object to append the received message, together with a newline at its end. | ||
#** Because '''readMessage''' may throw an exception, enclose it in a '''try'''-'''catch''' block and catch the exception to break the loop. | #** Because '''readMessage''' may throw an exception, enclose it in a '''try'''-'''catch''' block and catch the exception to break the loop. | ||
− | #* In the ''' | + | #* In the '''ClientFrame''''s constructor instantiate a '''Thread''' object with the curent '''ClientFrame''' object as the argument (use the '''this''' reference). For this '''Thread''' object call the <code style="color:green;">start</code> method, that actually initiates the new thread which will execute the '''run''' method of this '''ClientFrame''' object. |
− | # Everything is in place! Run the Server file, then run the | + | # Everything is in place! Run the Server file, then run the ClientFrame file. You are able now to communicate between twese to client GUIs. |
− | # For more fun, run a server and ask a colleague to run a client on his computer, with the host address (set upon ''' | + | # For more fun, run a server and ask a colleague to run a client on his computer, with the host address (set upon '''ClientFrame''' instantiation in the '''main''' method) equal to your IP address. |
Note: If you want to declare an anonymous class derived from some class, named ''BaseClass'', and to override the method ''baseMethod'', use the following structure: | Note: If you want to declare an anonymous class derived from some class, named ''BaseClass'', and to override the method ''baseMethod'', use the following structure: | ||
Linia 78: | Linia 77: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | To stop correctly the | + | To stop correctly the ClientFrame some additional steps are needed at the end of the '''ClientFrame''' constructor. A call to <code style="color:green;">join</code> for the thread object will synchronize the constructor with the started thread. The constructor will wait for the thread to finish, and the thread finishes when the connection is closed or something exceptional happens. After that, you call the <code style="color:green;">close</code> method of the '''ClientPeer''' object to close the streams. At the very end close the socket. |
== Submitting == | == Submitting == |
Versiunea curentă din 22 decembrie 2016 13:12
Required Tutorials
- The Object Oriented Paradigm; Classes and Objects
- Notions About the Java Language
- Writing and Executing a Java Program
- Java Syntax; A Program's Lexical Structure
- Coding Conventions
- Advanced Notions About Object Oriented Programming
- Java Application Programming Interface (API) (EN)
- Exception Handling
- Input/Output Streams
- Serialization
- Network Sockets
- Concurrent Programming - Threads
- Graphical User Interface (GUI) - Java Swing and JavaFX
Requirements
- For this task you need the previously designed classes Message, ClientPeer and Server. They are inside the package labutil.
- The class ClientPeer, which was used in the previous laboratory only to send messages, will manage a bidirectional connection, to send AND receive messages through it.
- Beside the ObjectOutputStream you need an input stream to read Message objects from. The following things need to be added to this class:
- a reference to an
ObjectInputStream
object; - The ObjectInputStream is a filter stream that encloses the input stream of the socket. You get the reference to that input stream using the Socket's method
getInputStream
. The filter stream will automatically deserialize the received Message objects. The input and output streams instantiations are done inside the constructor. - a method, named
readMessage
, without arguments, that returns the formatted string of the received Message object. Inside this method you read a single object from the input stream, using the proper method of the ObjectInputStream object, downcast the read object to a Message type and call the proper method of the received object to get the formatted string. This method may throw various exceptions of type IOException when called, and also an exception of type ClassNotFoundException when the downcasting is not possible. These are checked exceptions; though you do not catch them they need to be declared by the method. - The close method needs now to close both streams, the input stream and the output stream.
- a reference to an
- Remember! All fields are private, all methods are public.
- Beside the ObjectOutputStream you need an input stream to read Message objects from. The following things need to be added to this class:
- The client will be enclosed in a graphical user interface (GUI) frame, with two areas, one for typing the messages to be sent, and another area that displays in real time all messages. A message is sent the moment you press the Enter key. Also, when Enter is pressed the write area is emptied and your message is shown as the last message in the display area. All messages, sent and received, are displayed as formatted strings name:message. Add to your labutil package a class named
ClientFrame
, that will manage the GUI frame and the ClientPeer. This class will be designed in two steps. First, you design the GUI frame. Then you will add the functionality, coupling the GUI frame to the ClientPeer object. The GUI frame to design is the following: - The ClientFrame class extends the JFrame class from the javax.swing package. An object of JFrame type is a GUI frame, to be open as a separate window on your screen. The frame itself, with its three upper right buttons, is drawn automatically by the JFrame object on the screen upon JFrame instantiation. You need to design only the content of the framed window (the so called content pane), and all this may be done inside the constructor of the ClientFrame class, so that the frame is fully drawn upon its instantiation.
- The content pane for the ClientFrame has two parts, the display area and the typing area. These areas will be handled by two objects, that you instantiate as follows:
- a
JTextArea
object, instantiated with two arguments, the number of lines and the length of a line in number of characters. Instantiate the display area to have 10 lines with 40 characters per line. - a
JTextField
object, instantiated with one argument, the length of the line in number of characters;
- a
- Each graphical object needs to be added to the content pane, calling the
add
method of the JFrame class. The content pane has a default layout with 5 regions, one central region and four side regions (this default layout is managed by an object of type BorderLayout). The add method allows you to put a graphical object in any of these five regions. It may accept a single argument, a reference to a graphical object, in which case that graphical object will fill the central region. An overloaded version of the add allows a second argument that specifies the region where the graphical object is to be put. For this project:- put the text area object in the center region;
- put the text field object in the bottom region. This place is explicitly specified as constant argument value
BorderLayout.SOUTH
. This constant is defined in a class that's part of the java.awt package, so you need to import it too.
- The order of these additions is irrelevant, as long as they are in different regions, but be sure to add graphical objects that are already instantiated.
- If you do not want the frame size to be explicitly defined, but you want it to adapt to the specified dimensions of the text area and text field, call the method
pack
, inherited from the parent JFrame class. - In order to display the frame, call the parent class method
setVisible
with the proper argument value. - Your frame is ready to be displayed. You need only to explicitly instantiate a ClientFrame object.
- The content pane for the ClientFrame has two parts, the display area and the typing area. These areas will be handled by two objects, that you instantiate as follows:
- Add to ClientFrame class the main method, and put a single statement inside, the instantiation of a ClientFrame object. Run the class and you will see a window like the one shown above.
- Cosmeticize the GUI class as follows:
- declare the JtextArea and JTextField references as instance fields, so that they are accesible outside the constructor in this class. You will need them to couple the ClientPeer to user interface.
- display your name as the title of your frame. To do that, call somwhere inside the constructor the parent method
setTitle
with your name as an argument. Run the file and see the window being displayed with your name on the left top part of the frame. - Make your name an instance variable, so that it may be used by the ClientPeer to format the messages. Modify the constructor to accept the name as an argument and to initialize the name instance field.
- call somewhere in the constructor the parent method
setDefaultCloseOperation
whose argument tells the object what to do if the frame window is closed. We want the client application to end (so that it not remains active after the GUI interface dissapears, wasting computer resourses and needing the OS intervention to be killed). The argument value that defines this behavior is the constant valueEXIT_ON_CLOSE
inherited from the parent class.
- It's time to add functionality to the GUI. It will be defined also in the constructor, in its second part:
- The ClientFrame object encapsulates a ClientPeer object which manages the communication between clients. Instantiate a ClientPeer in the constructor. Its instantiation needs two arguments, the name and a reference to a Socket object. Change the ClientFrame constructor declaration so that it accepts two arguments, the name and a reference to a Socket. These two arguments are passed to the ClientPeer instantiation. The reference to ClientPeer needs to be an instance field too, in order to allow the ClientPeer to interact with the GUI.
- Change the main method so that it instantiates a Socket connected to some peer host (try first with the local host, "127.0.0.1"), and then it instantiates a ClientFrame with that socket.
- Modify the class Server so that its run method, instead of instantiating and running a ServerPeer (which no longer exists in this project), instantiates a ClientFrame object. The ClientFrame's constructor arguments are a string (the name of the client on the server side) and a socket returned by the accept method called for the ServerSocket object.
- You are now ready to open a connection. Run first the Server file, then run the ClientFrame file. After the connection is established you will see two client GUIs on the screen. For the moment they do not communicate.
- To be able to send messages we need to do the text field responsive to the Enter key, so that when it is pressed, the text typed in this field is packaged as a message and sent over the connection. The packaging and send operation are already designed in the ClientPeer class, so you need only to couple the user operation on the GUI (pressing the Enter key) with the sendMessage method of the ClientPeer object.
- In the constructor, call the
addActionListener
method for the JTextField object. This method has a single argument, a reference to an object of type ActionListener, the object that actually does the necessary coupling. But you need a type derived from that class, specifically designed for this task, to call a method of the ClientPeer a.s.o. Instead of designing a new file for this class, you will define that class right at its instantiation! You need it only here, so it doesn't need a name. This is an anonymous class. An anonymous class declared inside a method of some other class has the big advantage that it has access to all fields of the later class. Your anonymous class will have acces to ClientFrame's name, and to references to the ClientPeer and the graphical objects. The body of this anonymous class has only one method, the overriddenpublic void actionPerformed(ActionEvent e)
method of the ActionListener. This method is called automatically when the user presses the Enter key inside the graphical component to which this listener is attached (and you attach it to the text field). In this method you need to do the following:- call the
getText
for the JTextField object to read the typed string; - call the
sendMessage
for the ClientPeer, passing to it your name and the typed string; - erase the text field with a call of its
setText
method with an empty string as argument; - call the
append
method for the JTextArea object to append the typed string to the previous messages. Format the string to be displayed as name:message and add a newline at its end, so that each message is displayed in the text area in a separate line.
- call the
- Because sendMessage may throw an exception, enclose it in a try-catch block and catch the exception.
- Some classes used here are part of java.awt.event package.
- In the constructor, call the
- To display messages in real time a separate thread is needed, so that the main thread that sends messages is not blocked by message reading when this last operation waits for a message to be received.
- Make the ClientFrame class Runnable.
- Override the run method of the Runnable interface. This method will loop forever (or until the connection is closed by the peer). In each iteration it:
- calls the
readMessage
for the ClientPeer object to get the formatted version of the received message. This method waits until a message is received from the connection. - calls the
append
method for the JTextArea object to append the received message, together with a newline at its end. - Because readMessage may throw an exception, enclose it in a try-catch block and catch the exception to break the loop.
- calls the
- In the ClientFrame's constructor instantiate a Thread object with the curent ClientFrame object as the argument (use the this reference). For this Thread object call the
start
method, that actually initiates the new thread which will execute the run method of this ClientFrame object.
- Everything is in place! Run the Server file, then run the ClientFrame file. You are able now to communicate between twese to client GUIs.
- For more fun, run a server and ask a colleague to run a client on his computer, with the host address (set upon ClientFrame instantiation in the main method) equal to your IP address.
Note: If you want to declare an anonymous class derived from some class, named BaseClass, and to override the method baseMethod, use the following structure:
BaseClass ref = new BaseClass() {
// override the base class method
void baseMethod() {
// your specific statements
}
};
To stop correctly the ClientFrame some additional steps are needed at the end of the ClientFrame constructor. A call to join
for the thread object will synchronize the constructor with the started thread. The constructor will wait for the thread to finish, and the thread finishes when the connection is closed or something exceptional happens. After that, you call the close
method of the ClientPeer object to close the streams. At the very end close the socket.
Submitting
- The assignment will be evaluated automatically by the Web-CAT platform.
- You could access the Web-CAT platform using the username and the password with which you acces the electronica.curs.pub.ro intranet.
- Select the OOP Lab Task 6 assignment.
- Submit your work as a single .zip archive (give it whatever name you choose) containing only the Java source code files.
- Attention Any deviation from these instructions may lead to the loss of the entire amount of points.